Fix a pattern that backtracks
Rewrite the pattern so each input can match only one way: drop the inner repeat of a nested quantifier, make alternatives exclusive and put a required separator between repeats of the same characters. Then cap the input length before matching, and keep the attack string as a regression test.
// Before: nested quantifier, exponential on "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa!"
const slug = /^([a-z0-9]+-?)*$/;
// After: each character is consumed one way, and the length is capped first
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const isSlug = value => value.length <= 64 && SLUG.test(value);Where users supply the patterns, or the pattern must stay ambiguous, use a linear-time engine such as RE2 (through a binding such as the re2 package for Node.js) or .NET's NonBacktracking option. The analysis here is a heuristic: it can miss shapes that only some inputs exercise and flag shapes that your engine optimizes, which is why the probe times the pattern in this browser's engine.