An independent re-analysis by Goldberg Security Research of a publicly disclosed vulnerability: our breakdown of root cause, exploitation, and defence. Discovery credit belongs to the original researchers (see references).
Why this one matters
cross-spawn is one of the most-depended-upon packages in the npm ecosystem — a
tiny shim that normalises child_process.spawn across platforms, pulled in
transitively by thousands of tools. A denial-of-service here isn't niche; it sits deep in
countless dependency trees where most teams never look.
Root cause
The argument-escaping logic (lib/util/escape.js) uses a regular expression with
nested quantifiers to handle backslashes before quotes. On an input consisting of a long run
of backslashes followed by a specific character, the engine enters catastrophic
backtracking — work grows super-linearly with input length, pinning a CPU core at
100% and hanging the process.
// escape.js — argument escaping subject to catastrophic backtracking
// e.g. an input like "\\\\\\\\…\\\\" forces exponential work
arg = arg.replace(/(\\*)"/g, '$1$1\\"');
arg = '"' + arg.replace(/(\\*)$/, '$1$1') + '"';
Exploitability
Anywhere an attacker-influenced string flows into a cross-spawn call's arguments,
a single crafted value can wedge the event loop. In a server that shells out using a
user-supplied parameter, one request can take a worker offline — cheap, remote, no auth.
Remediation
- Upgrade to 6.0.6 or 7.0.5+ (the fix removes the backtracking path).
- Audit your lockfile — this is almost always a transitive dependency.
- Bound the length of any user input that reaches a spawned command's args.
Takeaway
ReDoS in deep transitive dependencies is the kind of thing that never shows up in a feature review. Lockfile-level SCA scanning is the only realistic way to catch it.