A single crafted string, dropped into a command that gets passed through cross-spawn, is enough to pin a Node.js process at 100% CPU and freeze its event loop. That's the practical impact of CVE-2024-21538, a Regular Expression Denial of Service (ReDoS) vulnerability in cross-spawn, the widely-used npm package for spawning child processes cross-platform. Because cross-spawn sits as a transitive dependency underneath tools like execa, npm-run-all, husky, ESLint, and countless CI/CD scripts, the blast radius of this flaw extends far beyond anyone who installed it directly — it touches a meaningful slice of the entire JavaScript build and tooling ecosystem.
What Is CVE-2024-21538?
cross-spawn exists to solve a real headache: spawning child processes on Windows requires quoting and escaping command arguments differently than on POSIX systems, and cross-spawn normalizes that behavior so developers can call spawn() the same way everywhere. To do that normalization, the library inspects each argument with a regular expression to decide whether it contains characters that need escaping or quoting before being handed to the underlying shell.
That inspection regex is the problem. When it's fed a specially constructed string — long runs of whitespace, quote characters, or other pattern-triggering sequences — the regex engine can fall into catastrophic backtracking. Instead of evaluating the string in linear time, the engine's execution time grows exponentially with input length, and a string only a few kilobytes long can occupy the CPU for seconds or minutes. Because Node.js runs JavaScript on a single-threaded event loop, that regex evaluation blocks everything else the process is doing: HTTP requests stall, health checks stop responding, and worker queues back up.
This is classic CWE-1333 (Inefficient Regular Expression Complexity), and it's a denial-of-service issue rather than a memory-corruption or code-execution one. There's no data exfiltration or privilege escalation risk here — the danger is availability. But availability matters: a build pipeline, deployment script, or backend service that hangs indefinitely because of one malicious argument is a real operational and security incident, especially if the affected process is on a critical path (CI runners, webhook handlers, or anything that shells out to a subprocess using attacker-influenced input).
Affected Versions and Components
- Package:
cross-spawn(npm) - Vulnerable versions: versions prior to
7.0.5in the 7.x line, and prior to6.0.6in the legacy 6.x line - Patched versions:
7.0.5and6.0.6(and later releases) - Advisory: GHSA-3xgq-45jj-v275
cross-spawn is not typically something teams install by choice — it arrives as a nested dependency. It is pulled in by execa, which itself underpins a large swath of the npm and Node.js tooling landscape (test runners, linters, git hooks, monorepo task runners, and CLI scaffolding tools). That means an application can be exposed to CVE-2024-21538 without cross-spawn ever appearing in the top-level package.json, and without any developer realizing it's part of the effective dependency graph until they run a full transitive audit.
The exploitability of this bug depends entirely on context: is user-controlled or externally sourced input ever passed as a command or argument into a function that calls cross-spawn under the hood? In many applications the answer is no — cross-spawn is invoked only with developer-controlled, hardcoded strings, in which case the vulnerability is present in the dependency tree but not practically reachable by an attacker. In others — anything that constructs shell commands from user input, file names, webhook payloads, or third-party data — the risk is concrete.
Severity: CVSS, EPSS, and KEV Context
CVE-2024-21538 carries a CVSS v3.1 base score of 7.5 (High), reflecting a network-exploitable, no-privileges-required, no-user-interaction vulnerability whose sole impact is availability (denial of service). The high score is driven by ease of exploitation, not breadth of impact — there's no confidentiality or integrity loss, and no lateral movement path from this bug alone.
From an EPSS (Exploit Prediction Scoring System) standpoint, published probability estimates for this CVE have sat in the low range, consistent with a DoS-only primitive that requires a fairly specific injection point (attacker-influenced input flowing into a cross-spawn call) rather than being remotely triggerable against any exposed service by default. As of this writing, CVE-2024-21538 does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog, and no public reporting has surfaced evidence of active, widespread exploitation in the wild.
That combination — high CVSS, low observed exploitation, no KEV listing — is exactly the kind of nuance that a raw severity score alone won't tell you. A vulnerability scanner that only reports "cross-spawn — CVE-2024-21538 — High" gives security teams no way to distinguish "this is being actively abused against us" from "this exists somewhere deep in our dependency tree but nothing in our code path ever reaches the vulnerable function." Prioritization requires knowing which of those two worlds you're in.
Disclosure Timeline
- Vulnerability introduced: present across multiple historical
cross-spawnreleases in the 6.x and 7.x lines, tied to the argument-escaping regex logic. - Fix shipped: the maintainers patched the regex and released
cross-spawn@7.0.5(with a corresponding fix backported to6.0.6), replacing the vulnerable pattern with a safer, linear-time implementation. - Public advisory: the issue was formalized as GHSA-3xgq-45jj-v275 and assigned CVE-2024-21538, entering NVD and other vulnerability databases in early 2024.
- Ecosystem propagation: because
cross-spawnis deeply nested underexecaand dozens of other packages, the patched version took time to propagate through downstream lockfiles — a pattern typical of high-fan-out dependencies, where the fix ships quickly but adoption lags until downstream maintainers bump their pinned ranges and consumers regenerate lockfiles.
This lag between "patch available" and "patch actually installed across the fleet" is where most of the real-world risk from CVE-2024-21538 lives today. The vulnerability itself is old news to the maintainers; the exposure that persists is almost entirely a function of stale lockfiles and un-refreshed transitive dependency trees sitting in production and CI environments.
Remediation Steps
- Identify exposure. Run
npm ls cross-spawnoryarn why cross-spawn(or the equivalent for pnpm) across every service, monorepo package, and CI configuration to find every nested copy and its resolved version. Don't stop at the top-level manifest — this bug hides in transitive dependencies. - Upgrade directly where possible. If
cross-spawnis a direct dependency, bump it to^7.0.5(or^6.0.6if you're pinned to the 6.x line) and regenerate your lockfile. - Force resolution for transitive copies. Where
cross-spawnis pulled in indirectly throughexecaor another package that hasn't bumped its own dependency range, useoverrides(npm),resolutions(yarn), orpnpm.overridesto force the patched version across the entire dependency graph without waiting on every upstream maintainer. - Re-run
npm audit/yarn auditafter the change to confirm the advisory no longer surfaces, and regenerate or update your SBOM so the fixed version is reflected in your compliance and inventory records. - Check for reachability, not just presence. Before treating this as an emergency patch, determine whether any code path in your application actually passes externally-influenced strings into a function that calls
cross-spawn. If it's confined to internal build tooling with no attacker-controlled input, it still warrants patching on your normal update cadence — but it shouldn't jump the queue ahead of vulnerabilities that are actually exploitable in your environment. - Add a regression check. If your CI pipeline enforces minimum dependency versions or runs
npm audit --audit-level=highas a gate, make surecross-spawnis covered so a future downgrade or a re-introduced vulnerable transitive dependency doesn't slip back in unnoticed.
How Safeguard Helps
CVE-2024-21538 is a textbook case for why severity scores alone don't drive good remediation decisions — the difference between "patch today" and "patch on the normal cycle" comes down to whether attacker-influenced data can actually reach the vulnerable cross-spawn code path. Safeguard's reachability analysis traces your application's real call graph to determine whether that path exists, so teams stop firefighting theoretical exposure buried six dependencies deep. Griffin AI, Safeguard's reasoning engine, correlates the CVE, the affected function, your SBOM, and your CVSS/EPSS/KEV context into a single prioritized signal instead of a flat severity list. Safeguard's SBOM generation and ingest pipeline continuously enumerates every nested instance of cross-spawn across your services and monorepos — including the copies hiding under execa — so you're never relying on a point-in-time audit. And when a fix is warranted, Safeguard opens an auto-fix pull request that bumps or overrides the dependency and regenerates the lockfile, turning a multi-step manual remediation into a single review-and-merge action.