From grimoire
Detects and prevents catastrophic regex backtracking (ReDoS) by identifying vulnerable patterns with nested quantifiers or overlapping alternation and rewriting them with atomic groups or possessive quantifiers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:prevent-redosThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Eliminate catastrophic regex backtracking by using atomic groups, possessive quantifiers, or linear-time regex engines — preventing single-user input from consuming CPU for minutes or hours.
Eliminate catastrophic regex backtracking by using atomic groups, possessive quantifiers, or linear-time regex engines — preventing single-user input from consuming CPU for minutes or hours.
Adopted by: CWE-1333 (Inefficient Regular Expression Complexity) is a named weakness tracked by NVD with hundreds of CVEs. Node.js, Cloudflare, and Stack Overflow have all experienced ReDoS outages. OWASP Top 10 2021 A06 (Vulnerable and Outdated Components) includes ReDoS in third-party regex libraries. Google's re2 library (used in Go's stdlib regex) is designed to guarantee linear-time matching. GitHub's CodeQL, Semgrep, and Snyk all include ReDoS detection rules.
Impact: Stack Overflow's 2016 outage was caused by a single malicious regex ^\s*(\w+\s*)+$ consuming 100% CPU on a crafted input string — taking down the site for 34 minutes. Cloudflare's 2019 global outage lasted 27 minutes due to a ReDoS in a WAF regex. Node.js has had multiple ReDoS CVEs in the validator, marked, and moment packages. A single well-crafted string can freeze a single-threaded server.
Why best: Input length limits alone are insufficient — catastrophic backtracking is exponential, so even 30-character inputs can cause seconds-long hangs. Rewriting to atomic/possessive patterns or using a linear-time engine eliminates the root cause.
Sources: OWASP ReDoS Cheat Sheet; Stack Overflow outage post-mortem (2016); Cloudflare outage post-mortem (2019); CWE-1333
Identify catastrophically vulnerable regex patterns — look for:
(a+)+, (a*)*, ([ab]+)+(a|aa)+(a+b?)+# VULNERABLE — exponential backtracking on "aaaaaaaaaaaaaaaax"
import re
pattern = re.compile(r'^(\w+\s*)+$') # nested quantifier
pattern = re.compile(r'^(a+)+$') # classic evil regex
pattern = re.compile(r'^([a-zA-Z]+)*$') # same structure
# Test: time how long this takes (should be microseconds, not seconds)
import time
start = time.time()
re.match(r'^(\w+\s*)+$', 'a' * 30 + '!')
print(f"{time.time() - start:.3f}s") # > 1s = vulnerable
Use atomic groups or possessive quantifiers to prevent backtracking:
# Python 3.11+ supports atomic groups (?>...) and possessive quantifiers
import re
# VULNERABLE
pattern = re.compile(r'^(\w+\s*)+$')
# FIXED — atomic group prevents backtracking into the group
pattern = re.compile(r'^(?>\w+\s*)+$') # Python 3.11+
# Alternative for older Python — use regex module
import regex
pattern = regex.compile(r'^(\w+\s*)++$') # possessive quantifier ++
Use re2 or linear-time engines for user-facing regex:
# Python — google-re2 (linear time guarantee)
pip install google-re2
import re2
pattern = re2.compile(r'^\w+(\.\w+)*$')
match = pattern.match(user_input)
# Note: re2 does not support backreferences or lookaheads
// Node.js — use 're2' package
const RE2 = require('re2');
const pattern = new RE2('^\\w+(\\.\\w+)*$');
pattern.test(userInput); // linear time, no catastrophic backtracking
Set timeouts on regex execution for patterns that cannot be rewritten:
import signal
class RegexTimeout(Exception):
pass
def safe_match(pattern, text, timeout_ms=100):
def handler(signum, frame):
raise RegexTimeout("Regex timed out")
signal.signal(signal.SIGALRM, handler)
signal.setitimer(signal.ITIMER_REAL, timeout_ms / 1000)
try:
result = pattern.match(text)
signal.setitimer(signal.ITIMER_REAL, 0)
return result
except RegexTimeout:
return None
Note: signal.SIGALRM is POSIX-only (not Windows).
Limit input length before applying regex:
MAX_INPUT_LEN = 1000 # adjust per expected input
def validate_input(user_input, pattern):
if len(user_input) > MAX_INPUT_LEN:
raise ValueError("Input too long")
return pattern.match(user_input)
Length limits reduce the window of catastrophic backtracking but don't eliminate it — pair with pattern fixing.
Use static analysis to detect vulnerable patterns at CI time:
# Semgrep ReDoS rules
semgrep --config p/redos .
# vuln-regex-detector (Node.js)
npx vuln-regex-detector check '(a+)+'
# safe-regex (npm)
npx safe-regex '(\w+\s*)+' # exits non-zero if unsafe
re2 does not support backreferences (\1), lookaheads, or lookbehinds — evaluate compatibility before switching.++, *+, ?+) in PCRE/Python regex module prevent backtracking into that group — they are safe but also change match semantics.(abc|abd) — alternation with overlapping suffixes is dangerous — (a|aa)+.validator, path-to-regexp, and moment have had ReDoS CVEs. Check package.json deps with npx auditjs.\S+@\S+\.\S+ or a dedicated library.'a' * 30 + '!' style inputs.npx claudepluginhub jeffreytse/grimoire --plugin grimoireDetects regex patterns vulnerable to catastrophic backtracking (ReDoS) in user-input processing, configuration, or validation. Flags nested quantifiers and overlapping alternations, suggests fixes.
Detects ReDoS vulnerabilities via catastrophic backtracking in regex patterns on user-controlled input. Audits JS/TS/Python/Ruby/PHP/Java code and measures growth rates.
DEPRECATED: No longer provides meaningful uplift over base model regex capabilities. Retained for reference only.