securite: #87 T6-T8 fin dette — deps qualifiées, semgrep, Secure auto, CORS

This commit is contained in:
2026-09-27 12:43:31 -04:00
parent b6e2029770
commit a3973b981c
24 changed files with 390 additions and 73 deletions
+27 -3
View File
@@ -260,12 +260,19 @@ async def lifespan(app: FastAPI):
# BUG-037: refuse to expose an unauthenticated instance on a public bind.
_guard_insecure_auth()
# #87 T3 : avertir quand les cookies d'auth circulent sans flag Secure
# sur un bind non-loopback (transactions observables en clair).
# #87 T3/T8 : avertir quand les cookies d'auth circulent sans flag Secure
# sur un bind non-loopback (transactions observables en clair). Avec
# `OBSIGATE_SECURE_COOKIES=auto` (défaut) + `OBSIGATE_TRUST_PROXY=true`,
# le flag suit `X-Forwarded-Proto` : pas d'avertissement, le https du
# reverse proxy est honoré.
from backend.auth.middleware import bind_host_from_argv, is_auth_enabled, is_loopback_host
from backend.auth.router import is_secure_cookies
from backend.services.net import is_trusted_proxy
if is_auth_enabled() and not is_secure_cookies() and not is_loopback_host(bind_host_from_argv()):
secure_mode = os.environ.get("OBSIGATE_SECURE_COOKIES", "auto").lower()
proxy_secure = secure_mode == "auto" and is_trusted_proxy()
if (is_auth_enabled() and not is_secure_cookies()
and not is_loopback_host(bind_host_from_argv()) and not proxy_secure):
logger.warning(
"Cookies d'authentification sans flag `Secure` sur un bind non-loopback : "
"activez TLS et posez OBSIGATE_SECURE_COOKIES=true en production."
@@ -388,6 +395,7 @@ async def _service_error_handler(request: Request, exc: ServiceError):
# GZip compression — reduces bandwidth by ~70% for text responses
# Custom wrapper: skip compression for SSE streams (/api/events)
from fastapi.middleware.gzip import GZipMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.types import Receive, Scope, Send
@@ -418,6 +426,22 @@ app.add_middleware(SSESafeGZipMiddleware, minimum_size=1000)
# Security headers on all responses
app.add_middleware(SecurityHeadersMiddleware)
# Explicit same-origin CORS policy (#87 T8 — finit BUG-034).
# `allow_origins=[]` : le navigateur n'émet aucun `Access-Control-Allow-*`,
# donc toute lecture cross-origin est refusée (défense explicite, plus
# seulement l'absence de middleware). Sûr pour tous les clients : web
# (same-origin), desktop Tauri (la webview est redirigée same-origin sur
# http://127.0.0.1:<port>, voir frontend/js/desktop.js) et API directe
# (curl/scripts, CORS non appliqué hors navigateur). Ajouté en dernier :
# le plus externe, les preflights court-circuitent avant tout le reste.
app.add_middleware(
CORSMiddleware,
allow_origins=[],
allow_credentials=False,
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# Auth router
# Multi-format export (HTML / MD bundle / ePub) — voir backend.routers.files_media (#85 T6c).
from backend.ai_routes import router as ai_router