Files
PacketStation/tests/server/test_security_headers.py
spencermandClaude Opus 5.5 6e820ba0a5 Version banner, File locations section, final CSP (no unsafe-inline styles, frame-src none, object-src none)
The MTR viewer draws PDFs with pdf.js instead of an iframe the server's frame-ancestors refused.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 17:50:05 -06:00

63 lines
1.9 KiB
Python

"""The CSP per response type: the strict policy for pages and JSON, object-src 'self' only for PDFs."""
from __future__ import annotations
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, Response
from fastapi.testclient import TestClient
from packetstation import security
def _client() -> TestClient:
app = FastAPI()
@app.get("/page")
def page() -> HTMLResponse:
return HTMLResponse("<!doctype html><title>x</title>")
@app.get("/_blob/doc")
def pdf() -> Response:
return Response(b"%PDF-1.7\n", media_type="application/pdf")
@app.get("/api/print")
def api_pdf() -> Response:
return Response(b"%PDF-1.7\n", media_type="Application/PDF; charset=binary")
app.add_middleware(security.SecurityHeadersMiddleware)
return TestClient(app)
def _directives(csp: str) -> dict[str, str]:
out: dict[str, str] = {}
for part in csp.split(";"):
name, _, value = part.strip().partition(" ")
out[name] = value
return out
def test_page_gets_the_strict_policy() -> None:
r = _client().get("/page")
csp = r.headers["content-security-policy"]
assert csp == security.CSP
d = _directives(csp)
assert d["style-src"] == "'self'"
assert d["object-src"] == "'none'"
assert d["frame-src"] == "'none'"
assert d["frame-ancestors"] == "'none'"
assert r.headers["x-frame-options"] == "DENY"
def test_pdf_gets_object_src_self_and_nothing_else_changes() -> None:
c = _client()
for path in ("/_blob/doc", "/api/print"):
r = c.get(path)
csp = r.headers["content-security-policy"]
assert csp == security.PDF_CSP, path
strict, pdf = _directives(security.CSP), _directives(csp)
assert pdf.pop("object-src") == "'self'"
strict.pop("object-src")
assert pdf == strict
assert r.headers["x-frame-options"] == "DENY"
assert c.get("/api/print").headers["cache-control"] == "no-store"