From 883ededa8fe96ad7145f2d11a1228b3c1b5c7844 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 13:45:52 -0400 Subject: [PATCH 1/2] CORS recherche (fusion #68/#69) : allowlist soeurs prod+dev explicites --- backend/recherche_cors.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/backend/recherche_cors.py b/backend/recherche_cors.py index af90390..14a3366 100644 --- a/backend/recherche_cors.py +++ b/backend/recherche_cors.py @@ -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) -- 2.54.0 From b9a7824b28ed3ed1b4315fbea59963f75d693fc3 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 13:45:55 -0400 Subject: [PATCH 2/2] Tests CORS recherche : prod+dev autorises, tiers refuse --- tests/test_recherche_cors.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_recherche_cors.py b/tests/test_recherche_cors.py index 86c7f3f..5ee067a 100644 --- a/tests/test_recherche_cors.py +++ b/tests/test_recherche_cors.py @@ -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(): -- 2.54.0