Skip to content

ReDoS via typesMap.json regex injection in loadTypesMap() #63719

Description

@bolverk

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions