Spaces:
Running
Running
| """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) | |