
Local Docker lab for reproducing CVE-2026-55255, an IDOR vulnerability in Langflow's Responses API. Validates cross-user flow execution in vulnerable vs. patched versions with a request-based PoC.
/api/v1/responsesThis repository contains a local Docker lab for reproducing and validating CVE-2026-55255, an Insecure Direct Object Reference (IDOR) vulnerability affecting Langflow's OpenAI-compatible Responses API.
Langflow is an open-source platform for building and deploying AI-powered agents and workflows. The vulnerable behavior affects the /api/v1/responses endpoint, where an authenticated attacker can provide another user's flow UUID as the model value and cause Langflow to execute that victim-owned flow.
This lab compares two Langflow versions:
| Service | Langflow version | Purpose | URL |
|---|---|---|---|
| vuln | 1.9.0 | Vulnerable comparison target | http://localhost:7860 |
| patched | 1.9.1 | Patched comparison target | http://localhost:7861 |
The demonstrated HTTP validation path in this local lab is:
Authenticated attacker API key
→ POST /api/v1/responses
→ request body sets model to victim-owned flow UUID
→ vulnerable target executes the victim-owned flow
→ patched target returns flow_not_found and does not execute the victim-owned flow
In the vulnerable target, the attacker-owned API key can execute the victim-owned flow and the response contains the victim-only marker:
VICTIM_ONLY_CONTEXT_55255_VULN
In the patched target, the same cross-user request does not return the victim marker and returns an OpenAI-style error body:
{"error":{"message":"Flow with id '<victim-flow-id>' not found","type":"invalid_request_error","code":"flow_not_found"}}
This lab validates the vulnerable-versus-patched HTTP behavior using Langflow 1.9.0 and Langflow 1.9.1.
The lab is intentionally scoped to local Docker services. It does not target external systems and does not include credential theft, database dumping, destructive payloads, external callbacks, malware, persistence, or post-exploitation activity.
| Claim | Evidence | How to verify in this lab |
|---|---|---|
CVE-2026-55255 affects Langflow's /api/v1/responses endpoint. | GitHub Advisory GHSA-qrpv-q767-xqq2 describes an IDOR in /api/v1/responses. | Review the References section and run the PoC against both local targets. |
GitHub Advisory lists affected versions as < 1.9.1 and patched version as 1.9.1. | GitHub Advisory GHSA-qrpv-q767-xqq2. | Compare the vulnerable and patched target versions in docker-compose.yml. |
| Some downstream vulnerability sources disagree on the exact fixed version. | GitHub/GitLab list 1.9.1 as fixed; some downstream intelligence pages mention 1.9.2 or contain mixed wording. | Review the References section and rely on the lab validation for the tested 1.9.1 behavior. |
| This lab uses Langflow 1.9.0 as the vulnerable comparison target. | The vuln service uses langflowai/langflow:1.9.0. | Inspect docker-compose.yml and run docker compose ps. |
| This lab uses Langflow 1.9.1 as the patched comparison target. | The patched service uses langflowai/langflow:1.9.1. | Inspect docker-compose.yml and run docker compose ps. |
Langflow's Responses API uses POST /api/v1/responses. | Langflow documentation describes the OpenAI-compatible Responses API endpoint. | Run the PoC or manual curl request against /api/v1/responses. |
Langflow's Responses API accepts a flow ID as the model value. | Langflow documentation states that the model value is replaced with a flow_id. | Inspect the PoC request body. |
Langflow API requests require an API key through x-api-key. | Langflow API documentation describes API key authentication with the x-api-key header. | Inspect the PoC request headers. |
| The PoC is request-based. | poc/validate_idor.py sends HTTP requests and does not call Docker, Docker Compose, shell commands, or container APIs. | Inspect poc/validate_idor.py. |
| The vulnerable target executes a victim-owned flow with an attacker-owned API key. | The vulnerable response returns VICTIM_ONLY_CONTEXT_55255_VULN. | Run the vulnerable PoC command with the victim flow ID and attacker API key. |
| The patched target blocks the same cross-user execution path. | The patched response returns error.code = flow_not_found and does not return the victim marker. | Run the patched PoC command with the victim flow ID and attacker API key. |
This lab uses Langflow 1.9.0 as the vulnerable comparison target because GitHub Advisory GHSA-qrpv-q767-xqq2 identifies versions before 1.9.1 as affected, and local testing confirmed the vulnerable behavior in 1.9.0.
This lab uses Langflow 1.9.1 as the patched comparison target because GitHub Advisory GHSA-qrpv-q767-xqq2 lists 1.9.1 as the patched version, and local testing confirmed that 1.9.1 blocks the tested cross-user /api/v1/responses execution path with flow_not_found.
There is a version discrepancy between sources. GitHub and GitLab advisories list versions before 1.9.1 as affected and 1.9.1 as fixed. Some downstream vulnerability intelligence pages mention 1.9.2 or contain mixed wording around the fixed version. This repository documents that discrepancy and validates the tested behavior directly:
Langflow 1.9.0
→ attacker API key + victim flow UUID
→ victim marker returned
→ vulnerable behavior observed
Langflow 1.9.1
→ attacker API key + victim flow UUID
→ flow_not_found
→ victim marker not returned
→ blocked behavior observed
This lab assumes the attacker already knows a victim flow UUID. The PoC does not brute-force flow IDs, enumerate flows, or attempt to discover victim flow IDs.
This lab focuses on the observable HTTP behavior of:
POST /api/v1/responses
with this request shape:
{
"model": "<victim-flow-id>",
"input": "cross-user CVE-2026-55255 validation request",
"stream": false
}
The lab demonstrates unauthorized cross-user flow execution in the vulnerable target and blocked behavior in the patched target.
The lab does not demonstrate:
The root cause of CVE-2026-55255 is an authorization gap in Langflow's flow resolution logic.
The /api/v1/responses endpoint accepts a flow UUID through the model field. In vulnerable versions, the UUID lookup path inside get_flow_by_id_or_endpoint_name() could load a Flow object directly by primary key without enforcing that the resolved Flow.user_id matched the authenticated API-key user.
The vulnerable behavior can be summarized as:
Attacker owns API key
→ attacker sends POST /api/v1/responses
→ model contains victim-owned flow UUID
→ flow resolver loads Flow by UUID
→ resolver does not enforce Flow.user_id == attacker_user.id
→ response endpoint executes the victim-owned flow
→ attacker receives victim flow output
The patched behavior can be summarized as:
Attacker owns API key
→ attacker sends POST /api/v1/responses
→ model contains victim-owned flow UUID
→ flow resolver loads candidate Flow
→ resolver compares Flow.user_id with authenticated API-key user id
→ cross-user lookup is treated as not found
→ response endpoint returns flow_not_found
→ victim-owned flow is not executed
The security issue is not that the attacker can call /api/v1/responses with their own flows. That is expected behavior. The issue is that a low-privileged authenticated user can cause the endpoint to execute a flow owned by another user when the victim flow UUID is supplied.
The security lesson is: