web: unlisted operator-dropped documents from <log>/site/

Generic mechanism: bare-name PDFs placed in the log directory's site/
folder are served by name, checked LAST in the route chain (can never
shadow an API route), traversal-safe, noindex, and deliberately absent
from the endpoint index and the docs page - the operator decides who
receives a link. Tested: serve, unlisted-in-404, traversal rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mrwulf 2026-07-07 14:17:39 +02:00
parent 5e8eba8e52
commit 920485eb83
2 changed files with 48 additions and 0 deletions

View file

@ -132,6 +132,8 @@ def make_handler(log: TransparencyLog, base_path: str, docs_html: str, paper_pdf
for entry in entries for entry in entries
] ]
}) })
elif self._serve_site_document(route, log):
pass # operator-dropped document; checked LAST so it can never shadow an API route
else: else:
self._send(404, { self._send(404, {
"error": "unknown endpoint", "error": "unknown endpoint",
@ -150,6 +152,32 @@ def make_handler(log: TransparencyLog, base_path: str, docs_html: str, paper_pdf
], ],
}) })
def _serve_site_document(self, route: str, log_obj: TransparencyLog) -> bool:
"""Serve operator-dropped PDFs from ``<log>/site/`` by bare name.
Deliberately UNLISTED: these documents do not appear in the
endpoint index or anywhere on the docs page - the operator
decides who receives a link. The name must be a single plain
path segment (no traversal); only ``.pdf`` payloads are served.
Returns True when it handled the request.
"""
name = route.lstrip("/")
if not name or not name.replace("-", "").replace("_", "").isalnum():
return False
candidate = (log_obj.log_dir / "site" / f"{name}.pdf").resolve()
site_dir = (log_obj.log_dir / "site").resolve()
if site_dir not in candidate.parents or not candidate.is_file():
return False
body = candidate.read_bytes()
self.send_response(200)
self.send_header("Content-Type", "application/pdf")
self.send_header("Content-Disposition", f'inline; filename="{name}.pdf"')
self.send_header("Content-Length", str(len(body)))
self.send_header("X-Robots-Tag", "noindex, nofollow")
self.end_headers()
self.wfile.write(body)
return True
def _send(self, code: int, payload: dict[str, Any], code_if_error: int | None = None) -> None: def _send(self, code: int, payload: dict[str, Any], code_if_error: int | None = None) -> None:
if code_if_error and "error" in payload: if code_if_error and "error" in payload:
code = code_if_error code = code_if_error

View file

@ -71,6 +71,26 @@ def test_web_endpoints_and_online_proof_roundtrip(tmp_path):
page = r.read().decode() page = r.read().decode()
assert "BEGIN PUBLIC KEY" in page assert "BEGIN PUBLIC KEY" in page
assert "pin this key" in page.lower() assert "pin this key" in page.lower()
# operator-dropped documents: served by bare name, absent from the
# endpoint index, traversal-safe
site = tmp_path / "log" / "site"
site.mkdir()
(site / "extra.pdf").write_bytes(b"%PDF-1.4 dummy")
with urllib.request.urlopen(base + "/extra", timeout=10) as r:
assert r.read().startswith(b"%PDF-")
assert r.headers["X-Robots-Tag"].startswith("noindex")
try:
urllib.request.urlopen(base + "/nope-not-there", timeout=10)
raise AssertionError("expected 404")
except urllib.error.HTTPError as exc:
listing = json.loads(exc.read())
assert exc.code == 404
assert not any("extra" in e for e in listing["endpoints"]) # unlisted
try:
urllib.request.urlopen(base + "/..%2fsth", timeout=10)
raise AssertionError("expected 404")
except urllib.error.HTTPError as exc:
assert exc.code == 404
finally: finally:
server.shutdown() server.shutdown()