"""Middlewares HTTP : en-tetes de securite et limite de taille des requetes.""" from __future__ import annotations from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import JSONResponse, Response CONTENT_SECURITY_POLICY = "; ".join( [ "default-src 'self'", # Les styles inline sont utilises par React Flow et par l'export image. "style-src 'self' 'unsafe-inline'", "script-src 'self'", "img-src 'self' data: blob:", "font-src 'self' data:", "connect-src 'self'", # Service worker et manifeste de la PWA. "worker-src 'self'", "manifest-src 'self'", "object-src 'none'", "base-uri 'self'", "form-action 'self'", "frame-ancestors 'none'", ] ) SECURITY_HEADERS = { "Content-Security-Policy": CONTENT_SECURITY_POLICY, "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Referrer-Policy": "no-referrer", "Permissions-Policy": "geolocation=(), microphone=(), camera=(), interest-cohort=()", "Cross-Origin-Opener-Policy": "same-origin", } class SecurityHeadersMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # type: ignore[no-untyped-def] response: Response = await call_next(request) for header, value in SECURITY_HEADERS.items(): response.headers.setdefault(header, value) return response class MaxBodySizeMiddleware(BaseHTTPMiddleware): """Refuse proprement les corps de requete trop volumineux. On s'appuie sur Content-Length : le client est rejete avant toute lecture complete du corps, et recoit un message clair plutot qu'une erreur brute. """ def __init__(self, app, max_body_size: int) -> None: # type: ignore[no-untyped-def] super().__init__(app) self.max_body_size = max_body_size async def dispatch(self, request: Request, call_next): # type: ignore[no-untyped-def] content_length = request.headers.get("content-length") if content_length is not None: try: size = int(content_length) except ValueError: size = 0 if size > self.max_body_size: limit_mb = self.max_body_size / (1024 * 1024) return JSONResponse( status_code=413, content={ "detail": ( "La carte est trop volumineuse pour etre partagee " f"(limite : {limit_mb:.0f} Mo)." ) }, ) return await call_next(request)