Compare commits

...
Author SHA1 Message Date
bruno b9a7824b28 Tests CORS recherche : prod+dev autorises, tiers refuse
CI / lint (pull_request) Failing after 0s
CI / security (pull_request) Failing after 1s
CI / test (pull_request) Skipped
CI / build (pull_request) Skipped
CI / e2e (pull_request) Skipped
2026-10-28 13:45:55 -04:00
bruno 883ededa8f CORS recherche (fusion #68/#69) : allowlist soeurs prod+dev explicites 2026-10-28 13:45:52 -04:00
2 changed files with 34 additions and 14 deletions
+16 -12
View File
@@ -8,12 +8,16 @@ Ce module n'autorise donc que :
- le chemin ``/api/search`` (jamais les autres routes, jamais les
écritures) ;
- les origines sœurs : celles de ``backend.draco_config``
(``urls_publiques()`` : explicite > dérivation par l'hôte de
l'agent > défaut officiel), **plus** les origines prod ou ``-dev``
officielles correspondant à l'hôte de la requête — ce second
ensemble couvre le cas où la configuration seule ne déclare pas
l'environnement courant ;
- les origines sœurs : les origines officielles **prod ET dev,
listées explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus**
celles de ``backend.draco_config`` (``urls_publiques()`` :
explicite > dérivation par l'hôte de l'agent > défaut officiel)
— jamais ``*``. Correctif 2026-10-28 (fusion #68/#69, matrice
constatée par Bruno) : l'ancienne allowlist ne contenait que
l'environnement de la configuration (plus celui de l'hôte de la
requête), jamais les deux à la fois — une page de l'autre
environnement voyait sa section afficher « Injoignable depuis
cet hôte (CORS ou réseau) » ;
- avec credentials (le cookie SSO ``draco_session``, accepté par
``/api/search`` via le repli de ``backend.auth.middleware``, ou
le cookie natif ``access_token``).
@@ -104,12 +108,12 @@ def sister_origins(resolved: dict | None = None, request_host: str | None = None
origin = origin_of((resolved or {}).get(key))
if origin:
origins.add(origin)
if is_dev_host(request_host):
origins |= DEV_ORIGINS
elif is_prod_host(request_host):
origins |= PROD_ORIGINS
if not origins:
return PROD_ORIGINS | DEV_ORIGINS
# Correctif 2026-10-28 : prod ET dev toujours — ``request_host``
# ne restreint plus l'allowlist (il ne servait qu'à ajouter
# l'environnement de la requête, désormais inclus d'office).
# ``resolved`` n'ajoute que des origines de configuration sans
# jamais retirer une origine officielle.
origins |= PROD_ORIGINS | DEV_ORIGINS
return frozenset(origins)
+18 -2
View File
@@ -38,7 +38,13 @@ def test_origin_of():
def test_sister_origins_dev():
# Correctif 2026-10-28 : 8 origines officielles, prod ET dev,
# quelle que soit la résolution.
assert sister_origins(RESOLVED_DEV) == {
"https://og.dracodev.net",
"https://flowdeck.dracodev.net",
"https://tiktak.dracodev.net",
"https://kanbante.dracodev.net",
"https://og-dev.dracodev.net",
"https://flowdeck-dev.dracodev.net",
"https://tiktak-dev.dracodev.net",
@@ -47,12 +53,18 @@ def test_sister_origins_dev():
def test_sister_origins_ajoute_env_de_la_requete():
# Correctif 2026-10-28 : configuration muette, quel que soit
# l'hôte de la requête, prod ET dev passent — l'hôte ne
# restreint plus l'allowlist.
assert "https://og-dev.dracodev.net" in sister_origins(
{}, request_host="og-dev.dracodev.net"
)
assert "https://og-dev.dracodev.net" not in sister_origins(
assert "https://og-dev.dracodev.net" in sister_origins(
{}, request_host="og.dracodev.net"
)
assert "https://og.dracodev.net" in sister_origins(
{}, request_host="og-dev.dracodev.net"
)
def test_cors_headers():
@@ -60,7 +72,11 @@ def test_cors_headers():
assert headers["Access-Control-Allow-Origin"] == "https://tiktak-dev.dracodev.net"
assert headers["Access-Control-Allow-Credentials"] == "true"
assert cors_headers_for("https://evil.example", RESOLVED_DEV) is None
assert cors_headers_for("https://og.dracodev.net", RESOLVED_DEV) is None
assert cors_headers_for("https://og-dev.dracodev.net.evil.example",
RESOLVED_DEV) is None
# Autre environnement autorisé (correctif 2026-10-28).
assert cors_headers_for("https://og.dracodev.net",
RESOLVED_DEV) is not None
def test_preflight_headers():