"""Tests — cookies Secure, CORS same-origin explicite, avertissement bind (ROADMAP #87 T3/T8). - `is_secure_cookies()` : `OBSIGATE_SECURE_COOKIES=true|false|auto` (défaut `auto` : Secure si la requête arrive en https, sinon pas de flag — les navigateurs ignorent les cookies `Secure` en clair). - CORS same-origin EXPLICITE : `CORSMiddleware(allow_origins=[])` — aucun `Access-Control-Allow-*` n'est émis même avec un `Origin` cross-origin, et les preflights sont rejetés (400). """ from __future__ import annotations def test_secure_cookies_default_auto(monkeypatch): """Défaut `auto` : sans requête → pas de flag (logins HTTP locaux préservés).""" from backend.auth.router import is_secure_cookies monkeypatch.delenv("OBSIGATE_SECURE_COOKIES", raising=False) assert is_secure_cookies() is False def test_secure_cookies_forced_values(monkeypatch): """`true`/`1`/`yes` → flag ; `false`/`0`/`no` → pas de flag (insensible à la casse).""" from backend.auth.router import is_secure_cookies for value in ("true", "True", "TRUE", "1", "yes", "on"): monkeypatch.setenv("OBSIGATE_SECURE_COOKIES", value) assert is_secure_cookies() is True for value in ("false", "False", "FALSE", "0", "no", "off"): monkeypatch.setenv("OBSIGATE_SECURE_COOKIES", value) assert is_secure_cookies() is False def test_no_cors_headers_on_api(client): """Pas de `Access-Control-Allow-Origin` : same-origin imposé par le navigateur.""" resp = client.get("/api/health") assert resp.status_code == 200 assert "access-control-allow-origin" not in {k.lower() for k in resp.headers} def test_no_cors_headers_on_public_share(client): """Idem sur la page publique de partage.""" resp = client.get("/s/jeton-inexistant") assert resp.status_code == 404 assert "access-control-allow-origin" not in {k.lower() for k in resp.headers} def test_cross_origin_get_emits_no_acao(client): """#87 T8 : même avec un `Origin` cross-origin, aucun ACAO (refus explicite).""" resp = client.get("/api/health", headers={"Origin": "http://evil.example"}) assert resp.status_code == 200 assert "access-control-allow-origin" not in {k.lower() for k in resp.headers} def test_cross_origin_preflight_rejected(client): """#87 T8 : preflight cross-origin → 400 (origine non autorisée).""" resp = client.options( "/api/health", headers={ "Origin": "http://evil.example", "Access-Control-Request-Method": "GET", }, ) assert resp.status_code == 400 def test_security_headers_present(client): """En-têtes de durcissement posés par le middleware (non-régression).""" resp = client.get("/api/health") assert resp.headers.get("x-content-type-options") == "nosniff" assert resp.headers.get("x-frame-options") == "SAMEORIGIN" csp = resp.headers.get("content-security-policy", "") assert "object-src 'none'" in csp assert "frame-ancestors 'self'" in csp