fix: prevent ReDoS vulnerability in UriTemplate regex patterns (#1363)

Co-authored-by: Paul Carleton <paulc@anthropic.com>
This commit is contained in:
Janderson Lira 2026-01-07 08:17:21 -04:00 • committed by GitHub
commit 0a75810b26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 35 additions and 2 deletions

View file

@ -0,0 +1,6 @@
---
'@modelcontextprotocol/core': patch
'@modelcontextprotocol/server': patch
---
Fix ReDoS vulnerability in UriTemplate regex patterns (CVE-2026-0621)

View file

@ -225,7 +225,7 @@ export class UriTemplate {
switch (part.operator) {
case '':
pattern = part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)';
pattern = part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)';
break;
case '+':
case '#':
@ -235,7 +235,7 @@ export class UriTemplate {
pattern = '\\.([^/,]+)';
break;
case '/':
pattern = '/' + (part.exploded ? '([^/]+(?:,[^/]+)*)' : '([^/,]+)');
pattern = '/' + (part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)');
break;
default:
pattern = '([^/]+)';

View file

@ -284,5 +284,32 @@ describe('UriTemplate', () => {
vars[longName] = 'value';
expect(() => template.expand(vars)).not.toThrow();
});
it('should not be vulnerable to ReDoS with exploded path patterns', () => {
// Test for ReDoS vulnerability (CVE-2026-0621)
// See: https://github.com/modelcontextprotocol/typescript-sdk/issues/965
const template = new UriTemplate('{/id*}');
const maliciousPayload = '/' + ','.repeat(50);
const startTime = Date.now();
template.match(maliciousPayload);
const elapsed = Date.now() - startTime;
// Should complete in under 100ms, not hang for seconds
expect(elapsed).toBeLessThan(100);
});
it('should not be vulnerable to ReDoS with exploded simple patterns', () => {
// Test for ReDoS vulnerability with simple exploded operator
const template = new UriTemplate('{id*}');
const maliciousPayload = ','.repeat(50);
const startTime = Date.now();
template.match(maliciousPayload);
const elapsed = Date.now() - startTime;
// Should complete in under 100ms, not hang for seconds
expect(elapsed).toBeLessThan(100);
});
});
});