DEV Community

correctover
correctover

Posted on

Chainlit Fixed a CVSS 9.8 RCE. The Post-Patch Problem Is Worse.

Chainlit Just Fixed a CVSS 9.8 RCE. The Post-Patch Problem Is Worse.

On August 25, Chainlit shipped v2.12.0 fixing CVE-2026-45018: an unauthenticated remote code execution via the MCP stdio transport. CVSS 9.8. Any network-adjacent attacker could POST a crafted JSON payload to the /mcp endpoint, pass npx -y -c '<arbitrary shell>' as a "command", and get code execution with the Chainlit process privileges.

The root cause was almost boring: the validate_mcp_command() function checked the executable name against an allowlist but performed zero validation on arguments. Even with a strict allowlist containing npx, an attacker could use npx -c to execute arbitrary shell. If the allowlist was omitted (the default), any binary on the host was fair game.

There was also a companion SSRF (CVE-2026-45019, CVSS high) via SSE and streamable-http transports.

Chainlit isn't alone. This was the same week that:

  • Azure DevOps MCP Server shipped a confused-deputy bug where hidden instructions in PR comments could exploit agents (disclosed by Manifold Security)
  • mcp-atlassian fixed CVE-2026-27825, an arbitrary file write leading to RCE via unconstrained download_path
  • LangBot fixed CVE-2026-54449, where authenticated users could change MCP server configuration to execute arbitrary commands
  • A DeepInspect audit found 91.8% of audited MCP servers run without OAuth

The patches are good. Upgrade. But the pattern these CVEs expose isn't fixed by patches.

The Post-Patch Problem

Every one of these vulnerabilities follows the same arc:

  1. A scanner or researcher finds the bug
  2. A CVE is assigned
  3. A patch ships
  4. Everyone upgrades (eventually)
  5. Nobody can prove what happened before the patch, or that the system behaves correctly after it

The Chainlit advisory recommends EDR monitoring for "anomalous process lineage where the Chainlit Python process spawns interactive shell interpreters." That's detection. It works by flagging anomalies after they happen. It produces false positives. It requires a human analyst to investigate.

But when your security team asks "did an agent execute an unauthorized tool call between the vulnerability being introduced and the patch being deployed," detection-based logs can't answer that with certainty. The logs were produced by the same runtime that was compromised. A compromised runtime can modify, delete, or fabricate log entries.

This is not a Chainlit-specific problem. It's structural to every AI agent deployment:

  • Application logs are writable by the application. If the application is compromised, the logs are compromised.
  • SIEM forwarding assumes the host producing the logs is trustworthy. A compromised host can forward plausible-but-false events.
  • Audit trails stored in the same database as the runtime data can be silently altered by anyone with database access.

The common phrase is "tamper-evident audit log," but most implementations are tamper-detectable only against external modification after the fact. They don't protect against a compromised runtime producing valid-but-false entries in the first place.

What Cryptographic Evidence Changes

A different approach: every agent tool call produces an Ed25519-signed receipt over RFC 8785 canonical JSON. The signing key lives outside the agent process — in a sidecar that the agent can communicate with but cannot read the key from.

Each receipt binds:

  • The action identity (what tool was called)
  • SHA-256 hashes of canonical input and output
  • Caller identity
  • Timestamp with issued-at/expiry window
  • The hash of the previous receipt, forming a chain

The chain property means you can't silently truncate the log — the head hash won't match. The sidecar key isolation means a compromised agent runtime can't forge receipts for actions that didn't happen, because it can't access the signing key. It can't rehash the chain because it doesn't hold the key.

This is structurally different from detection:

Detection Verification
Mechanism Pattern matching, anomaly scoring Ed25519 signature over canonical JSON
False positives Inherent None — a receipt is either valid or invalid
Compromised runtime Can manipulate logs Cannot forge signatures without the key
Evidence type "This looks anomalous" "This action happened, with these inputs and outputs, signed by a key the runtime can't access"
Performance Varies with ruleset P50 ~27 microseconds end-to-end (Python, including signing)

The sidecar doesn't make the runtime uncompromisable. It doesn't independently verify event semantics — if a compromised orchestrator hands it a fabricated human-approval event, it signs because it can't know the human didn't actually approve it. That's a confused deputy, and the honest boundary is: key isolation plus chain integrity, not dishonest-runtime defense.

What it does guarantee is that after the fact, you have a sequence of receipts that:

  • Can't be silently modified (signature breaks)
  • Can't be silently truncated (chain breaks)
  • Can't be forged by the agent runtime alone (key isn't in the process)

For the post-CVE question — "what did our agents actually do during the exposure window?" — that's the difference between a log entry and evidence.

The Scanner Gap

Tools like SplxAI's agentic-radar (1,000+ stars), SecureAI-Scan, and mcpscan find vulnerabilities before they're exploited. That's necessary and valuable. But scanning is point-in-time. The scan runs, finds issues, you fix them. Between scans, there's no continuous record.

The complementary layer is runtime verification: signed receipts produced on every tool call, independently verifiable, forming a chain that can't be altered by the system being monitored. A scanner tells you what could go wrong. A receipt tells you what did happen, with cryptographic certainty.

We built this as an open-source verifier: ccs-verifier, 157 tests, zero dependencies. The conformance test vectors are MIT-licensed if you're building or evaluating a receipt format and want cross-implementation fixtures.

This isn't a replacement for patching CVEs. Patch CVE-2026-45018 today. But after you patch, the question "are we sure nothing happened during the exposure window" needs evidence the runtime can't produce about itself.


Guigui Wang builds runtime verification for agent systems at Correctover. The ccs-verifier is open source on PyPI.

Top comments (0)