From cbaec9ad36a3b40127d5da1d5cc94df91e0c03e4 Mon Sep 17 00:00:00 2001 From: xpltd Date: Thu, 19 Mar 2026 06:53:08 -0500 Subject: [PATCH] =?UTF-8?q?R020:=20Zero=20outbound=20telemetry=20=E2=80=94?= =?UTF-8?q?=20CSP=20+=20security=20headers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified: no external URLs in frontend (no CDN fonts, no analytics, no Google Fonts, no external scripts). All fonts use system fallback chains (JetBrains Mono → Cascadia Code → Fira Code → monospace). No outbound HTTP calls in backend code. Added SecurityHeadersMiddleware enforcing: - Content-Security-Policy: default-src 'self', script/font/connect restricted to 'self', style allows 'unsafe-inline' for Vue scoped styles, img allows data: URIs, object-src 'none', frame-ancestors 'none' - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - Referrer-Policy: no-referrer These headers prevent any accidental introduction of external resources in future development — CSP violations will block them. --- backend/app/main.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/backend/app/main.py b/backend/app/main.py index 8c29797..ec4c471 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -106,6 +106,37 @@ async def lifespan(app: FastAPI): app = FastAPI(title="media.rip()", lifespan=lifespan) app.add_middleware(SessionMiddleware) + + +# --- Security headers middleware (R020: zero outbound telemetry) --- +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.requests import Request as StarletteRequest +from starlette.responses import Response + + +class SecurityHeadersMiddleware(BaseHTTPMiddleware): + """Add security headers enforcing no outbound resource loading.""" + + async def dispatch(self, request: StarletteRequest, call_next): # type: ignore[override] + response: Response = await call_next(request) + # Content-Security-Policy: only allow resources from self + response.headers["Content-Security-Policy"] = ( + "default-src 'self'; " + "script-src 'self'; " + "style-src 'self' 'unsafe-inline'; " + "img-src 'self' data:; " + "font-src 'self'; " + "connect-src 'self'; " + "object-src 'none'; " + "frame-ancestors 'none'" + ) + response.headers["X-Content-Type-Options"] = "nosniff" + response.headers["X-Frame-Options"] = "DENY" + response.headers["Referrer-Policy"] = "no-referrer" + return response + + +app.add_middleware(SecurityHeadersMiddleware) app.include_router(admin_router, prefix="/api") app.include_router(cookies_router, prefix="/api") app.include_router(downloads_router, prefix="/api")