CVE-2026-45018: Chainlit MCP stdio unauthenticated RCE (and sibling CVE-2026-45019)

CVE-2026-45018: Chainlit MCP stdio unauthenticated RCE (and sibling CVE-2026-45019)

How to fix CVE-2026-45018: upgrade chainlit to 2.12.0, then restart so /mcp loads the new wheel

3 min read564 words
Contents

CVE-2026-45018: Chainlit MCP stdio unauthenticated RCE

Chainlit 2.12.0 (2026-08-25) closes two bugs on /mcp. CVE-2026-45018 is unauthenticated command execution via the MCP stdio transport. CVE-2026-45019 is unauthenticated SSRF via SSE and streamable-http. Both require features.mcp.enabled = true. MCP is off by default since 2.7.0. If that flag is false, you can stop here.

Who is not in scope

  • features.mcp.enabled is missing or false in .chainlit/config.toml.
  • You already run Chainlit 2.12.0 and the live process loaded that wheel.
  • The Chainlit HTTP port is not reachable from networks you do not trust, and MCP will stay off.

This is not every MCP CVE in the ecosystem. It is not LiteLLM's authenticated MCP stdio test-endpoint issue.

What broke

With MCP on, POST /mcp accepted a client-supplied fullCommand when clientType was stdio. validate_mcp_command() in backend/chainlit/mcp.py split the string and checked only the executable basename against allowed_executables (examples: npx, uvx). Arguments were not inspected. Common Node launchers can run an arbitrary shell string through a short flag, so an allowlisted basename plus client-controlled arguments still ran as the Chainlit user before the MCP handshake failed. SPL Security (Vipin and Stephen) reported working proofs against 2.11.0. Vendor severity for SPL-2026-001 / CVE-2026-45018 is CVSS 3.1 9.8. Affected range: >=2.4.0rc0, <2.12.0 with MCP enabled. GitHub advisory GHSA-w3fx-mc44-mf6j.

CVE-2026-45019 (SPL-2026-002, CVSS 3.1 7.2, GHSA-hvfh-5mj3-5f3j) is the sibling. For sse and streamable-http, the same endpoint took a raw url and optional headers with no scheme check, no host allowlist, and no header denylist. The process made outbound HTTP, including caller-controlled auth headers, toward internal hosts and link-local metadata endpoints. Blind SSRF: response bodies stay inside the MCP client. Header forwarding for that path arrived in 2.6.4.

If allowed_executables is removed so the value is None, validation allows every executable. The allowlist is not a safety net by itself.

What this is not

Not default-on. Not fixed by shrinking allowed_executables; 2.12.0 removes client-supplied commands. After upgrade, anonymous clients can still start developer-named stdio servers by name if auth is off. That is pinned-command start, not client-chosen RCE.

How to check

Run pip show chainlit and print chainlit.__version__ from the same environment that serves the app. Open .chainlit/config.toml and look for features.mcp.enabled, allowed_executables, user_servers, and [[features.mcp.servers]]. A Version below 2.12.0 with MCP enabled is exposed. Restart after upgrade so an old wheel is not still listening.

How to fix

pip install --upgrade 'chainlit==2.12.0'

Then restart Chainlit. 2.12.0 breaks old MCP config: [features.mcp.sse], [features.mcp.stdio], [features.mcp.streamable-http], and allowed_executables abort startup when MCP is on. Declare stdio servers under [[features.mcp.servers]] and connect by name. User-provided SSE/HTTP needs [features.mcp.user_servers] enabled = true and a non-empty allowed_urls. Redirects are not followed; put the final HTTPS URL in config.

If you cannot upgrade today: set features.mcp.enabled = false, restrict egress, and register auth so /mcp is not anonymous. Auth alone does not remove command execution for an authenticated user on vulnerable builds; it only removes the anonymous path.

GitHub advisory pages still showed empty patched versions at research time; the 2.12.0 release and in-tree MCP advisory name 2.12.0 as the fix.

This is the operator write-up. The HOL Guard evidence page is the source record for CVE-2026-45018; sibling evidence for CVE-2026-45019 is at HOL Guard CVE-2026-45019.

References

Continue reading

All posts