{"id":"CVE-2026-55255","aliases":["GHSA-qrpv-q767-xqq2","PYSEC-2026-221"],"url":"https://o3.security/vulnerability/CVE-2026-55255","summary":"Langflow: IDOR Vulnerability in `/api/v1/responses` Endpoint Allows Authenticated Attackers to Access Another User's Flow","details":"## Summary\n\nInsecure Direct Object Reference (IDOR) vulnerability in `/api/v1/responses` endpoint allows an authenticated attacker to execute any flow belonging to another user by specifying the victim's flow ID in the request.\n\n## Details\n\nThe vulnerability exists in the `get_flow_by_id_or_endpoint_name` helper function in [`src/backend/base/langflow/helpers/flow.py` (lines 399-414)](https://github.com/langflow-ai/langflow/blob/v1.9.0/src/backend/base/langflow/helpers/flow.py#L399C1-L414C67).\n\nWhen a flow is accessed via UUID (flow_id), the function queries the database directly without verifying if the authenticated user owns that flow:\n\n```python\n# src/backend/base/langflow/helpers/flow.py:399-414\nasync def get_flow_by_id_or_endpoint_name(flow_id_or_name: str, user_id: str | UUID | None = None) -> FlowRead:\n    async with session_scope() as session:\n        try:\n            flow_id = UUID(flow_id_or_name)\n            # When using UUID, query directly WITHOUT checking user_id\n            flow = await session.get(Flow, flow_id)  # ❌ No user_id check!\n        except ValueError:\n            endpoint_name = flow_id_or_name\n            stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)\n            # Only when using endpoint_name is user_id checked\n            if user_id:\n                stmt = stmt.where(Flow.user_id == uuid_user_id)\n```\n\nThis function is used by the `/api/v1/responses` endpoint (defined in [`src/backend/base/langflow/api/v1/openai_responses.py:589`](https://github.com/langflow-ai/langflow/blob/v1.9.0/src/backend/base/langflow/api/v1/openai_responses.py#L589)).\n\n## PoC (Proof of Concept)\n\n```bash\n# Attacker (user A) with API_KEY_A tries to execute victim (user B)'s flow\ncurl -X POST \"http://localhost:7860/api/v1/responses\" \\\n  -H \"x-api-key: sk-ATTACKER_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"VICTIM_FLOW_ID\",\n    \"input_value\": \"test\",\n    \"stream\": false\n  }'\n# Returns 200 and executes the victim's flow\n```\n\n## Impact\n\nAny authenticated user can:\n1. Execute any flow in the system by knowing its flow ID\n2. Access potentially sensitive data processed by victim's flows\n3. Consume victim's resources\n\n## Fixes\n\nFixed in **PR #12832** (`fix(security): close IDOR in get_flow_by_id_or_endpoint_name`), merged 2026-04-22, released in **Langflow 1.9.1**.\n\nThe helper normalizes `user_id` once and enforces ownership on **both** lookup branches (UUID *and* `endpoint_name`):\n\n```python\nflow_id = UUID(flow_id_or_name)\nflow = await session.get(Flow, flow_id)\nif flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id:\n    flow = None  # cross-user lookup falls through to the shared 404\n```\n\nKey points:\n- Cross-user lookups return **404** (not 403), so flow existence is not disclosed via a 403-vs-404 oracle.\n- `/api/v1/responses` and `/api/v2/workflow` pass `user_id` explicitly, so fixing the helper closes them directly; the `/api/v1/run*` routes were additionally moved from a bare `Depends(get_flow_by_id_or_endpoint_name)` to auth-aware wrapper dependencies (defense in depth).\n- A malformed `user_id` now fails closed (404 instead of a raw 500).\n- Webhook routes intentionally keep the unscoped lookup (public by design / explicit ownership check elsewhere).\n- Regression tests cover the cross-user UUID case and reproduce the original PoC against `/api/v1/responses`.\n\n\n\n\n\n## Acknowledgements\n\nThanks to the security researchers who responsibly disclosed this vulnerability:\n* @yzeirnials\n* @johnatzeropath\n* @LeftenantZero\n* @Zwique","published":"2026-06-23T16:28:20.985Z","modified":"2026-08-12T03:51:09.019174573Z","cvss":{"score":8.4,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:L"},"epss":{"score":0.00887,"percentile":0.57499,"asOf":"2026-09-16"},"cisaKev":{"dateAdded":"2026-07-07","dueDate":"2026-07-10","knownRansomwareCampaignUse":false},"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"langflow","fixedVersion":"1.9.1"}],"fix":{"url":"https://github.com/langflow-ai/langflow/commit/2c9f498d664a3c32698b57d7c5e752625291060e","label":"langflow-ai/langflow@2c9f498"},"references":[{"type":"WEB","url":"https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-55255"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55255.json"},{"type":"ADVISORY","url":"https://github.com/langflow-ai/langflow/security/advisories/GHSA-qrpv-q767-xqq2"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-55255"},{"type":"ADVISORY","url":"https://webflow.sysdig.com/blog/understanding-langflow-cve-2026-55255-and-why-higher-cvss-vulnerabilities-arent-always-the-most-exploited"},{"type":"FIX","url":"https://github.com/langflow-ai/langflow/commit/2c9f498d664a3c32698b57d7c5e752625291060e"},{"type":"FIX","url":"https://github.com/langflow-ai/langflow/pull/12832"},{"type":"PACKAGE","url":"https://github.com/langflow-ai/langflow"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/langflow/PYSEC-2026-221.yaml"}],"provenance":{"sources":["OSV.dev","CISA KEV","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:09.019174573Z"}}