From c97dfb0ef47bcfc651ae0947c96be0274b15edc7 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 16:11:26 -0400 Subject: [PATCH 1/2] CORS recherche: autoriser les origines agent (console Spark) prod+dev --- app/recherche.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/recherche.py b/app/recherche.py index 0f8ba5d..3266dff 100644 --- a/app/recherche.py +++ b/app/recherche.py @@ -10,7 +10,10 @@ testables sans base ni serveur. tous projets du workspace) et la regroupe par dépôt. - La CORS bornée n'autorise que ``GET /api/search``, et seulement pour les origines sœurs officielles **prod ET dev, listées - explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus** les + explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus les + origines de la console Spark** (``AGENT_ORIGINS`` : agent prod ET + dev — fédération de la recherche depuis la console, 2026-10-28), + **plus** les origines résolues par ``app.config`` — jamais ``*``. Correctif 2026-10-28 (fusion #68/#69, matrice constatée par Bruno) : l'ancienne allowlist ne contenait que l'environnement résolu @@ -44,6 +47,12 @@ DEV_ORIGINS = frozenset({ "https://kanbante-dev.dracodev.net", }) +AGENT_ORIGINS = frozenset({ + "https://agent.dracodev.net", + "https://agent-dev.dracodev.net", +}) + + # Clés du dictionnaire rendu par render() (app/main.py). _SOEUR_KEYS = ("obsigate", "flowdeck", "tiktak", "kanbante") @@ -105,7 +114,7 @@ def sister_origins( # Correctif 2026-10-28 : prod ET dev toujours, quelle que soit # la résolution — ``resolved`` n'ajoute que des origines de # configuration sans jamais retirer une origine officielle. - return frozenset(origins) | PROD_ORIGINS | DEV_ORIGINS + return frozenset(origins) | PROD_ORIGINS | DEV_ORIGINS | AGENT_ORIGINS def cors_headers_for( -- 2.54.0 From 0219fafa34d2bb5bb369feb4939d728d4c3e6083 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 16:11:30 -0400 Subject: [PATCH 2/2] Tests CORS recherche: origines agent autorisees, tiers refuse --- tests/test_recherche.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_recherche.py b/tests/test_recherche.py index a484bcc..b7089ac 100644 --- a/tests/test_recherche.py +++ b/tests/test_recherche.py @@ -34,7 +34,7 @@ def test_origin_of(): def test_sister_origins_dev(): - # Correctif 2026-10-28 : 8 origines officielles, prod ET dev, + # Correctif 2026-10-28 : 10 origines officielles (8 sœurs + 2 agent), prod ET dev, # quelle que soit la résolution. assert sister_origins(RESOLVED_DEV) == { "https://og.dracodev.net", @@ -45,9 +45,24 @@ def test_sister_origins_dev(): "https://flowdeck-dev.dracodev.net", "https://tiktak-dev.dracodev.net", "https://kanbante-dev.dracodev.net", + "https://agent.dracodev.net", + "https://agent-dev.dracodev.net", } + +def test_cors_headers_agent_console_autorise(): + # Fédération depuis la console (2026-10-28) : les origines agent + # prod ET dev passent, avec credentials ; un tiers reste refusé. + for origine in ("https://agent.dracodev.net", + "https://agent-dev.dracodev.net"): + headers = cors_headers_for(origine, RESOLVED_DEV) + assert headers is not None + assert headers["Access-Control-Allow-Origin"] == origine + assert headers["Access-Control-Allow-Credentials"] == "true" + assert cors_headers_for("https://agent.dracodev.net.evil.example", + RESOLVED_DEV) is None + def test_cors_headers(): headers = cors_headers_for("https://tiktak-dev.dracodev.net", RESOLVED_DEV) assert headers["Access-Control-Allow-Origin"] == "https://tiktak-dev.dracodev.net" -- 2.54.0