flawopen.com/Teardowns/cve-2026-105699-langflow-mcp-cross-project-file-disclosure
CVE-2026-105699: Langflow MCP File Disclosure
Langflow authenticated the MCP connection but did not authorize each requested file. A user could read another user’s flow files; 1.9.1 fixes the ownership check.[1], [2], [5]
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:NA hotel checks that you have a room key, then lets you ask for any room’s luggage by naming its tag. The key proves you may enter the hotel, not that another guest’s bag belongs to you. Langflow’s MCP read must check the file’s owner and project before opening it.[1]
Core Concepts & Subsystem Terms
Model Context Protocol (MCP)- A protocol that lets an AI application request tools and resources from a server. The server still has to authorize every requested object.[1]
Project-scoped MCP endpoint- A Langflow MCP connection associated with one project. Access to that endpoint does not grant access to files in every project.[1]
resources/read- An MCP operation that asks the server to return a resource identified by a URI.[1]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-639
Root Cause Analysis
In affected Langflow versions 1.6.8 through 1.9.0, the project-scoped MCP transport authenticated access to the project ID in its connection URL. Its resources/read handler then accepted a resource URI, parsed a flow ID and filename, and passed those identifiers to storage_service.get_file() without checking that the flow belonged to the authenticated user or the current project. The storage layer reads by namespace and filename; it does not know Langflow’s user or project permissions. Therefore, a user with an account and access to their own project MCP endpoint could request a file in another user’s flow namespace. The separately documented global resources/list and tools/list behavior could expose flow identifiers and make targeting easier, but enumeration was not required for the read. This is an authenticated cross-user confidentiality issue, not unauthenticated access or code execution. The CVE record assigns CVSS 4.0 7.1 High (AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N) and CWE-639. Langflow 1.9.1 fixes the path: handle_read_resource() obtains the authenticated user, resolves the flow with both Flow.user_id == current_user.id and, for a project transport, Flow.folder_id == project_id, and rejects an unauthorized namespace before storage access. The patch also rejects path separators and traversal sequences, adds containment checks across storage read/write/delete paths, and scopes global resource/tool listings to the caller. The maintainer’s regression test denies a read of another user’s flow.[1], [2], [3], [5]
Step-by-Step Attack Flow
A user authenticates to their own project MCP endpoint
The attacker needs a Langflow account and access to at least one project-scoped MCP connection. The issue does not bypass login.[1]
The client requests a resource URI
The MCP resources/read request carries a URI whose final path components identify a flow namespace and filename.[1]
The storage service reads by namespace
storage_service.get_file() returned the file for the supplied namespace. Storage had no user-ownership context to enforce.[1]
Source Code: Flaw vs. Secure Implementation
These are focused Python excerpts from the documented code path, not a standalone application. The affected path treated a caller-supplied namespace as sufficient authority. The 1.9.1 fix first resolves the flow under the authenticated owner and, on project-scoped transports, the current project; only then may storage read it. The upstream patch also validates filenames and enforces path containment in the storage layer, so alternate read/write/delete call sites cannot bypass the boundary. Keep authorization in the service path that has the user and project context; a storage adapter alone cannot infer ownership. Scope of the excerpt: the real handler also permits a user-bucket file only when the namespace equals the authenticated caller’s own user ID and the server is not project-scoped. The snippet omits that separate branch; it must not be generalized into arbitrary namespace access. The upstream regression suite includes test_handle_read_resource_denies_other_users_flow, which asserts that the cross-user read is denied.[1], [3], [4], [7]
Source excerpt: mcp_utils.py — focused source excerpts
# Excerpt of the affected read path: parsed IDs went directly to storage.
content = await storage_service.get_file(
flow_id=flow_id,
file_name=filename,
)
# Source-shaped reduction of the 1.9.1 authorization boundary.
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,
)
Engineering & System Hardening Checklist
- ✓Upgrade Langflow to 1.9.1 or later; verify the deployed package and container image rather than relying on a source checkout alone.[5]
- ✓Authorize every
resources/readURI against the authenticated user and the MCP connection’s project before calling storage.[1], [3] - ✓Resolve the resource identifier to a database object and require both owner equality and project membership where the endpoint is project-scoped.[1], [3]
- ✓Prefer opaque, server-generated resource IDs over accepting arbitrary storage paths or download URIs from clients.[1], [3]
- ✓Validate filenames after decoding, reject path separators and traversal components, and enforce canonical path containment at the storage boundary for every read, write, and delete operation.[1], [3]
- ✓Scope
resources/listandtools/listqueries to the authenticated caller; treat identifier enumeration as a separate disclosure that can amplify an authorization flaw.[7] - ✓Add a regression test with two users: a caller’s project MCP endpoint must deny another user’s flow file, and a project endpoint must deny flows from a different project.[1]
- ✓Review access logs for affected deployments and assess whether cross-user flow files were exposed; the advisory documents the flaw, not evidence of exploitation in the wild.[7], [3]
- ✓Keep the upstream regression
test_handle_read_resource_denies_other_users_flowand add a project-scope case proving that a valid user-owned flow outside the current project is denied.[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