Compare commits
2
Commits
465b72235c
...
v7.76.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
215990adc0 | ||
|
|
fae335f808 |
@@ -1,5 +1,19 @@
|
||||
# Changelog - FlowDeck
|
||||
|
||||
## v7.76.1 (2026-10-22) — Correctif : le MCP était bloqué par le CSRF
|
||||
|
||||
### Corrigé
|
||||
|
||||
- **Tout client MCP réel recevait 403 sur `initialize`** : le
|
||||
middleware CSRF exigeait un jeton de session pour tout POST hors
|
||||
préfixes exemptés, et `/mcp` n'y était pas — alors que c'est du
|
||||
machine-à-machine authentifié par jeton d'API (Bearer), comme
|
||||
`/scim/v2`. Les tests ne voyaient rien : leur client porte une
|
||||
session et un CSRF. `/mcp` rejoint les préfixes exemptés du CSRF
|
||||
(son authentification propre est inchangée : sans jeton valide,
|
||||
toujours 401). Test de régression ajouté : client Bearer anonyme,
|
||||
sans session ni CSRF.
|
||||
|
||||
## v7.76.0 (2026-10-21) — Fusion : serveur MCP en écriture
|
||||
|
||||
### Ajouté
|
||||
|
||||
@@ -20,6 +20,11 @@ class CSRFMiddleware(BaseHTTPMiddleware):
|
||||
SAFE_METHODS = {"GET", "HEAD", "OPTIONS"}
|
||||
# NOTE: ``/scim/v2`` is Bearer-token only (no cookie auth), so CSRF does not
|
||||
# apply — IdP SCIM clients (Okta, Entra) cannot send an X-CSRF-Token.
|
||||
# Même logique pour ``/mcp`` (Fusion, 7.76.1) : le serveur MCP est
|
||||
# machine-à-machine, authentifié par les jetons d'API personnels
|
||||
# (Bearer) — le service d'agents ne peut pas envoyer de X-CSRF-Token.
|
||||
# Sans cette exemption, tout POST /mcp mourait en 403 avant la route
|
||||
# (les tests passaient : leur client porte session + CSRF).
|
||||
# A19 — LISTE FINALE : plus aucun préfixe cookie-auth n'est exempté.
|
||||
# Tous les appels non-GET du front envoient désormais `X-CSRF-Token`
|
||||
# (46 sites équipés en v7.3.6 : agent_panel, settings, local_workspace,
|
||||
@@ -30,7 +35,7 @@ class CSRFMiddleware(BaseHTTPMiddleware):
|
||||
# - publics : /s/ (sites), /f/ (forms)
|
||||
# - infra/diag : /api/csrf-token (le jeton lui-même), /api/frontend-error
|
||||
EXCLUDED_PATHS = {
|
||||
"/api/webhook", "/api/v1", "/api/v2", "/scim/v2",
|
||||
"/api/webhook", "/api/v1", "/api/v2", "/scim/v2", "/mcp",
|
||||
"/auth/callback", "/auth/register", "/auth/local-login", "/auth/local-verify",
|
||||
"/auth/2fa", "/auth/saml", "/auth/oidc", "/auth/webauthn",
|
||||
"/s/", "/f/", "/api/csrf-token", "/api/frontend-error",
|
||||
|
||||
@@ -156,6 +156,21 @@ def test_mcp_ecriture_sans_jeton_401(client, donnees):
|
||||
assert r.status_code == 401
|
||||
|
||||
|
||||
def test_mcp_client_bearer_sans_session_ni_csrf(client, jeton, donnees):
|
||||
"""Régression 7.76.1 : un vrai client MCP est machine-à-machine
|
||||
(Bearer seul, ni session ni jeton CSRF) — exactement le service
|
||||
d'agents. Le client de test habituel porte une session + un CSRF,
|
||||
ce qui masquait le 403 du middleware en production."""
|
||||
from tests.conftest import anon
|
||||
anon(client)
|
||||
r = _rpc(client, jeton, 60, "initialize", {})
|
||||
assert r.status_code == 200, r.text
|
||||
cols = _appel(client, jeton, 61, "list_collections")
|
||||
assert any(c["name"] == "Projet Alpha" for c in cols)
|
||||
r = _rpc(client, None, 62, "tools/list")
|
||||
assert r.status_code == 401, r.text
|
||||
|
||||
|
||||
def test_mcp_info_get(client, jeton):
|
||||
r = client.get("/mcp")
|
||||
assert r.status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user