1. Paste your code
2. What it found
| Rule | Level | Line | Detail |
|---|
Prefer a CLI, or want this as a CI gate?
Same engine, zero dependencies — walks a whole source tree, skipping node_modules, .git and similar:
node cli.mjs scan src # human-readable node cli.mjs scan . --json # machine-readable node cli.mjs scan src --probe # time the real JS engine to confirm suspects node cli.mjs scan src --fail-on warn # fail CI on polynomial warnings too node cli.mjs check '(a+)+$' # analyse one pattern
Get cli.mjs, engine.mjs and probe.mjs — Node 18+, nothing to install, no network call. Exits 1 on a likely-exponential pattern (or on any warning with --fail-on warn). More: how ReDoS works and how to find it before an attacker does.
What's recognised
| Language | Where regexes are read from |
|---|
Rules: nested-quantifier, overlapping-alternation (exponential, errors); adjacent-overlap, quadratic-end-anchor (polynomial, warnings); dynamic-regexp (note). Patterns built by string concatenation are not guessed at. Linear-time engines (RE2, Go, Rust) are not affected by any of this.
Common questions
Does this run my regexes or upload my code?
The browser page only parses the text you paste, in your own browser, and never sends it anywhere. It does not execute your patterns. The CLI reads local files and makes no network call; its optional --probe mode runs a pattern against generated strings in a sandboxed Node vm with a hard timeout.
What does a finding actually mean?
It is a suspect, not a proof. The analysis reads the structure of the pattern and finds shapes where a backtracking engine can match the same text in many ways, so a long input that almost matches takes exponential or polynomial time. Whether that is exploitable depends on whether an attacker controls the input and how long it can be. The CLI's --probe mode times the real JavaScript engine on a generated input to confirm.
Which regex engines does this apply to?
Backtracking engines: JavaScript, Python's re, Java, .NET, Ruby (before 3.2's memoisation), PHP's PCRE. Engines built on automata, such as RE2, Go's regexp and Rust's regex crate, run in linear time and are not affected. Patterns are parsed with JavaScript semantics, with Python named groups, inline flags and possessive quantifiers accepted.
Why does it flag \s+$ with no ^?
Without a start anchor the engine retries the pattern from every position. On a string with a long run of spaces that is not at the end, each retry scans the whole run again, which is quadratic. A trim like that was behind a 2016 Stack Overflow outage. Anchor the start, or use String.prototype.trim().