CVE-2026-45018 Overview
CVE-2026-45018 is a command injection vulnerability in Chainlit, a Python framework for building production-ready conversational AI applications. Affected versions from 2.4.0rc0 up to (but not including) 2.12.0 expose an unauthenticated POST /mcp endpoint when features.mcp.enabled is set to true in .chainlit/config.toml. An attacker can supply a crafted fullCommand string to execute arbitrary shell commands with the privileges of the Chainlit process. The flaw is tracked as [CWE-78] (OS Command Injection) and fixed in version 2.12.0.
Critical Impact
Unauthenticated remote attackers can achieve arbitrary command execution on any Chainlit deployment with the Model Context Protocol (MCP) feature enabled.
Affected Products
- Chainlit 2.4.0rc0 through versions prior to 2.12.0
- Deployments with features.mcp.enabled = true in .chainlit/config.toml
- Configurations where config.features.mcp.stdio.allowed_executables is unset (defaults to None, permitting any executable)
Discovery Timeline
- 2026-08-25 - CVE-2026-45018 published to the National Vulnerability Database (NVD)
- 2026-08-25 - Last updated in the NVD database
Technical Details for CVE-2026-45018
Vulnerability Analysis
The vulnerability resides in Chainlit's MCP integration. When MCP is enabled, the POST /mcp endpoint is exposed without any authentication requirement. For the stdio transport, the request body accepts a user-controlled fullCommand string that Chainlit uses to launch a subprocess. The validate_mcp_command() function in backend/chainlit/mcp.py inspects only the executable name and matches it against config.features.mcp.stdio.allowed_executables. It then forwards the remaining, unvalidated arguments to StdioServerParameters in backend/chainlit/server.py.
Because tools such as npx accept a -c flag that runs an arbitrary shell string, an attacker who supplies npx -c "<command>" bypasses the executable allowlist entirely. This yields code execution as the Chainlit process user, which typically owns application secrets, model API keys, and access to internal services.
Root Cause
The root cause is incomplete input validation of the fullCommand argument combined with missing authentication on the MCP endpoint. Validation logic gates only the executable basename and never sanitizes subsequent arguments. Additionally, an unset allowed_executables value defaults to None, which the code interprets as "allow all," widening the attack surface for default deployments.
Attack Vector
Exploitation is remote and requires no authentication or user interaction. An attacker sends a single HTTP POST request to /mcp containing a stdio transport specification with a malicious fullCommand. Any executable that supports inline shell evaluation (for example npx -c, bash -c, python -c) can serve as the command injection primitive to reach arbitrary OS command execution.
# Patch excerpt from backend/chainlit/session.py (Chainlit 2.12.0)
# Source: https://github.com/Chainlit/chainlit/commit/0565fd0eccb915fce159929598b053ed79f6e0c9
_CLOSE_TIMEOUT = 10.0 # seconds to wait for a background MCP task to finish
async def stop_mcp_task(
task: asyncio.Task, stop_event: asyncio.Event, name: str
) -> None:
"""Signal an MCP background task to shut down and wait for it."""
stop_event.set()
try:
await asyncio.wait_for(task, timeout=_CLOSE_TIMEOUT)
except asyncio.TimeoutError:
logger.warning(
"MCP session %r did not shut down within %.1fs — cancelling",
name,
_CLOSE_TIMEOUT,
)
task.cancel()
The fix adds bounded task shutdown for MCP sessions and, per the accompanying GitHub Security Advisory GHSA-w3fx-mc44-mf6j, tightens validation of MCP stdio commands.
Detection Methods for CVE-2026-45018
Indicators of Compromise
- Unexpected POST /mcp requests in Chainlit access logs, especially from external IPs
- Child processes spawned by the Chainlit process running sh, bash, npx -c, or python -c with unusual argument strings
- Outbound network connections initiated by subprocesses of the Chainlit worker to unfamiliar destinations
- Modifications to .chainlit/config.toml or environment variables containing API keys shortly after MCP activity
Detection Strategies
- Inspect HTTP request bodies for /mcp endpoints and flag fullCommand values containing -c, backticks, shell metacharacters, or piped commands
- Baseline expected child processes of the Chainlit runtime and alert on any deviation
- Correlate /mcp request timestamps with process creation and outbound network telemetry to identify command chains
Monitoring Recommendations
- Enable verbose application logging for the Chainlit MCP subsystem and forward logs to a centralized SIEM
- Monitor for anomalous process ancestry where the Chainlit Python interpreter spawns interactive shells or package runners
- Alert on any HTTP 200 responses from /mcp originating from non-allowlisted source networks
How to Mitigate CVE-2026-45018
Immediate Actions Required
- Upgrade Chainlit to version 2.12.0 or later across all deployments
- If upgrade is not immediately possible, set features.mcp.enabled = false in .chainlit/config.toml to disable the vulnerable endpoint
- Rotate any credentials, API keys, or tokens accessible to the Chainlit process, as they may have been exposed
- Audit HTTP logs for prior POST /mcp requests to determine whether exploitation occurred
Patch Information
The vulnerability is remediated in Chainlit 2.12.0. Review the GitHub Release Version 2.12.0, the GitHub Commit Changes, and the GitHub Security Advisory SPL-2026-001 for full remediation details.
Workarounds
- Disable the MCP feature entirely by setting features.mcp.enabled = false until patching is complete
- Restrict allowed_executables to a minimal explicit list and remove any binary that supports inline command evaluation such as npx, bash, sh, or python
- Place Chainlit behind an authenticating reverse proxy and deny external access to /mcp
- Run the Chainlit process under a low-privilege service account with no access to sensitive credentials or lateral network paths
# Configuration example: disable MCP in .chainlit/config.toml
[features.mcp]
enabled = false
# If MCP must remain enabled, restrict allowed executables explicitly
[features.mcp.stdio]
allowed_executables = ["my-safe-mcp-binary"]
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

