"""Author RAG Chatbot SaaS — Security Headers Middleware. Adds production-grade security headers to every HTTP response using pure ASGI (avoids BaseHTTPMiddleware request body consumption issues). """ from starlette.types import ASGIApp, Receive, Scope, Send from app.config import get_settings class SecurityHeadersMiddleware: """Add security headers to all HTTP responses (pure ASGI).""" def __init__(self, app: ASGIApp) -> None: self.app = app async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: if scope["type"] != "http": await self.app(scope, receive, send) return cfg = get_settings() use_hsts = cfg.APP_ENV == "production" async def send_with_headers(message): if message["type"] == "http.response.start": headers = list(message.get("headers", [])) security_headers = [ (b"x-content-type-options", b"nosniff"), (b"x-frame-options", b"SAMEORIGIN"), (b"x-xss-protection", b"1; mode=block"), (b"referrer-policy", b"strict-origin-when-cross-origin"), (b"permissions-policy", b"camera=(), microphone=(), geolocation=(), payment=()"), # R-043: CSP — primary XSS defense (b"content-security-policy", b"default-src 'self'; " b"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; " b"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " b"font-src 'self' https://fonts.gstatic.com; " b"img-src 'self' data: https:; " b"connect-src 'self' https://api.openai.com https://cdn.jsdelivr.net; " b"frame-ancestors 'none'"), ] if use_hsts: security_headers.insert( 0, (b"strict-transport-security", b"max-age=31536000; includeSubDomains"), ) headers.extend(security_headers) message["headers"] = headers await send(message) await self.app(scope, receive, send_with_headers)