matrix-builder / services /api /app /core /security_headers.py
ruslanmv
Deploy: metrics + docs (Batch 12)
22b729d
Raw
History Blame Contribute Delete
1.95 kB
from __future__ import annotations
from typing import Any
from app.core.config import Settings
class SecurityHeadersMiddleware:
def __init__(self, app: Any, settings: Settings) -> None:
self.app=app; self.settings=settings
async def __call__(self, scope: dict[str, Any], receive: Any, send: Any) -> None:
if scope.get('type')!='http':
await self.app(scope, receive, send); return
async def send_wrapper(message: dict[str, Any]) -> None:
if message.get('type')=='http.response.start':
headers=message.setdefault('headers', [])
self._append(headers, 'x-content-type-options', 'nosniff')
# X-Frame-Options can't allow-list an origin; leave it unset (X_FRAME_OPTIONS="")
# when the app must be embeddable (e.g. the Hugging Face Space iframe) and rely
# on the CSP frame-ancestors directive instead.
if self.settings.x_frame_options:
self._append(headers, 'x-frame-options', self.settings.x_frame_options)
self._append(headers, 'referrer-policy', 'no-referrer')
self._append(headers, 'permissions-policy', 'camera=(), microphone=(), geolocation=()')
self._append(headers, 'cross-origin-opener-policy', 'same-origin')
self._append(headers, 'content-security-policy', self.settings.content_security_policy)
if self.settings.enable_hsts:
self._append(headers, 'strict-transport-security', f'max-age={self.settings.hsts_max_age_seconds}; includeSubDomains')
await send(message)
await self.app(scope, receive, send_wrapper)
@staticmethod
def _append(headers: list[tuple[bytes, bytes]], name: str, value: str) -> None:
key=name.encode('latin-1')
if not any(k.lower()==key for k,_ in headers): headers.append((key, value.encode('latin-1')))