Skip to content

CVE-2026-55255: Verified Repro With Script Download

CVE-2026-55255: Langflow’s /api/v1/responses endpoint contains an IDOR that lets any authenticated user execute another user’s flow by supplying the victim’s flow UUID.

CVE-2026-55255 is verified against langflow (pip) · pip. Affected versions: < 1.9.1. Fixed in >=1.9.1. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00268.

REPRO-2026-00268 langflow (pip) · pip Variant found Jul 8, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.9
Confidence
HIGH
Reproduced in
28m 29s
Tool calls
241
Spend
$4.67
01 · Overview

What Is CVE-2026-55255?

CVE-2026-55255 is a critical (CVSS 9.9) Insecure Direct Object Reference (CWE-639) in Langflow's /api/v1/responses endpoint that lets any authenticated user execute another user's flow by supplying the victim's flow UUID. Pruva reproduced it (reproduction REPRO-2026-00268).

02 · Severity & CVSS

CVE-2026-55255 Severity & CVSS Score

CVE-2026-55255 is rated critical severity, with a CVSS base score of 9.9 out of 10.

CRITICAL threat level
9.9 / 10 CVSS base
Weakness CWE-639 — Authorization Bypass Through User-Controlled Key

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected langflow (pip) Versions

langflow (pip) · pip versions < 1.9.1 are affected.

How to Reproduce CVE-2026-55255

$ pruva-verify REPRO-2026-00268
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00268/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-55255

Authorization bypass — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

victim flow UUID supplied as the `model` value in POST /api/v1/responses with the attacker's x-api-key

Attack chain
  1. POST /api/v1/responses
  2. openai_responses.create_response
  3. get_flow_by_id_or_endpoint_name(request.model, str(api_key_user.id))
  4. UUID branch calls session.get(Flow, flow_id) without checking flow.user_id
Variants tested

Alternate trigger of CVE-2026-55255: the same IDOR in get_flow_by_id_or_endpoint_name is reachable via POST /api/v2/workflows (Developer API). execute_workflow calls get_flow_by_id_or_endpoint_name(workflow_request.flow_id, api_key_user.id) then executes the resolved flow with no check_flow_user_permission; the vulner…

How the agent worked 564 events · 241 tool calls · 28 min
28 minDuration
241Tool calls
151Reasoning steps
564Events
3Dead-ends
Agent activity over 28 min
Support
13
Repro
294
Judge
35
Variant
218
0:0028:29

Root Cause and Exploit Chain for CVE-2026-55255

Versions: Langflow < 1.9.1 (advisory) / < 1.9.2 (NVD). The

Langflow's OpenAI-compatible POST /api/v1/responses endpoint contains an Insecure Direct Object Reference (IDOR). The helper get_flow_by_id_or_endpoint_name resolves a flow by UUID using session.get(Flow, flow_id) without comparing flow.user_id to the caller's identity. Because /api/v1/responses passes the authenticated user's id as the second argument but the UUID branch ignores it, any authenticated user can execute any other user's flow by supplying the victim's flow UUID as the request model value. The fix (commit b0afe3d2d6, PR #12832) adds an ownership check on both the UUID and endpoint-name branches and returns the flow as not-found when the caller is not the owner.

  • Package/component affected: langflow-base 0.9.0 (langflow 1.9.0), helper langflow.helpers.flow.get_flow_by_id_or_endpoint_name, reached via langflow.api.v1.openai_responses.create_response (POST /api/v1/responses).
  • Affected versions: Langflow < 1.9.1 (advisory) / < 1.9.2 (NVD). The vulnerable source analyzed here is commit f52d0f0072 (langflow 1.9.0 / langflow-base 0.9.0), the parent of the fix commit b0afe3d2d6.
  • Risk level and consequences: High. An authenticated (non-superuser) attacker can execute arbitrary flows owned by other tenants by UUID, exposing those flows' outputs (cross-tenant data disclosure), consuming the victims' resources, and affecting integrity. This is a cross-tenant authorization bypass.

Impact Parity

  • Disclosed/claimed maximum impact: Authenticated cross-tenant flow execution / IDOR (authorization bypass) on /api/v1/responses.
  • Reproduced impact from this run: An authenticated low-privileged attacker executed a victim-owned flow through the real POST /api/v1/responses endpoint by supplying the victim's flow UUID as model, receiving a completed response whose output contained the flow's executed result. The same request against the fixed build (commit b0afe3d2d6) was rejected with error.code = "flow_not_found".
  • Parity: full — the claimed IDOR (cross-tenant flow execution) was reproduced end-to-end through the real HTTP API on a running Langflow 1.9.0 server, with a fixed-commit negative control.
  • Not demonstrated: No privilege escalation to superuser, no code execution beyond running an existing flow, and no RCE. The proof demonstrates the authorization bypass (flow execution + output disclosure), which is the claimed impact.

Root Cause

get_flow_by_id_or_endpoint_name(flow_id_or_name, user_id) has two branches:

# vulnerable (commit f52d0f0072, langflow-base 0.9.0)
try:
    flow_id = UUID(flow_id_or_name)
    flow = await session.get(Flow, flow_id)     # <-- no user_id check
except ValueError:
    endpoint_name = flow_id_or_name
    stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
    if user_id:                                  # <-- only scoped when truthy
        uuid_user_id = UUID(user_id) if isinstance(user_id, str) else user_id
        stmt = stmt.where(Flow.user_id == uuid_user_id)
    flow = (await session.exec(stmt)).first()

The UUID branch calls session.get(Flow, flow_id) and returns the flow unconditionally — the user_id argument is never consulted. The endpoint-name branch only filters by user_id when a truthy value is passed.

POST /api/v1/responses (openai_responses.create_response) authenticates the caller via api_key_security (the x-api-key header) and then resolves the flow with the authenticated user's id:

flow = await get_flow_by_id_or_endpoint_name(request.model, str(api_key_user.id))

request.model is attacker-controlled. When it parses as a UUID, the vulnerable UUID branch ignores str(api_key_user.id) and returns any flow with that UUID, regardless of ownership. The flow is then executed by run_flow_for_openai_responses, so the attacker runs the victim's flow and reads its output.

The fix (commit b0afe3d2d6, PR #12832, "fix(security): close IDOR in get_flow_by_id_or_endpoint_name (LE-639)") normalizes user_id once and enforces it on both branches:

flow = await session.get(Flow, flow_id)
if flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id:
    flow = None      # cross-user lookup -> treated as not found (404)

so the shared if flow is None: raise HTTPException(404) fires for cross-tenant lookups and /api/v1/responses returns error.code = "flow_not_found".

Fix commit: b0afe3d2d6 (PR #12832), file src/backend/base/langflow/helpers/flow.py.

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh (self-contained, idempotent).
  2. What it does:
    • Builds the vulnerable Langflow 1.9.0 stack (langflow-base 0.9.0) from the repo source at commit f52d0f0072 (b0afe3d2d6^). PyPI never published langflow 1.9.0/1.9.1/1.9.2 (the 1.x series starts at 1.9.3 which already contains the fix), so the vulnerable stack must be built from source. The sandbox ships only Python 3.14 but langflow 1.9.0 requires <3.14, so a standalone CPython 3.12 is provisioned via uv.
    • Starts a real Langflow server (multi-user mode, SQLite) on 127.0.0.1:7860.
    • Registers a victim and an attacker user via the public POST /api/v1/users/ signup endpoint (both is_superuser: false).
    • Logs both in, mints API keys via POST /api/v1/api_key/.
    • The victim creates a flow (ChatInput→ChatOutput echo, no LLM) and captures the victim flow UUID (VICTIM_FLOW_ID).
    • Ownership proof: GET /api/v1/flows/{id} returns 200 for the victim and 404 for the attacker, confirming the flow is victim-owned and the attacker has no legitimate access.
    • IDOR (vulnerable): the attacker calls POST /api/v1/responses with x-api-key: <attacker> and {"model": "<VICTIM_FLOW_ID>", "input": "<probe>", "stream": false}.
    • Swaps in the fixed helpers/flow.py from commit b0afe3d2d6, restarts the server on the same SQLite DB (same flow/users), and repeats the attacker call.
  3. Expected evidence of reproduction:
    • Vulnerable build: HTTP 200, object: "response", status: "completed", model equals the victim flow UUID, and output[].content[].text contains the attacker's probe string — the victim's flow was executed by the attacker.
    • Fixed build: {"error": {"code": "flow_not_found", "message": "Flow with id '<uuid>' not found"}} — the same request is blocked.
    • The only difference between the two phases is helpers/flow.py, proving the divergence is the missing ownership check.

Evidence

  • bundle/logs/reproduction_steps.log — full run log with version, marker checks, and both IDOR responses.
  • bundle/logs/langflow_server_vuln.log / bundle/logs/langflow_server_fixed.log — server startup/health logs for each phase.
  • bundle/repro/artifacts/victim_register.json / attacker_register.json — signup responses (is_superuser: false).
  • bundle/repro/artifacts/victim_flow_create.json — flow creation response containing VICTIM_FLOW_ID and user_id (victim).
  • bundle/repro/artifacts/ownership_victim_get.txt / ownership_attacker_get.txtGET /api/v1/flows/{id} status codes (200 vs 404).
  • bundle/repro/artifacts/idor_response_vuln.json — vulnerable IDOR response (status: completed, probe in output).
  • bundle/repro/artifacts/idor_response_fixed.json — fixed IDOR response (error.code: flow_not_found).
  • bundle/repro/artifacts/summary.json — structured classification (idor_confirmed: true).
  • bundle/repro/runtime_manifest.json — runtime evidence manifest (service_started/healthcheck_passed/target_path_reached = true).
  • bundle/repro/validation_verdict.json — structured verdict.

Environment: Langflow 1.9.0 (langflow-base 0.9.0) built from source commit f52d0f0072; fixed control commit b0afe3d2d6; Python 3.12 (uv standalone); SQLite; uvicorn backend-only; multi-user mode (LANGFLOW_AUTO_LOGIN=false, signup enabled).

Key excerpt (vulnerable IDOR response):

{
  "object": "response",
  "status": "completed",
  "error": null,
  "model": "<VICTIM_FLOW_UUID>",
  "output": [{ "type": "message", "status": "completed",
    "content": [{ "type": "output_text", "text": "IDOR_PROBE_VULN_..." }] }]
}

Key excerpt (fixed IDOR response):

{ "error": { "message": "Flow with id '<VICTIM_FLOW_UUID>' not found",
  "type": "invalid_request_error", "code": "flow_not_found" } }

Recommendations / Next Steps

  • Upgrade to Langflow >= 1.9.1 (contains the fix from PR #12832 / commit b0afe3d2d6).
  • Fix approach (already applied upstream): enforce user_id on both branches of get_flow_by_id_or_endpoint_name and return the flow as not-found on cross-user lookups, so the shared 404 path fires and flow existence is not disclosed to unauthorized callers.
  • Defense in depth: audit other callers of get_flow_by_id_or_endpoint_name (e.g. webhook/run endpoints) for the same ownership gap; add an integration test that asserts a cross-tenant UUID lookup returns 404 (the upstream regression tests in tests/unit/helpers/test_flow_helpers.py cover this).
  • Testing recommendations: add a multi-tenant API test that creates a flow as user A and asserts user B cannot execute it via /api/v1/responses by UUID.

Additional Notes

  • Idempotency: The script extracts the vulnerable source fresh each run (so the editable langflow-base install always starts from the vulnerable flow.py), reuses a cached venv at the durable project cache path when present, and uses a fresh SQLite DB each run. Consecutive runs produce the same vulnerable/completed vs fixed/flow_not_found divergence.
  • Note on request field: the advisory's example curl uses "input_value", but the actual OpenAIResponsesRequest schema field is "input". The reproduction uses the correct input field; the IDOR is independent of this field (it is triggered by model = victim flow UUID).
  • Note on HTTP status: both the vulnerable (flow executed) and the fixed (flow not found) responses are returned with HTTP 200 by the OpenAI-compatible endpoint; the discriminator is the response body (status: completed + output vs error.code: flow_not_found), which is a stronger signal than the status code.
  • Scope: The vulnerable wt-vuln/wt-fixed worktrees provided in the project cache already contained the fix commit, so the reproduction anchors the vulnerable checkout to b0afe3d2d6^ (f52d0f0072) per the fixed-commit checkout rule, and the fixed checkout to b0afe3d2d6.

Variant Analysis & Alternative Triggers for CVE-2026-55255

Versions: versions (as tested): vulnerable at commit f52d0f0072

This report documents a validated alternate trigger of CVE-2026-55255 — the same Insecure Direct Object Reference (IDOR) in Langflow's get_flow_by_id_or_endpoint_name, reached through a materially different entry point: the v2 Developer API endpoint POST /api/v2/workflows (router prefix /workflows, mounted at /api/v2). Its handler execute_workflow calls get_flow_by_id_or_endpoint_name(workflow_request.flow_id, api_key_user.id) and then executes the resolved flow with no check_flow_user_permission. On the vulnerable helper (f52d0f0072, langflow 1.9.0) the UUID branch ignores api_key_user.id, so an authenticated attacker runs a victim's workflow by supplying the victim's flow UUID as flow_id and reads the executed output — the same cross-tenant authorization bypass and impact class as the disclosed /api/v1/responses IDOR, via a different endpoint and request shape.

This is not a bypass: the released fix (commit b0afe3d2d6, PR #12832) makes the helper enforce flow.user_id != uuid_user_id -> None on the UUID branch, so the identical /api/v2/workflows request returns 404 FLOW_NOT_FOUND on the fixed code. The variant was reproduced on the vulnerable build and confirmed blocked on the fixed build, on the same DB/flow/users, swapping only helpers/flow.py between phases. A bounded search for a true bypass (a path that survives the fix) found none on the authenticated HTTP surface; the only residual user_id=None paths (public webhook, agentic stdio MCP tools) are documented-by-design or a different trust boundary.

Fix Coverage / Assumptions

  • Invariant the fix relies on: every authenticated HTTP caller passes a real user_id to the helper; user_id=None is reserved for webhooks and internal callers that legitimately need cross-user lookup.
  • Code paths the fix explicitly covers:
    • helpers/flow.py::get_flow_by_id_or_endpoint_name — ownership enforced on both the UUID and endpoint-name branches; bad user_id -> 404 (fail-closed).
    • api/v1/endpoints.py — new wrappers get_flow_for_api_key_user / get_flow_for_current_user; the three /run* routes swapped off the bare Depends helper.
    • api/v1/openai_responses.py::create_response and api/v2/workflow.py::execute_workflow / get_workflow_status — already pass api_key_user.id explicitly, so the helper fix auto-closes them.
  • What the fix does NOT cover: it does not mechanically require user_id for authenticated callers (a future route that forgets to pass it reintroduces the IDOR). The public webhook and the agentic stdio MCP tools keep user_id=None (documented-by-design / different trust boundary). See bundle/vuln_variant/patch_analysis.md.

Variant / Alternate Trigger

Entry point: POST /api/v2/workflows (Developer API; requires LANGFLOW_DEVELOPER_API_ENABLED=true, a supported setting: "enable developer API endpoints for advanced debugging and introspection").

Request shape (materially different from the parent):

{ "flow_id": "<VICTIM_FLOW_UUID>", "inputs": {"ChatInput-<id>.input_value": "<probe>"} }

with the attacker's own x-api-key header. (Parent used POST /api/v1/responses with {"model": victimUUID, "input": ...}.)

Code path:

  • src/backend/base/langflow/api/v2/workflow.py::execute_workflow (line 151): flow = await get_flow_by_id_or_endpoint_name(workflow_request.flow_id, api_key_user.id)
  • vulnerable helper src/backend/base/langflow/helpers/flow.py UUID branch: flow = await session.get(Flow, flow_id) — ignores api_key_user.id
  • execute_sync_workflow builds the graph with user_id = str(api_key_user.id) (the attacker's id) and runs the victim's flow, returning its outputs to the attacker. There is no check_flow_user_permission call on this path (unlike the v1 /run* routes), so on the vulnerable version this is a full execution IDOR, not merely an existence oracle.

Bounded search for a bypass (paths that survive the fix):

  • POST /api/v1/webhook/{victimUUID} — resolves + background-runs any flow by UUID with user_id=None. Documented as intentionally public by the fix author; caller receives only 202, output is delivered to ownership-gated SSE listeners. Not a bypass (by-design, different trust boundary).
  • GET /api/v1/webhook-events/{victimUUID} — explicit str(flow.user_id) != str(user.id) check. Already guarded.
  • POST /api/v1/run/{victimUUID} (+ /session, /advanced) — on vulnerable, bare Depends helper (user_id=None) resolves the flow, then check_flow_user_permission blocks with 403 (existence oracle only, no execution). Fix swaps these to auth-aware wrappers (now 404). Alternate trigger, closed by the fix, lower impact (info disclosure, not execution).
  • HTTP-mounted Server("langflow-mcp-server") at /api/v1/mcp — does not use this helper (uses get_flow_snake_case(name, current_user.id, ...)). Not affected.
  • Agentic FastMCP("langflow-agentic") tools — stdio subprocess, optional user_id=None; different trust boundary (MCP client/agent owner), not a cross-tenant HTTP bypass. Residual hardening surface, not a bypass.

No authenticated HTTP path reaches the sink with an attacker-controllable or unset user_id after the fix. No bypass found.

  • Package/component affected: langflow-base 0.9.0 (langflow 1.9.0), helper langflow.helpers.flow.get_flow_by_id_or_endpoint_name, reached via langflow.api.v2.workflow.execute_workflow (POST /api/v2/workflows, Developer API).
  • Affected versions (as tested): vulnerable at commit f52d0f0072 (langflow 1.9.0); fix at commit b0afe3d2d6 (PR #12832). Advisory: <1.9.1; NVD: <1.9.2. Requires developer_api_enabled=true for this variant's entry point (non-default but supported configuration).
  • Risk level and consequences: High. An authenticated (non-superuser) attacker executes arbitrary flows owned by other tenants by UUID and reads their outputs (cross-tenant data disclosure + integrity + resource consumption). Same impact class as the parent CVE.

Impact Parity

  • Disclosed/claimed maximum impact (parent): authenticated cross-tenant flow execution / IDOR (authorization bypass) returning the victim flow's output.
  • Reproduced impact from this variant run: an authenticated attacker executed a victim-owned flow through the real POST /api/v2/workflows endpoint by supplying the victim's flow UUID as flow_id, receiving a WorkflowExecutionResponse with status:"completed" whose outputs.ChatOutput-J6aor.content contained an attacker-injected probe string — i.e. the victim's flow ran with attacker-controlled input and the output was returned to the attacker. The same request on the fixed build returns 404 FLOW_NOT_FOUND.
  • Parity: full. The variant reaches the same sink, crosses the same trust boundary, and yields the same authorization-bypass + output-disclosure impact as the parent, via a different endpoint.
  • Not demonstrated: no code execution / memory primitive is claimed or needed (this is an authorization/IDOR issue).

Root Cause

The shared helper get_flow_by_id_or_endpoint_name performed a raw session.get(Flow, flow_id) on the UUID branch without comparing flow.user_id to the caller's user_id. /api/v2/workflows::execute_workflow passes api_key_user.id (the authenticated caller) as the second argument, but the vulnerable UUID branch discards it, so any authenticated user resolves any flow by primary key and execute_sync_workflow runs it. The fix normalizes user_id once and enforces flow.user_id != uuid_user_id -> None on both branches, so cross-user UUID lookups fall through to the shared 404 path. The same single-helper change that closes the parent /api/v1/responses IDOR also closes this /api/v2/workflows variant (the maintainers note in the commit message that "openai_responses.py and v2/workflow.py pass user_id explicitly, so fixing the helper auto-fixes those endpoints").

Fix commit: b0afe3d2d6506f3d69aff83325d46f0ec481e02a (PR #12832).

Reproduction Steps

  1. Script: bundle/vuln_variant/reproduction_steps.sh (self-contained, idempotent; reuses the prepared project cache's venv + source).
  2. What it does:
    • Extracts the vulnerable source f52d0f0072, ensures the vulnerable helpers/flow.py is installed (editable), and starts a real Langflow 1.9.0 server (multi-user, SQLite) on 127.0.0.1:7861 with LANGFLOW_DEVELOPER_API_ENABLED=true.
    • Registers a victim and an attacker via the public signup endpoint (both is_superuser:false), logs both in, mints API keys.
    • Victim creates a flow (ChatInput→ChatOutput echo, no LLM) and the script captures VICTIM_FLOW_ID + the ChatInput node id.
    • Ownership proof: GET /api/v1/flows/{id} → victim 200, attacker 404 (attacker has no legitimate access).
    • Variant (vulnerable): attacker POST /api/v2/workflows {flow_id: VICTIM_FLOW_ID, inputs: {ChatInput-<id>.input_value: <probe>}} with the attacker's x-api-key.
    • Swaps in the fixed helpers/flow.py (b0afe3d2d6), restarts on the same SQLite DB (same flow/users), re-logs in the attacker, and repeats the identical request. Restores the vulnerable flow.py at the end.
  3. Expected evidence of reproduction:
    • Vulnerable: HTTP 200, object:"response", status:"completed", and the attacker-injected probe in outputs.ChatOutput-J6aor.content — the victim's flow executed by the attacker. (Observed: probe IDOR_VARIANT_PROBE_V2WF_... in the response.)
    • Fixed: HTTP 404 {"detail":{"error":"Flow not found","code":"FLOW_NOT_FOUND",...}}. (Observed.)

Evidence

  • bundle/vuln_variant/artifacts/variant_response_vuln.json — vulnerable IDOR response (status:"completed", probe in outputs.ChatOutput-J6aor.content).
  • bundle/vuln_variant/artifacts/variant_response_fixed.json — fixed response (http_status=404, code:"FLOW_NOT_FOUND").
  • bundle/vuln_variant/artifacts/ownership_victim_get.txt / ownership_attacker_get.txt — 200 vs 404 ownership proof.
  • bundle/vuln_variant/artifacts/victim_flow_create.json — victim flow + VICTIM_FLOW_ID + user_id (victim).
  • bundle/vuln_variant/artifacts/victim_register.json / attacker_register.jsonis_superuser:false signups.
  • bundle/vuln_variant/artifacts/summary.json — structured classification.
  • bundle/vuln_variant/runtime_manifest.json — runtime evidence manifest.
  • bundle/vuln_variant/validation_verdict.json — structured verdict.
  • bundle/logs/vuln_variant/reproduction_steps.log — full run log.
  • bundle/logs/vuln_variant/langflow_server_vuln.log / langflow_server_fixed.log — server startup/health per phase.

Environment: Langflow 1.9.0 (langflow-base 0.9.0) built from source at f52d0f0072; fixed phase at b0afe3d2d6; SQLite; uvicorn; Python 3.12 (via uv); LANGFLOW_DEVELOPER_API_ENABLED=true.

Recommendations / Next Steps

  • No fix gap for the authenticated HTTP surface. The released fix (b0afe3d2d6) closes this variant. Coders should treat /api/v2/workflows as a co-affected endpoint of the same CVE (it is already fixed by the helper change; verify the deployed version is >=1.9.1/>=1.9.2).
  • Regression guardrail: make get_flow_by_id_or_endpoint_name require an explicit user_id for any caller not on a public/internal allowlist, instead of trusting every caller to pass it. Prevents a future route from reintroducing the IDOR by forgetting user_id.
  • Agentic MCP tools: default the optional user_id argument to the authenticated/agent-context user instead of None, so an agent cannot resolve another tenant's flow by UUID by omitting the argument.
  • Public webhook: consider per-flow webhook opt-in so an unauthenticated caller cannot trigger arbitrary flows by UUID (separate from this CVE's authenticated authorization model, but worth hardening).

Additional Notes

  • Idempotency: the script was run three times end-to-end; all runs reproduced the variant on the vulnerable build (status:"completed", probe in output) and confirmed the block on the fixed build (404 FLOW_NOT_FOUND), with SCRIPT_EXIT_CODE=1 (alternate trigger, not a bypass) each time. The script restores the vulnerable flow.py on exit so the project cache is left as the repro stage expects.
  • Exit-code semantics: 0 would mean a true bypass (reproduced on fixed); 1 = alternate trigger reproduced on vulnerable only / no bypass; 2 = infrastructure failure. This run is 1.
  • Caveat: the /api/v2/workflows entry point requires developer_api_enabled=true (default false). This is a supported, documented configuration, so the variant is valid; deployments with the Developer API disabled are not exposed via this specific endpoint (but the /api/v1/responses parent IDOR affects them on the vulnerable version regardless).
  • Out of scope ruled out: the public webhook and agentic stdio MCP tools are not claimed as variants (documented-by-design / different trust boundary).

CVE-2026-55255 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:32
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-55255 · REPRO-20
0:02
0:03
web search
0:05
web search
0:06
0:08
0:19
0:19
extract_facts
no facts extracted
0:20
0:20
0:20
supportrepro
0:22
0:22
0:23
0:23
0:24
0:24
0:24
0:25
0:25
0:25
0:28
0:28
0:28
$cd /data/pruva/project-cache/688af86e-dc4c-4d20-bb02-ccdf8da2c7d0 && echo "=== wt-vuln HEAD ===" && git -C wt-vuln log --oneline -1 && echo "=== wt-fixed HEAD ===" && git -C wt-fixed log --oneline -1 && echo "=== repo HEAD ===" && git -C repo log --oneline -1 && echo "=== branches ===" && git -C repo branch -a
0.2s
=== wt-vuln HEAD ===
b11bd1f261 chore: update lock files
=== wt-fixed HEAD ===
69982462bb fix: add SSRF protection to legacy RSS Reader and SearXNG components (#13395)
=== repo HEAD ===
315cc41b43 ci: surface nightly Group 3 stall via 90s pytest thread-timeout (#13938)
=== branches ===
* main
0:30
0:30
0:30
0:30
0:30
0:32

Artifacts and Evidence for CVE-2026-55255

Scripts, logs, diffs, and output captured during the reproduction.

bundle/artifact_promotion_manifest.json15.5 KB
bundle/artifact_promotion_report.json21.5 KB
bundle/vuln_variant/source_identity.json2.4 KB
bundle/vuln_variant/root_cause_equivalence.json2.5 KB
bundle/repro/reproduction_steps.sh23.6 KB
bundle/repro/rca_report.md10.6 KB
bundle/repro/runtime_manifest.json1.1 KB
bundle/repro/validation_verdict.json1.0 KB
bundle/repro/artifacts/idor_response_vuln.json0.7 KB
bundle/repro/artifacts/idor_response_fixed.json0.1 KB
bundle/repro/artifacts/summary.json0.4 KB
bundle/logs/reproduction_steps.log3.3 KB
bundle/logs/langflow_server_vuln.log3.2 KB
bundle/logs/langflow_server_fixed.log2.5 KB
bundle/repro/artifacts/victim_register.json0.3 KB
bundle/repro/artifacts/attacker_register.json0.3 KB
bundle/repro/artifacts/victim_flow_create.json21.4 KB
bundle/repro/artifacts/ownership_victim_get.txt0.1 KB
bundle/repro/artifacts/ownership_attacker_get.txt0.1 KB
bundle/vuln_variant/artifacts/variant_response_vuln.json0.4 KB
bundle/vuln_variant/artifacts/variant_response_fixed.json0.2 KB
bundle/vuln_variant/artifacts/summary.json0.7 KB
bundle/logs/vuln_variant/reproduction_steps.log13.8 KB
bundle/vuln_variant/validation_verdict.json1.4 KB
bundle/vuln_variant/variant_manifest.json5.0 KB
bundle/vuln_variant/reproduction_steps.sh24.0 KB
bundle/vuln_variant/rca_report.md13.1 KB
bundle/vuln_variant/patch_analysis.md10.1 KB
bundle/vuln_variant/runtime_manifest.json1.3 KB
bundle/vuln_variant/artifacts/ownership_victim_get.txt0.1 KB
bundle/vuln_variant/artifacts/victim_flow_create.json21.4 KB
bundle/logs/vuln_variant/langflow_server_vuln.log3.6 KB
bundle/logs/vuln_variant/langflow_server_fixed.log2.5 KB
bundle/vuln_variant/artifacts/victim_register.json0.3 KB
bundle/vuln_variant/artifacts/attacker_register.json0.3 KB
bundle/vuln_variant/artifacts/ownership_attacker_get.txt0.1 KB
08 · How to Fix

How to Fix CVE-2026-55255

Upgrade langflow (pip) · pip to >=1.9.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-55255 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-55255

How does the CVE-2026-55255 IDOR attack work?

An authenticated, non-superuser attacker sends a request to POST /api/v1/responses with the model value set to another user's flow UUID. Because get_flow_by_id_or_endpoint_name looks up the flow purely by UUID without an ownership check, Langflow executes the victim's flow and returns its output to the attacker, exposing that user's flow logic and data across tenants.

Which Langflow versions are affected by CVE-2026-55255, and where is it fixed?

Langflow versions before 1.9.1 are affected. It is fixed in 1.9.1 and later, which adds an ownership check on both the UUID and endpoint-name resolution branches and returns the flow as not-found when the caller isn't the owner.

How severe is CVE-2026-55255?

It is rated critical severity with a CVSS score of 9.9. Any authenticated user can execute arbitrary flows belonging to other tenants, exposing their outputs and consuming their resources without needing superuser privileges.

How can I reproduce CVE-2026-55255?

Download the verified script from this page and run it in an isolated environment against Langflow before 1.9.1. It authenticates as one user, creates a flow, then authenticates as a second user and calls /api/v1/responses with the first user's flow UUID to show cross-tenant execution succeeding, then confirms the fixed version rejects it.
11 · References

References for CVE-2026-55255

Authoritative sources for CVE-2026-55255 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.