"""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("x") @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"