mediumConfiguration

Regular Expression That Can Run Forever

A regular expression can take exponential time on a crafted input, or is built from input the caller controls.

Why it matters

Backtracking regex engines try every way to match. When a repeated group contains another repeat, the number of ways grows with the length of the input, and a string of a few dozen characters that almost matches keeps the process busy for a long time. One request is enough to take a server down. Rewriting the pattern so each character can only be consumed one way keeps the check and removes the cost.

What it looks like

This is the shape of code that triggers the rule. AI tools produce it because it works, and nothing tells them it is unsafe.

Nested quantifier in an email check
const EMAIL_RE = /^([a-zA-Z0-9_.+-]+)+@(\w+)+\.com$/;

The smallest fix

minimal patch
// Before: nested repeats
const EMAIL = /^([a-zA-Z0-9_.+-]+)+@(\w+\.)+\w+$/;
// After: one way to match each character, and a length cap
const EMAIL = /^[a-zA-Z0-9_.+-]{1,64}@[a-zA-Z0-9-]{1,63}(?:\.[a-zA-Z0-9-]{1,63})+$/;
if (input.length > 254) return false;

The better fix

If you have a few more minutes, this is the approach that holds up as the app grows.

safe alternative
// Use a maintained validator instead of a hand-written pattern:
import isEmail from 'validator/lib/isEmail';
// For patterns that must stay hand-written, run them through the
// "recheck" or "safe-regex" package in a test, and never build a RegExp
// from request data without escaping it (lodash.escapeRegExp).

Let your AI tool fix it

When the scanner finds this in your project, it fills in the file and line for you. This is the prompt it gives you to paste into Claude Code, Cursor or whatever you use.

In [the file] at line [the line number] a regular expression is vulnerable to catastrophic backtracking or is built from caller input: [the flagged code]. Rewrite it so no repeated group contains another unbounded repeat, add a length limit on the input before matching, or replace it with a validator library. If the pattern comes from the request, escape it with escapeRegExp or use a fixed allow-list of patterns.

How to check the fix worked

1. Time the regex against 30 characters that almost match (for example
   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa!"); it must return in under a millisecond.
2. Confirm valid inputs still match and invalid ones still fail.

Further reading