Skip to main content
interlace
Plugin: secure-codingRules

no-redos-vulnerable-regex

ESLint Rule: no-redos-vulnerable-regex

CWE: CWE-693
OWASP Mobile: OWASP Mobile Top 10

ESLint Rule: no-redos-vulnerable-regex. This rule is part of eslint-plugin-secure-coding.

Quick Summary

AspectDetails
SeverityError (Security)
Auto-Fix❌ No (requires manual review)
CategorySecurity
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForProduction applications handling user input
Suggestions❌ None — the message names the ambiguity and the fix

Vulnerability and Risk

Vulnerability: Regular Expression Denial of Service (ReDoS) occurs when a regular expression is crafted in a way that causes catastrophic backtracking when processing certain input strings.

Risk: An attacker can provide a specially crafted input that triggers this catastrophic backtracking, causing the application to consume excessive CPU resources and become unresponsive (Denial of Service).

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

🔒 CWE-400 OWASP:A06 CVSS:7.5 | Uncontrolled Resource Consumption (ReDoS) detected | HIGH
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A06_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-400 OWASP:A06 CVSS:7.5
Issue DescriptionSpecific vulnerabilityUncontrolled Resource Consumption (ReDoS) detected
Severity & ComplianceImpact assessmentHIGH
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Rule Details

This rule detects regular expressions that are vulnerable to Regular Expression Denial of Service (ReDoS). ReDoS occurs when a regex engine takes an exponential amount of time to find a match (or fail to match) for certain inputs, usually due to "catastrophic backtracking".

Catastrophic backtracking happens when a regex contains:

  1. Nested Quantifiers: e.g., (a+)+
  2. Overlapping Disjunctions: e.g., (a|a)+
  3. Ambiguous Repetitions: e.g., (.*?)*

When these patterns are applied to a long string that almost matches but fails at the end, the regex engine tries every possible combination of repetitions, leading to $O(2^n)$ runtime.

Why This Matters

IssueImpactSolution
🔒 SecurityDenial of Service (DoS) attacksUse atomic groups or simple patterns
PerformanceServer freezing, high CPU usageValidate input length, use timeouts (if available)
🐛 ReliabilityUnexpected application crashesavoid nested quantifiers (a+)+

Configuration

This rule accepts an options object:

{
  "rules": {
    "secure-coding/no-redos-vulnerable-regex": ["error", {
      // Deprecated and ignored; gated the removed heuristic layer.
      "allowCommonPatterns": false,
      "maxPatternLength": 500       // Default: 500. Maximum length of regex string to analyze.
    }]
  }
}

Examples

❌ Incorrect

// Nested quantifiers - O(2^n)
const badRegex1 = /(a+)+/;
const badRegex2 = /([a-zA-Z]+)*$/;

// Overlapping disjunctions
const badRegex3 = /(a|a)+/;

// Ambiguous optional repetitions
const badRegex4 = /(.*)*$/;

// Usage in RegExp constructor
const userPattern = new RegExp('(a+)+');

✅ Correct

// Non-nested quantifiers
const goodRegex1 = /a+/;
const goodRegex2 = /[a-zA-Z]+$/;

// Atomic groups (simulated in JS using lookahead)
// (?=(a+))\1 matches 'a+' atomically (no backtracking into it)
const atomicRegex = /(?=(a+))\1/;

// Using a safe library like validator.js for email/URL instead of custom regex
import isEmail from 'validator/lib/isEmail';
const valid = isEmail(input);

How detection works

Every pattern goes to scslre, the NFA analyser eslint-plugin-regexp uses. It builds the automaton and looks for genuine ambiguity, and its verdict is final:

  • Reports — a self-loop or cross-quantifier trade was found. The message names which, and whether backtracking is exponential or polynomial.
  • Clean — analysed and not vulnerable. Nothing else gets to overrule this.
  • Unparseable — the pattern is not a regex at all. new RegExp("(a+") throws at construction and can never backtrack, so it is not reported here. It is a real bug, but a different one.

There is deliberately no pattern-matching fallback. An earlier version kept a table of regexes matched against the pattern text, which reported every pattern the analyser had already cleared — including /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/, which is anchored at both ends and linear — and reported invalid syntax as CRITICAL ReDoS. Counting quantifier characters is not the same as finding quantifier nesting.

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Values from Variables

Why: Values stored in variables are not traced.

// ❌ NOT DETECTED - Value from variable
const value = userInput;
dangerousOperation(value);

Mitigation: Validate all user inputs.

Wrapper Functions

Why: Custom wrappers not recognized.

// ❌ NOT DETECTED - Wrapper
myWrapper(userInput); // Uses dangerous API internally

Mitigation: Apply rule to wrapper implementations.

Dynamic Invocation

Why: Dynamic calls not analyzed.

// ❌ NOT DETECTED - Dynamic
obj[method](userInput);

Mitigation: Avoid dynamic method invocation.

Further Reading

⚙️ Options

OptionTypeDefaultDescription
allowCommonPatternsbooleanfalseDeprecated and ignored. Gated the removed heuristic layer; still accepted so existing configs load. Removed in the next major.
maxPatternLengthnumber500Maximum pattern length to analyze

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.