Denial of service through dynamic regular expressions (ReDoS)

Regular expression denial of service (ReDoS)

Description

Letting external input determine a regular expression pattern in TypeScript or JavaScript can allow an attacker to submit a pattern with excessive execution time, causing denial of service.

Potential impact

  • Long occupation of the event loop can delay legitimate requests.
  • High CPU usage may destabilize application instances.

Remediation

  • Prefer reviewed regular expression literals or constants.
  • If dynamic selection is necessary, use an allow-list rather than interpreting user input as pattern syntax.
  • Avoid nested repetition and ambiguous alternatives that can increase execution time.

Examples

Before

typescript
function search(value: string, pattern: string) {
  return new RegExp(pattern).test(value);
}

After

typescript
const allowedPatterns = new Map([
  ["username", /^[a-z0-9_]{3,32}$/i],
  ["ticket", /^TICKET-\d{4,10}$/],
]);

function search(value: string, patternName: string) {
  const pattern = allowedPatterns.get(patternName);
  if (!pattern) {
    throw new Error("Unsupported pattern");
  }
  return pattern.test(value);
}

Explanation:

  • Before: A caller-supplied string is interpreted directly as a regular expression pattern.
  • After: A name selects a reviewed fixed pattern; unknown names are rejected.

References