Rewrites a JavaScript regular expression that can hang on a crafted input (catastrophic backtracking, ReDoS) into one that gives exactly the same yes or no for every text and runs in linear time, answered as JSON with the pattern and flags. Knows the shapes that explode - a quantified group whose inside is quantified too, alternatives under a star that can match the same text, an optional separator between repeats, a counted group of runs, an unanchored run before a literal - and how to remove the ambiguity without atomic groups, which JavaScript lacks. Keeps the flags, keeps an already linear pattern as it is, and answers with a CANNOT line when no regular expression can give the same answers, such as a backreference that compares two arbitrary substrings. Use when a regex in a validator or a search times out, when a security scan reports ReDoS or catastrophic backtracking, or when asked to make a regex safe for untrusted input.
ReDoS Regex Rewrite: Same Answers, Linear Time is a tested SKILL.md that rewrites a JavaScript regular expression that can hang on a crafted input (catastrophic backtracking, ReDoS) into one that gives exactly the same yes or no for every text and runs in linear time, answered as JSON with the pattern and flags; an agent buys it once for $0.02 over x402.
Not for
Writing a new regular expression from examples, changing what a pattern accepts, other regex engines than the JavaScript one in Node and the browsers, or proving a pattern safe for every engine. It rewrites one pattern used as a yes or no test and keeps its flags; matched text and capture groups are not preserved unless a backreference needs them.
Tested, honestly
Tested 2026-10-09 with a strong and a weak model.
With and without the skill
Results with and without the skill, for Sonnet and Haiku |
| with | without | with | without |
|---|
| Patterns handled right (22 patterns) |
| Patterns handled right (22 patterns) | 22/22 | 21/22 | 22/22 | 21/22 |
|---|
Same request on both sides; it states the JSON shape and asks for a CANNOT line when no linear pattern gives the same answers. Without the skill both models already rewrote all thirteen hanging patterns correctly and kept the safe ones. Each missed one refusal: for a pattern whose first word must equal its last word they returned another pattern with the same backreference, which still is not linear.
Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.
- SonnetStrong model, claude-sonnet-5-5
- Right on all 22, checked by running every answer: the same yes or no as the original on a pool of short texts, and each near-miss input finished within 200 ms. It collapsed nested runs, made optional separators mandatory where the set allows, shrank unanchored runs to one character, worked out a glued repeat of digits and decimals, turned a backreference over runs of one letter into an even count, kept the flags, left seven linear patterns as they were or rewrote them to the same set, and refused both patterns whose backreference repeats arbitrary text.
- HaikuWeak model, claude-haiku-5-5
- Right on all 22, checked by running every answer, with the same rewrites, the same seven safe patterns kept and the same two refusals as Sonnet.
Full test summary
Example
Our own test text, before and after the skill ran. Excerpts only.
English · claude-sonnet-5-5
Before
Pattern (source, no slashes): ^(a+)+$
Flags: (none)
After
{"pattern": "^a+$", "flags": ""}
What is in the file
- The answer
- Only the yes or no counts
- The shapes that hang
- How to rewrite
- When the pattern is already safe
- When to refuse
- Work in this order
- Short example
Languages
English. Tried in: English.
License
Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.
Versions
Current version 1.0.0, updated 2026-10-09. Whoever bought an earlier version gets new ones free through the same re-download token.
v1.0.0 · 2026-10-09
First release: rewrites a JavaScript regular expression used with test() that can backtrack catastrophically into one that accepts exactly the same texts in linear time, answered as JSON with the pattern and the unchanged flags; returns an already linear pattern unchanged and answers with a CANNOT REWRITE line when a backreference must repeat arbitrary text. The examples in the skill are not the test patterns. The tests measure the accepted set of each original over a pool of short strings, run every answer on near-miss inputs under a 200 ms limit, and the case script proves each trap really hangs and each control does not. No model run yet: the baseline, the final price and the sentence on what Sonnet gains are still to be written. Price 40000 is provisional.
FAQ
How can a short pattern hang a whole server?
The engine in Node and the browsers backtracks: when a match fails at the end, it goes back and tries every other way the pattern could have split the text. A group of runs inside another run, two alternatives that start alike, or an optional separator between repeats gives a forty-character string billions of splits. The input that triggers it is usually a near miss, such as letters followed by one wrong character.
Why not just add a lazy quantifier or an atomic group?
JavaScript has no atomic groups and no possessive quantifiers, so those patterns do not compile. A lazy quantifier only changes the order in which the splits are tried, not how many there are, so the near miss still hangs. The fix is a pattern in which every character can be consumed in one way only, written from the set of texts the original accepts, with its edges checked.
When does it refuse to rewrite?
When a backreference has to repeat an arbitrary earlier piece of text, such as the same word twice or the same number on both sides of a dash, no regular expression can describe that set, so no rewrite can give the same answers quickly. You get one CANNOT REWRITE line with the reason. A request to change what the pattern accepts is refused too, because that is a new regex, not a safe copy.
Does it help Claude Sonnet?
Only a little, so two cents. Both Claude models rewrote twenty-two patterns, file loaded and not, and every answer was run: same yes or no as the original on thousands of short texts, and no near-miss input above 200 ms. Plain Sonnet already fixed all thirteen hanging patterns. It missed one refusal: a pattern whose first word must equal its last got another backreference pattern back, still not linear. With the file it refused it. Haiku also went from 21 to 22.