What happened
Chainlit's MCP stdio transport validated only the executable name of a caller-supplied command string, not its arguments, and treated an unset allowlist as permit-all, letting an unauthenticated network attacker achieve OS command injection (CWE-78) via POST /mcp. A companion flaw (CVE-2026-45019, CVSS 7.2) affects the SSE/streamable-HTTP transports with SSRF because the same endpoint accepts an attacker-selected upstream MCP URL without validating against loopback/internal addresses.
Why it matters
This is unauthenticated, pre-auth RCE (CVSS 9.8) in a popular AI-application framework whenever the MCP feature — increasingly the default way to wire LLM agents to tools — is turned on. An attacker with no credentials can get shell execution with the privileges of the Chainlit process, which typically also holds LLM provider API keys and access to backend data sources.
Attack vector
validate_mcp_command() in backend/chainlit/mcp.py checks only the executable name in the attacker-supplied fullCommand string against config.features.mcp.stdio.allowed_executables, then passes remaining arguments unchecked to StdioServerParameters. If allowed_executables is unset (the default), it permits every executable rather than none, letting an unauthenticated caller submit a command like an allowed executable combined with flags that execute arbitrary shell commands.
Affected systems
Chainlit (Python conversational-AI framework) 2.4.0rc0 – 2.11.x
Mitigation
Upgrade to Chainlit 2.12.0; explicitly set and restrict features.mcp.stdio.allowed_executables; disable MCP if not required; see Chainlit's own security advisory.