Safeguard
Vulnerability Analysis

CVE-2021-3807: ReDoS in ansi-regex

A ReDoS flaw in the widely-depended-on ansi-regex npm package could hang Node.js processes on crafted input. Here's what's affected and how to fix it.

Vikram Iyer
Security Researcher
7 min read

Few npm packages are as quietly ubiquitous as ansi-regex. It has no marquee use case of its own — it just strips or matches ANSI escape codes so terminal output looks clean — but it sits in the dependency tree of chalk, strip-ansi, wrap-ansi, cli-cursor, and dozens of other packages that, in turn, sit underneath tools like eslint, webpack, jest, and countless CLIs. That reach is exactly what made CVE-2021-3807 worth paying attention to: a Regular Expression Denial of Service (ReDoS) in the regex ansi-regex uses to detect ANSI codes.

The flaw is a classic case of catastrophic backtracking. The vulnerable pattern, when fed a specially crafted string, forces the JavaScript regex engine into exponential-time evaluation instead of the near-instant match developers expect. In a Node.js process — which runs on a single-threaded event loop — that translates directly into a hung process. An attacker who can get a string of their choosing into a code path that calls the vulnerable regex (for example, a value later logged, linted, or pretty-printed) can freeze the application or build step until it's killed and restarted.

Affected versions and components

ansi-regex is the package with the actual defect, but almost nobody depends on it directly — it's pulled in transitively. According to the public advisory, the vulnerable ranges are:

  • ansi-regex before 3.0.1
  • ansi-regex >=4.0.0 and before 4.1.1
  • ansi-regex >=5.0.0 and before 5.0.1

The fix landed in versions 3.0.1, 4.1.1, and 5.0.1. Because ansi-regex is versioned and required independently by each of its dependents, a single project can end up with several vulnerable copies at once through different branches of the dependency graph — one pulled in by chalk, another by wrap-ansi, another by a CLI tool's own direct dependency. That fragmentation is a big part of why this CVE lingered in so many npm audit reports long after a fix existed: bumping the top-level package doesn't guarantee every nested copy gets updated unless lockfiles are regenerated or resolutions/overrides are used.

The practical blast radius is large not because ansi-regex itself is exciting, but because of what consumes it. Build tooling, CI/CD pipelines, linters, test runners, and interactive CLIs across the JavaScript ecosystem carry this dependency somewhere in their tree, which is why this CVE shows up repeatedly in software composition analysis (SCA) scans of Node.js projects, even in codebases that never call ansi-regex directly.

CVSS, EPSS, and KEV context

The National Vulnerability Database scored this issue as a High severity Denial of Service, consistent with the CVSS v3.1 vector for a network-reachable, low-complexity, no-privileges-required flaw whose worst outcome is availability loss (the process hangs) rather than any confidentiality or integrity impact. That profile — high severity on paper, but limited to availability and dependent on an attacker controlling input that reaches the regex — is typical of ReDoS findings across the npm ecosystem.

CVE-2021-3807 has not been added to CISA's Known Exploited Vulnerabilities (KEV) catalog, and there is no public evidence of widespread active exploitation. Its exploit likelihood (as reflected in EPSS-style scoring) is consistent with most library-level ReDoS bugs: real and worth fixing, but not the kind of vulnerability driving mass internet scanning campaigns, since it typically requires an attacker to control a specific input string that then flows into a vulnerable code path — a condition that varies a lot depending on how the consuming application is built.

That combination — high CVSS, no KEV listing, modest real-world exploitation signal — is exactly the kind of nuance that raw severity scores miss, and exactly why remediation prioritization should never stop at "CVSS is High, drop everything."

Timeline

  • Root cause: an inefficient regular expression in ansi-regex used to detect ANSI escape sequences, susceptible to catastrophic backtracking on adversarial input.
  • Disclosure: the issue was reported through coordinated disclosure channels and published as a GitHub Security Advisory (GHSA-93q8-gq69-wqmw) alongside the CVE assignment in 2021.
  • Fix: maintainers patched the regex in ansi-regex 3.0.1, 4.1.1, and 5.0.1, released in 2021 to cover each actively used major version line.
  • Downstream propagation: packages depending on vulnerable ansi-regex ranges (chalk, strip-ansi, wrap-ansi, and others) subsequently bumped their own dependency constraints, but adoption of those updates across the ecosystem happened unevenly and over an extended period, which is why the CVE continued to surface in dependency audits well after a fix was available.

Remediation steps

  1. Identify every copy in the tree. Run npm ls ansi-regex or yarn why ansi-regex to find all resolved versions, including nested ones pulled in by chalk, wrap-ansi, string-width, cli-cursor, and similar packages.
  2. Upgrade to a patched version. Ensure resolution to 3.0.1, 4.1.1, 5.0.1, or later within each major line. If direct dependencies pin older, vulnerable ranges, upgrade those parent packages first.
  3. Force resolution where needed. If a transitive dependency won't update on its own, use overrides (npm) or resolutions (Yarn) to pin ansi-regex to a safe version across the whole tree, then regenerate the lockfile.
  4. Regenerate and commit lockfiles. A package.json range fix is meaningless if package-lock.json or yarn.lock still resolves to a vulnerable version. Always verify the resolved version, not just the declared range.
  5. Re-scan. Run npm audit, yarn audit, or your SCA tool of choice after the change to confirm no vulnerable resolution remains anywhere in the graph, including devDependencies used only in CI.
  6. Treat build and CI systems as in-scope. Because much of ansi-regex's exposure comes through build tooling and linters rather than production runtime code, don't limit remediation to dependenciesdevDependencies in CI pipelines are a legitimate attack surface if they process untrusted strings (e.g., PR titles, commit messages, or user-supplied file content).

How Safeguard Helps

CVE-2021-3807 is a good illustration of why modern software supply chain security can't stop at flat dependency lists. The vulnerable package is almost never a direct dependency — it's buried two or three levels deep, duplicated across multiple resolution paths, and easy to miss with a manual review or a one-time audit.

Safeguard is built for exactly this shape of problem:

  • Full-depth SBOM and dependency graph visibility — Safeguard maps transitive dependencies, not just top-level package.json entries, so every resolved copy of ansi-regex across every consuming package is visible in one place, instead of requiring engineers to manually trace chalk → ansi-regex chains project by project.
  • Continuous monitoring, not point-in-time scans — new advisories against packages already in your SBOM are matched automatically as they're published, so a fix that ships years after initial adoption (as happened with several ansi-regex-dependent packages) is still caught.
  • Context-aware prioritization — Safeguard surfaces CVSS alongside exploitability signals like EPSS and KEV status, so teams can tell the difference between a "High severity, low real-world exploitation" finding like this one and a vulnerability that demands emergency response, instead of treating every High-severity CVE identically.
  • Policy enforcement in CI/CD — Safeguard can gate builds and pull requests on the presence of vulnerable dependency versions, catching a reintroduced vulnerable ansi-regex resolution (via a stale lockfile, a new dependency, or a dependency downgrade) before it merges.
  • Remediation guidance mapped to your actual graph — rather than a generic "upgrade ansi-regex" instruction, Safeguard identifies which of your direct dependencies need to be bumped to pull in the patched transitive version, cutting down the trial-and-error of manually testing overrides and resolutions.

ReDoS bugs like CVE-2021-3807 rarely make headlines, but they're a recurring pattern in the JavaScript ecosystem precisely because small, deeply-nested utility packages get reused everywhere. Treating them seriously — with full dependency visibility, continuous rescanning, and risk-based prioritization — is what keeps a "High severity" CVE from becoming either a false alarm that wastes engineering time or a genuine incident that slips through because it was buried three layers deep in a lockfile.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.