Description
The TypeScript language server's loadTypesMap() function reads a typesMap.json file and passes the match field directly to new RegExp() without sanitization:
// editorServices.ts:1571-1574
const raw: TypesMapFile = JSON.parse(fileContent);
// Parse the regexps
for (const k of Object.keys(raw.typesMap)) {
raw.typesMap[k].match = new RegExp(raw.typesMap[k].match as {} as string, "i");
}
A malicious typesMap.json file with a crafted regex pattern in the match field can cause ReDoS (Regular Expression Denial of Service), hanging the TypeScript language server when it processes type definitions.
Repro
Create a malicious typesMap.json with a ReDoS pattern:
{
"typesMap": {
"malicious": {
"match": "^(a+)+$"
}
},
"simpleMap": {}
}
When the TypeScript language server loads this file (via loadTypesMap() at line 1564), it calls new RegExp("^(a+)+$", "i"). The resulting regex has nested quantifiers (a+)+ which cause catastrophic backtracking when tested against strings like "aaaaaaaaaaaaaaaaaaaaaaaaaaa!".
Impact
- Denial of Service: The TypeScript language server hangs indefinitely when processing type definitions that match the malicious regex pattern
- IDE Freeze: VS Code and other editors using tsserver become unresponsive
- CPU Exhaustion: The regex engine consumes 100% CPU during backtracking
Attack Vector
An attacker who can modify the typesMap.json file (e.g., via supply chain attack on a TypeScript package, shared development environment, or compromised CI/CD pipeline) can inject a ReDoS pattern. The default typesMap.json is located alongside the TypeScript installation.
Suggested Fix
Sanitize the regex pattern before passing it to new RegExp():
// Option 1: Use a safe regex library
import safeRegex from 'safe-regex';
for (const k of Object.keys(raw.typesMap)) {
const pattern = raw.typesMap[k].match as string;
if (safeRegex(pattern)) {
raw.typesMap[k].match = new RegExp(pattern, "i");
} else {
// Skip or use a safe default pattern
raw.typesMap[k].match = new RegExp("^$", "i");
}
}
// Option 2: Validate pattern complexity
for (const k of Object.keys(raw.typesMap)) {
const pattern = raw.typesMap[k].match as string;
if (pattern.length > 1000 || (pattern.match(/\(/g) || []).length > 10) {
// Pattern too complex, skip
continue;
}
raw.typesMap[k].match = new RegExp(pattern, "i");
}
Related
- This is a separate issue from the
__proto__ prototype pollution in tsconfig parsing (also reported)
- The
typesMap.json file is part of TypeScript's @types auto-installation system
Affected Versions
All TypeScript versions that use typesMap.json (TypeScript 2.x through 6.0)
Description
The TypeScript language server's
loadTypesMap()function reads atypesMap.jsonfile and passes thematchfield directly tonew RegExp()without sanitization:A malicious
typesMap.jsonfile with a crafted regex pattern in thematchfield can cause ReDoS (Regular Expression Denial of Service), hanging the TypeScript language server when it processes type definitions.Repro
Create a malicious
typesMap.jsonwith a ReDoS pattern:{ "typesMap": { "malicious": { "match": "^(a+)+$" } }, "simpleMap": {} }When the TypeScript language server loads this file (via
loadTypesMap()at line 1564), it callsnew RegExp("^(a+)+$", "i"). The resulting regex has nested quantifiers(a+)+which cause catastrophic backtracking when tested against strings like"aaaaaaaaaaaaaaaaaaaaaaaaaaa!".Impact
Attack Vector
An attacker who can modify the
typesMap.jsonfile (e.g., via supply chain attack on a TypeScript package, shared development environment, or compromised CI/CD pipeline) can inject a ReDoS pattern. The defaulttypesMap.jsonis located alongside the TypeScript installation.Suggested Fix
Sanitize the regex pattern before passing it to
new RegExp():Related
__proto__prototype pollution in tsconfig parsing (also reported)typesMap.jsonfile is part of TypeScript's @types auto-installation systemAffected Versions
All TypeScript versions that use
typesMap.json(TypeScript 2.x through 6.0)