Two 9.9s, One Sink, Three Different “Fixed In” Versions — Langflow’s MCP Stdio RCE
On 5 October 2026, two CVE records for Langflow were published 30 minutes apart. CVE-2026-105697 went out at 20:16 UTC and CVE-2026-105740 at 20:46 UTC. Both carry CVSS 9.9 on the same vector string, both are CWE-78, and both describe the identical thing: the Model Context Protocol stdio transport launching a user-supplied command as an operating-system process. They are the same bug at the same sink.
They do not agree on when it was fixed. CVE-2026-105740 says the flaw affects versions “prior to 1.9.0” and “is fixed in 1.9.0.” CVE-2026-105697 says the affected range is >= 1.1.2, < 1.10.3 and that the fix is 1.10.3. An operator who patched to 1.9.0 on the strength of the first record spent four more releases exposed. We read the source at four release tags to work out which record is right, and the answer is the more alarming one — with a wrinkle that neither record states cleanly.
What the two advisories actually claim
Both CVEs were assigned by GitHub from repository advisories whose GitHub publication dates predate the CVE records: GHSA-7w94-79vh-5mr2 (→ CVE-2026-105740) went up on 10 September 2026 and lists exactly one affected version, 1.8.3; GHSA-w794-rj3p-xv45 (→ CVE-2026-105697) went up on 28 September 2026 with the full 1.1.2 – 1.10.2 range across three PyPI packages (langflow, langflow-base < 0.10.3, and lfx < 1.10.3). Both were edited on 5 October, minutes before their CVE records appeared.
The second advisory is unusually candid about why there are two numbers. It states the issue “was fixed in two steps”: pull request #12290 in 1.9.0 added a command allowlist to the REST model, and pull request #14036 in 1.10.3 applied the same policy at the execution sink. In other words, CVE-2026-105740 is not wrong about its reported vector — the “Add MCP Server” settings page — and is wrong about the vulnerability class. That distinction is invisible to anyone reading a severity feed.
Reading the code at four tags
We fetched src/lfx/src/lfx/base/mcp/util.py and src/backend/base/langflow/api/v2/schemas.py at v1.9.0, v1.10.0, v1.10.2 and v1.10.3 from the project repository. The sequence is cleaner than either advisory describes:
- v1.9.0 — allowlist at the door, nothing at the sink.
schemas.pydefinesALLOWED_MCP_COMMANDSand avalidate_commandfield validator.util.pycontains no reference to any validation helper, and_connect_to_serverstill buildsStdioServerParameters(command="bash", args=["-c", f"exec {command_str} || echo ..."])on POSIX and acmd /cequivalent on Windows. Anything that reaches the launcher without passing through the Pydantic model — a config embedded in a flow, a tweak, the MCP Tools component — executes unchecked. - v1.10.0 — unchanged. Same
bash -cwrapper, same absent sink validation. - v1.10.2 — the shell goes away, the gap stays. The launcher now execs the binary directly with structured arguments and an explicit
shell=Falsecomment noting that “no shell interpreter is ever interposed.” That removes metacharacter injection. It does not add validation:validate_mcp_stdio_configstill appears nowhere in the file, so an arbitrary executable is still spawnable, just without a shell to chain through. - v1.10.3 — the sink is finally guarded.
util.pyimportsvalidate_mcp_stdio_configfromlfx.base.mcp.securityand calls it at three points, including immediately before spawn.ALLOWED_MCP_COMMANDShas moved out of the API schema into the shared security module so one policy serves both paths.
This matters for a claim in CVE-2026-105697 itself. The record says 1.10.3 “removed the bash -c wrapper.” The code says the wrapper was already gone in 1.10.2, published to PyPI on 7 July 2026, sixteen days before 1.10.3. What 1.10.3 added was the missing authorization check at the sink. The distinction is not pedantry: a defender who reads “removed the shell wrapper” as the fix, and who confirms the wrapper is absent in their deployed 1.10.2, will conclude they are patched. They are not.
The default that makes this unauthenticated
Both records describe the bug as requiring privileges — CVSS PR:L, any authenticated non-admin user. The advisory then notes that with the default LANGFLOW_AUTO_LOGIN=true, GET /api/v1/auto_login issues a token with no credentials at all. On an exposed instance running stock configuration, the privilege requirement evaporates. Langflow documents AUTO_LOGIN as a development-only setting; the exploitation record of this project suggests that documentation is not doing the work. CISA added a Langflow origin-validation flaw to the Known Exploited Vulnerabilities catalog in May, and the platform has been a recurring target since.
An allowlist that allows bash
Worth reading the policy before trusting it. ALLOWED_MCP_COMMANDS at 1.10.3 is {node, python, python3, npx, uvx, docker, cmd, sh, bash}. Three of those nine entries are shells. The maintainers handle this by treating cmd/sh/bash as wrappers and recursively validating the wrapped payload with an explicit depth bound — a defensible design given that Langflow’s own starter projects ship sh -c uvx ... invocations, but one whose safety rests entirely on the recursive parser being complete.
Two follow-up fixes in the same release window show the parser was not complete on the first attempt. PR #14122 (merged 16 July) found that the Docker policy inspected arguments for host-access flags without first requiring the run subcommand, so docker cp reached the daemon unchecked; its own description notes that deployments leaving LANGFLOW_MCP_SERVER_DOCKER_HARDENING=false with daemon access were affected, while published Langflow images enable hardened mode. PR #14149 (17 July) then denied non-superusers local stdio configuration entirely when custom code execution is locked down. An allowlist containing a container runtime and three shells is a parser, and parsers accumulate CVEs — the same structural problem as the shell-operator bypass in Ollama’s agent approval gate.
Two more MCP records in the same batch
- CVE-2026-105741 (CVSS 7.1, CWE-290) —
get_client_iptrusted the leftmost, fully client-controlled entry ofX-Forwarded-Forwith no trusted-proxy check, letting a remote attacker spoof127.0.0.1and pass the “local-only” gate onPOST /api/v1/mcp/project/{project_id}/installto write an MCP client config on the server filesystem. Affects 1.5.0 to 1.10.2. The advisory includes an explicit correction to the original report, noting the destination path is not attacker-supplied but resolved from a fixed set of per-OS developer-tool paths. Credit to the maintainers for publishing the correction rather than the louder version. - CVE-2026-105699 (CVSS 7.1, CWE-639) — project-scoped MCP connections authenticated the
project_idin the URL but never authorized the URI passed toresources/read, sohandle_read_resourceparsed an attacker-suppliedflow_idand calledstorage_service.get_fileacross tenants. Affects 1.6.8 through 1.9.0, fixed in 1.9.1. Authorization at connection time, not at resource time — precisely the missing field-level control that MLCommons named in its privacy taxonomy last week.
The pattern worth naming
Langflow’s 1.9.0 fix was a real fix of a real vector, shipped quickly after a good report. It was also validation attached to one data path into a sink that had several. That shape — guard the API model, leave the executor open — is the most common way an AI-platform patch under-delivers, because the REST schema is where reviewers look and the component loader is not. The same gap produced the MySQL MCP server’s unauthenticated SSE path, and it is why “patched” needs to mean “patched at the sink.”
The second-order problem is the metadata. Two 9.9 records, published half an hour apart, describing one bug, pointing at different fixed versions, is a scanner-accuracy problem before it is a disclosure problem. We have now seen three variants of this failure in six weeks — an off-by-one patched version in a Grafana MCP PoC, a malware advisory naming the wrong dependency, and now a duplicate pair that disagrees with itself. Vulnerability data is becoming the weak link in the AI supply chain.
What to do
- Treat 1.10.3 as the floor, not 1.9.0. If your inventory says “Langflow ≥ 1.9.0, not vulnerable” because it matched CVE-2026-105740, re-evaluate against CVE-2026-105697’s range. Current PyPI release is 1.12.4;
langflow-baseneeds ≥ 0.10.3 andlfx≥ 1.10.3 as separate pins. - Check 1.10.2 specifically. It is the trap version: the shell wrapper is visibly gone, so a code-level spot check looks clean, but the sink has no validation.
- Disable
LANGFLOW_AUTO_LOGINon anything reachable. It is the difference between an authenticated-user bug and an internet-facing unauthenticated RCE, and it is on by default. - Audit stdio MCP configs embedded in flows, not just in settings. The vector that survived 1.9.0 was the one that never touched the REST model. Enumerate MCP Tools components and tweak payloads too.
- Set
LANGFLOW_MCP_SERVER_DOCKER_HARDENINGand the code-execution lockdown. Published Docker images enable hardened mode; source and custom deployments may not. If the Langflow process can reach a Docker socket, the allowlist’sdockerentry is a host-compromise primitive. - Do not grant stdio MCP configuration to ordinary users. The 1.10.3-era fix restricts it to superusers under lockdown. Make that your policy regardless of version.
Verification note: we read the CVE records for CVE-2026-105697, CVE-2026-105740, CVE-2026-105741 and CVE-2026-105699 from the MITRE CVE Services API (publication timestamps 20:16:58Z, 20:46:18Z, 20:56:00Z and 20:37:59Z on 5 October 2026), and the corresponding GitHub repository advisories for their original publication dates, version ranges and full descriptions. The version analysis is ours: we fetched src/lfx/src/lfx/base/mcp/util.py and src/backend/base/langflow/api/v2/schemas.py at tags v1.9.0, v1.10.0, v1.10.2 and v1.10.3 and confirmed the presence of the bash -c "exec ..." wrapper through v1.10.0, its absence in v1.10.2, and the first appearance of validate_mcp_stdio_config calls in v1.10.3. Release and merge dates come from the GitHub releases and pull-request APIs; PyPI upload timestamps (1.10.2 on 7 July 2026, 1.10.3 on 23 July 2026) and the current 1.12.4 release come from the PyPI JSON API. We did not test any exploit and did not contact the maintainers before publication.
Sources:
- GitHub Security Advisory GHSA-w794-rj3p-xv45 — Langflow < 1.10.3: OS command injection via MCP stdio server configuration (CVE-2026-105697; the two-step fix narrative and version table)
- GitHub Security Advisory GHSA-7w94-79vh-5mr2 — Authenticated RCE via MCP Stdio transport (CVE-2026-105740; the “fixed in 1.9.0” claim and 1.8.3 affected entry)
- langflow-ai/langflow PR #12290 — prevent MCP command injection via allowlist validation (CWE-78), merged 27 March 2026, shipped in 1.9.0
- langflow-ai/langflow PR #14036 — harden MCP stdio configuration, merged 14 July 2026 (shared policy at the execution sink)
- langflow-ai/langflow PR #14122 — require docker run for MCP stdio, merged 16 July 2026 (the non-
runsubcommand gap) - langflow-ai/langflow release v1.10.3 (23 July 2026; the security backport list)
- GitHub Security Advisory GHSA-4f6c-2vvp-gw82 — X-Forwarded-For spoofing allows remote configuration write (CVE-2026-105741)
- GitHub Security Advisory GHSA-4hmc-cfm3-w43c — cross-project file disclosure via unscoped MCP resource handlers (CVE-2026-105699)