flawopen.com/Teardowns/cve-2026-105699-langflow-mcp-cross-project-file-disclosure
CVE-2026-105699 : fichiers MCP Langflow
Langflow authentifiait la connexion MCP, mais n’autorisait pas chaque fichier demandé. Un utilisateur pouvait lire les fichiers de flux d’un autre ; la version 1.9.1 corrige le contrôle de propriété.[1], [2], [5]
Vecteur CVSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:NImaginez un hôtel qui vérifie votre carte, puis vous remet les bagages de n’importe quelle chambre si vous indiquez leur étiquette. La carte prouve que vous pouvez entrer dans l’hôtel, pas que le sac d’un autre client vous appartient. La lecture MCP de Langflow doit vérifier le propriétaire et le projet avant d’ouvrir le fichier.[1]
Concepts Clés et Termes
Model Context Protocol (MCP)- Protocole permettant à une application d’IA de demander des outils et des ressources à un serveur. Celui-ci doit encore autoriser chaque objet demandé.[1]
Point de terminaison MCP lié à un projet- Connexion MCP de Langflow associée à un projet. L’accès à ce point ne donne pas accès aux fichiers de tous les projets.[1]
resources/read- Opération MCP qui demande au serveur une ressource identifiée par une URI.[1]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-639
Analyse de Cause Racine
Dans les versions de Langflow affectées, de 1.6.8 à 1.9.0, le transport MCP lié à un projet authentifiait l’accès à l’identifiant du projet présent dans l’URL de connexion. Ensuite, le gestionnaire resources/read recevait une URI, en extrayait un identifiant de flux et un nom de fichier, puis les transmettait à storage_service.get_file() sans vérifier que le flux appartenait à l’utilisateur authentifié ou au projet courant. Le stockage lit par espace de noms et nom de fichier ; il ne connaît pas les autorisations Langflow des utilisateurs et des projets. Un utilisateur disposant d’un compte et de l’accès à son propre point MCP de projet pouvait donc demander un fichier dans l’espace de noms de flux d’un autre utilisateur. Le comportement global distinct de resources/list et tools/list pouvait révéler des identifiants et faciliter le ciblage, mais n’était pas nécessaire à la lecture. Il s’agit d’une atteinte authentifiée à la confidentialité entre utilisateurs, pas d’un accès anonyme ni d’une exécution de code. La fiche CVE attribue CVSS 4.0 7,1 High et CWE-639. Langflow 1.9.1 corrige le parcours : handle_read_resource() récupère l’utilisateur authentifié et recherche le flux en imposant Flow.user_id == current_user.id et, pour un transport de projet, Flow.folder_id == project_id; tout espace non autorisé est rejeté avant l’accès au stockage. Le correctif rejette aussi les séparateurs et séquences de traversal, vérifie le confinement des chemins de lecture/écriture/suppression et limite les listes globales à l’utilisateur courant. Le test de régression du mainteneur refuse la lecture du flux d’un autre utilisateur.[1], [2], [3], [5]
Déroulement de l'Attaque Étape par Étape
Un utilisateur s’authentifie sur son propre point MCP de projet
L’attaquant doit posséder un compte Langflow et accéder à au moins une connexion MCP de projet. La faille ne contourne pas la connexion.[1]
Le client demande une URI de ressource
La requête MCP resources/read contient une URI dont les derniers composants désignent un espace de noms de flux et un fichier.[1]
Le stockage lit selon l’espace de noms
storage_service.get_file() renvoyait le fichier de l’espace indiqué. Le stockage ne disposait pas du contexte de propriété utilisateur.[1]
Code Source : Vulnérable vs Sécurisé
Ces extraits Python ciblent le parcours documenté ; ils ne constituent pas une application autonome. Le code vulnérable traitait l’espace fourni par le client comme une autorité suffisante. Le correctif 1.9.1 résout d’abord le flux dans le périmètre de l’utilisateur authentifié et, pour un transport MCP de projet, du projet courant ; le stockage ne peut le lire qu’ensuite. Le correctif valide aussi les noms et impose le confinement des chemins dans la couche de stockage pour empêcher tout contournement par d’autres chemins de lecture, d’écriture ou de suppression. Placez l’autorisation dans le service qui connaît l’utilisateur et le projet : un adaptateur de stockage ne peut pas déduire la propriété. Portée de l’extrait : le gestionnaire réel autorise aussi un fichier du compartiment utilisateur uniquement si l’espace de noms correspond à l’ID du demandeur authentifié et si le serveur n’est pas limité à un projet. L’extrait omet cette branche distincte ; il ne faut pas en déduire un accès à n’importe quel espace de noms. La suite de régression amont contient test_handle_read_resource_denies_other_users_flow, qui vérifie le refus d’une lecture entre utilisateurs.[1], [3], [4], [7]
Extrait source: mcp_utils.py — extraits ciblés du code
# Extrait du chemin de lecture concerné : les ID analysés étaient transmis directement au stockage.
content = await storage_service.get_file(
flow_id=flow_id,
file_name=filename,
)
# Réduction fidèle à la structure source de la limite d’autorisation de la version 1.9.1.
current_user = current_user_ctx.get()
query = select(Flow).where(
Flow.id == namespace_id,
Flow.user_id == current_user.id,
)
if project_id is not None:
query = query.where(Flow.folder_id == project_id)
flow = (await session.exec(query)).first()
if flow is None:
raise ValueError("Resource not found or access denied")
content = await storage_service.get_file(
flow_id=namespace_id,
file_name=filename,
)
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Mettez Langflow à jour vers la version 1.9.1 ou ultérieure et vérifiez le paquet et l’image de conteneur réellement déployés, pas seulement le dépôt source.[5]
- ✓Autorisez chaque URI
resources/readselon l’utilisateur authentifié et le projet de la connexion MCP avant l’appel au stockage.[1], [3] - ✓Résolvez l’identifiant en objet de base de données et exigez la propriété de l’utilisateur ainsi que l’appartenance au projet pour les points de projet.[1], [3]
- ✓Préférez des identifiants de ressource opaques générés par le serveur plutôt que des chemins ou URI de téléchargement arbitraires envoyés par le client.[1], [3]
- ✓Validez les noms après décodage, rejetez séparateurs et composants de traversal, et imposez le confinement canonique dans le stockage pour toute lecture, écriture et suppression.[1], [3]
- ✓Limitez
resources/listettools/listà l’utilisateur authentifié ; l’énumération d’identifiants est une divulgation distincte qui peut amplifier un défaut d’autorisation.[7] - ✓Ajoutez un test avec deux utilisateurs : le point MCP de projet de l’un doit refuser le fichier de flux de l’autre et les flux d’un autre projet.[1]
- ✓Examinez les journaux des installations affectées et évaluez une éventuelle exposition ; l’avis décrit le défaut, pas une exploitation confirmée dans la nature.[7], [3]
- ✓Conservez le test amont
test_handle_read_resource_denies_other_users_flowet ajoutez un cas de portée projet qui vérifie le refus d’un flux appartenant à l’utilisateur mais situé hors du projet courant.[7], [3]
Sources
- Langflow maintainer advisory GHSA-4hmc-cfm3-w43c
- CVE Program record CVE-2026-105699
- Upstream fix commit b8fe970
- Upstream pull request #12818 and regression coverage
- Langflow 1.9.1 release
- MITRE CWE-639 — Authorization Bypass Through User-Controlled Key
- Langflow regression tests — MCP resource authorization