SIGN IN SIGN UP

fix(security): refuse MCP stdio server configuration on the public flow path (#14644)

* fix(security): refuse MCP stdio server configuration on the public flow path

The public-flow guards only reason about the node `code` field:
`validate_public_flow_no_code_execution` matches component types and code
hashes, and `prepare_public_flow_build` substitutes the server's trusted
source into every code-bearing node. An MCP Tools node's stdio server
configuration — the executable and its arguments — lives in the `mcp_server`
input's VALUE instead, so neither guard inspected or rewrote it. A public flow
carrying one therefore let an unauthenticated visitor cause the server to spawn
that process under the Langflow service account, which is the exact outcome the
public-flow guards exist to prevent.

`validate_public_flow_no_code_execution` now rejects a public build whose graph
selects the MCP stdio transport, mirroring `update_tools`' own transport
selection (explicit `mode` of `Stdio`, otherwise the presence of `command`) so a
configuration this check passes cannot become a spawn at the sink. Detection
follows the stored configuration's shape rather than the node type, because the
same component is legitimate over HTTP/SSE: remote MCP transports spawn nothing
and keep working on public flows. The stdio-only legacy `MCPStdio` component is
blocked by type. The scan walks every template field and recurses into inlined
sub-flows, matching the existing blocklist collector.

Placing the check in the shared validator covers all four unauthenticated
execute paths (v1 build_public_tmp, v2 workflows/public, and both A2A entry
points), both at their route-level precheck and at the post-substitution
recheck inside `prepare_public_flow_build`, including under the
`allow_public_custom_components` opt-in. Authenticated builds are unaffected.

Regression tests cover the guard functions directly (stdio via `command`,
via explicit `mode`, bare and `mcpServers`-map shapes, a relabelled field key,
nested sub-flows, the legacy component type, and the opt-in path), assert that
HTTP/SSE configurations still build, assert no false positive on an ordinary
dict input carrying a `command` key, and exercise both reported endpoints
end to end.

Fixes LE-2244

* test(security): exercise the genuinely unauthenticated public path

The shared test client persists access-token cookies from logged_in_headers, so
setting client_id alone left the request authenticated and the test did not cover
the unauthenticated boundary it claims. Clear cookies first, matching the
_send_unauthenticated helper in tests/unit/api/v2/test_workflow_public.py.

The guard still rejects the build, so this strengthens the assertion without
changing the outcome.
E
Eric Hare committed
d6967f6657668b40c5641bebf3a1299758fd7f91
Parent: d4c8ea1
Committed by GitHub <noreply@github.com> on 8/19/2026, 5:26:03 PM