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

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions