You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
parse() is quadratic in leading whitespace: 30k-space error message blocks the event loop for ~3s #49
1000 4.2 ms
2000 14.6 ms
4000 57.9 ms
8000 231.3 ms
16000 924.0 ms
Time quadruples for each doubling of the input — clean O(n²).
Cause
Two patterns start with ^\s* followed immediately by a group that also matches whitespace, so a
leading run of spaces can be divided between them in n ways:
src/stack-trace-parser.js:74 — geckoRe: /^\s*(.*?)…/ (. matches a space)
src/stack-trace-parser.js:103 — javaScriptCoreRe: /^\s*(?:([^@]*)…/ ([^@] matches a space)
When the line does not match, the engine has to try every split before failing.
chromeRe, winjsRe and nodeRe are unaffected: each has a literal at after ^\s*, which
anchors the boundary.
Why it is reachable
err.stack embeds the message, and messages can contain attacker-influenced text:
consterr=newError('Invalid value: \n'+' '.repeat(30000));parse(err.stack);// ~3.3 s of blocked event loop
Any service that parses stack traces from errors carrying user-supplied values — a validation
message, a filename, a parsed field — can be stalled by one request. Availability only; nothing is
disclosed or corrupted.
Fix
The leading \s* is redundant, because the group after it already matches whitespace. Removing it
makes the match linear. It was doing one useful thing — keeping leading whitespace out of the
captured methodName — so that is handled where the capture is read instead.
Measured after the change, same inputs: 8000 → 0.16 ms, 16000 → 0.37 ms, 32000 → 0.63 ms, i.e. ~2×
per doubling. The 30k case above drops from ~3.3 s to under a millisecond.
Happy to open a PR — I have one ready. All 35 existing fixtures pass unchanged, plus a timing
regression test that fails on current main.
Activity