Back to Blog
medium SEVERITY7 min read

How Denial of Service via Catastrophic Backtracking happens in Node.js and how to fix it

CVE-2026-4867 is a Regular Expression Denial of Service (ReDoS) vulnerability in the `path-to-regexp` package (versions prior to 0.1.13) that allows an attacker to craft malformed URL parameters that cause catastrophic backtracking in the regex engine, effectively hanging the Node.js event loop. The fix upgrades `path-to-regexp` from 0.1.12 to 0.1.13 and pins the version via an `overrides` field in `package.json` to ensure the patched version is used throughout the entire dependency tree. Any Ex

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

CVE-2026-4867 is a Regular Expression Denial of Service (ReDoS) vulnerability (CWE-1333) in the `path-to-regexp` npm package versions before 0.1.13. When a Node.js application uses `path-to-regexp` to match URL routes, a specially crafted malformed URL parameter can trigger catastrophic backtracking in the underlying regular expression engine, causing the event loop to stall and effectively denying service to all other users. The fix is to upgrade `path-to-regexp` to version 0.1.13 and, if the package is a transitive dependency, add an `overrides` entry in `package.json` to force the patched version across the entire dependency tree.

Vulnerability at a Glance

cweCWE-1333 (Inefficient Regular Expression Complexity)
fixUpgrade path-to-regexp from 0.1.12 to 0.1.13 and pin the version with a package.json `overrides` entry
riskAn unauthenticated attacker can stall the Node.js event loop by sending a single crafted HTTP request, denying service to all legitimate users
languageJavaScript / Node.js
root causeAmbiguous regex patterns in path-to-regexp 0.1.12 allow exponential backtracking when matching malformed URL parameter strings
vulnerabilityRegular Expression Denial of Service (ReDoS)

How Denial of Service via Catastrophic Backtracking Happens in Node.js and How to Fix It

In the BakaMusic plugin subscription service (bakamusic-plugins), a routine dependency audit surfaced a medium-to-high severity vulnerability hiding inside a small but widely used routing helper: path-to-regexp. The Trivy scanner flagged rule CVE-2026-4867 against package-lock.json, pointing directly at version 0.1.12 of the package. A single HTTP request with a malformed URL parameter is all it would take to freeze the Node.js event loop and deny service to every user of the application.

This post walks through exactly what the vulnerability is, how an attacker would exploit it, and the two-file change that closes the door.


The Vulnerability Explained

What is Catastrophic Backtracking?

Regular expression engines work by trying every possible way to match a pattern against an input string. Most of the time this is fast. But certain patterns — particularly those with nested or ambiguous quantifiers — can cause the engine to explore an exponentially growing number of paths when the input doesn't match. This is called catastrophic backtracking, and it is the root cause of ReDoS (Regular Expression Denial of Service) attacks.

path-to-regexp converts URL route templates like /user/:id into regular expressions used to match incoming request paths. In version 0.1.12, the generated regex for certain parameter patterns contained ambiguous constructs. When a crafted string is fed to that regex — for example, a URL parameter containing a long sequence of repeated characters followed by a character that forces a mismatch — the regex engine enters an exponential backtracking loop.

The Vulnerable Dependency

The vulnerable entry in package-lock.json before the fix:

"node_modules/path-to-regexp": {
  "version": "0.1.12",
  "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
  "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
  "license": "MIT"
}

Note that the package was resolved from registry.npmmirror.com (a Chinese npm mirror), which is a secondary concern — the primary issue is the version number 0.1.12.

The Attack Scenario

Consider the bakamusic-plugins service, which exposes HTTP endpoints that match plugin subscription paths. Express (or a similar framework) uses path-to-regexp internally to compile route patterns. When a request arrives, the compiled regex is evaluated against the request URL.

An attacker sends a single HTTP GET request with a URL like:

GET /plugins/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!

The trailing ! is the mismatch character. The regex engine compiled from the route pattern by path-to-regexp 0.1.12 begins backtracking through all possible ways to match the long sequence of a characters, never succeeding, and consuming 100% of one CPU core in the process. Because Node.js runs on a single-threaded event loop, every other request queues up and waits. The server becomes unresponsive for the duration of the backtracking computation — which can be seconds or even minutes depending on input length.

No authentication is required. No special privileges are needed. One request, one stalled server.

Real-World Impact for BakaMusic

The bakamusic-plugins service is described as a plugin subscription service. If route matching is performed on user-supplied plugin identifiers or subscription paths, any unauthenticated user (or an automated scanner) could trigger this condition. The result is a complete denial of service for all subscribers trying to access the platform.


The Fix

The fix involves exactly two files: package-lock.json and package.json.

1. Upgrading the Resolved Version in package-lock.json

 "node_modules/path-to-regexp": {
-  "version": "0.1.12",
-  "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
-  "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+  "version": "0.1.13",
+  "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+  "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
   "license": "MIT"
 }

Two things changed here beyond the version bump:
- The registry URL switched from registry.npmmirror.com back to the official registry.npmjs.org, ensuring the package is sourced from the canonical registry.
- The integrity hash is updated to match the new 0.1.13 tarball, preventing any tampering or substitution.

Version 0.1.13 patches the regex patterns that caused catastrophic backtracking. The fix in the upstream library introduces input validation and rewrites the offending patterns to use possessive quantifiers or atomic groups (depending on the engine), ensuring linear-time matching regardless of input content.

2. Pinning the Version via package.json Overrides

This is the more important change from a long-term security perspective:

 "engines": {
   "node": ">=18"
+},
+"overrides": {
+  "path-to-regexp": "0.1.13"
 }

The overrides field (introduced in npm 8.3) forces all packages in the dependency tree — not just direct dependencies — to use path-to-regexp@0.1.13. This matters because path-to-regexp is typically a transitive dependency (pulled in by Express, for example). Without the override, running npm install after a lockfile deletion could silently reinstall 0.1.12 if a parent package's version range permits it.

Before the fix: The lockfile pinned 0.1.12, but nothing in package.json prevented a future npm install from resolving back to a vulnerable version.

After the fix: The overrides entry acts as a hard floor — npm will always resolve path-to-regexp to 0.1.13 or higher, regardless of what transitive parents request.


Prevention & Best Practices

1. Audit Transitive Dependencies Regularly

The vulnerability was in a transitive dependency — path-to-regexp was not listed directly in package.json before this fix. Use npm audit, Trivy, or Snyk in your CI pipeline to catch these:

npm audit --audit-level=moderate
trivy fs --scanners vuln .

2. Use overrides (npm) or resolutions (Yarn) Proactively

When a transitive dependency has a known vulnerability and the parent hasn't released a patch yet, pin the safe version yourself:

// package.json
"overrides": {
  "path-to-regexp": ">=0.1.13"
}

3. Validate and Limit URL Parameter Length

Even with the patched library, defense in depth is valuable. Reject or truncate URL parameters that exceed a reasonable length before they reach route matching logic:

// Express middleware example
app.use((req, res, next) => {
  if (req.path.length > 512) {
    return res.status(400).json({ error: 'Path too long' });
  }
  next();
});

4. Audit Custom Regex Patterns

If your application defines its own regular expressions that match user input, audit them with tools like safe-regex or vuln-regex-detector:

npx safe-regex '/^(a+)+$/'  # Returns: false (unsafe)

5. Relevant Standards

  • OWASP: Input Validation Cheat Sheet
  • CWE-1333: Inefficient Regular Expression Complexity
  • OWASP Top 10 2021: A06 — Vulnerable and Outdated Components

Key Takeaways

  • path-to-regexp@0.1.12 is vulnerable to ReDoS via malformed URL parameters — any Node.js application using this version (directly or transitively through Express) is at risk until upgraded to 0.1.13.
  • Transitive dependencies are just as dangerous as direct ones — the vulnerability lived in node_modules/path-to-regexp, not in any code the BakaMusic team wrote, but it was fully exploitable through their HTTP surface.
  • The overrides field in package.json is the correct way to force a safe version across the entire dependency tree, not just the lockfile entry.
  • Switching the registry from npmmirror.com to registry.npmjs.org in the same fix improves supply chain integrity by sourcing the package from the canonical registry.
  • A single unauthenticated HTTP request is enough to trigger this vulnerability — there is no need for an attacker to have an account or any prior knowledge of the application.

How Orbis AppSec Detected This

  • Source: Incoming HTTP request URL path — user-controlled URL parameters supplied to the route matching layer of the bakamusic-plugins Node.js server.
  • Sink: The path-to-regexp@0.1.12 regex compilation and matching call, invoked for every incoming request against registered route patterns in node_modules/path-to-regexp.
  • Missing control: No input length validation before route matching, and no version constraint preventing the vulnerable 0.1.12 release from being installed as a transitive dependency.
  • CWE: CWE-1333 — Inefficient Regular Expression Complexity.
  • Fix: Upgraded path-to-regexp from 0.1.12 to 0.1.13 in package-lock.json and added an overrides entry in package.json to pin the safe version across the full dependency tree.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-4867 is a reminder that a single small dependency — just one file, no native code, no network calls — can become the weakest link in an application's availability posture. The path-to-regexp package is used by millions of Node.js projects, often invisibly as a transitive dependency of Express. Version 0.1.12 contained regex patterns that could be exploited with a single crafted HTTP request to freeze the event loop of the bakamusic-plugins service entirely.

The fix is surgical and safe: upgrade to 0.1.13, switch back to the canonical npm registry, and use package.json overrides to make the constraint durable across future installs. Two files changed, zero behavior change for valid inputs, and the attack surface is closed.

Keep your dependency tree shallow where possible, audit it continuously, and treat transitive vulnerabilities with the same urgency as direct ones.


References

Frequently Asked Questions

What is a ReDoS (Regular Expression Denial of Service) vulnerability?

ReDoS occurs when a regular expression with ambiguous or nested quantifiers takes exponential time to evaluate certain inputs. An attacker can craft a string that causes the regex engine to backtrack catastrophically, consuming 100% CPU and blocking all other work.

How do you prevent ReDoS in Node.js applications?

Use well-audited regex libraries, keep dependencies up to date, validate and limit the length of user-supplied input before regex evaluation, and use tools like `safe-regex` or `vuln-regex-detector` to audit patterns.

What CWE is ReDoS?

ReDoS maps to CWE-1333 (Inefficient Regular Expression Complexity), which describes situations where a regex can be made to run in super-linear time on attacker-controlled input.

Is rate limiting enough to prevent ReDoS?

Rate limiting reduces the frequency of attacks but does not eliminate them. A single well-crafted request can still stall the event loop for seconds or minutes. The only reliable fix is to remove the vulnerable regex pattern.

Can static analysis detect ReDoS vulnerabilities?

Yes. Tools like Trivy (which flagged this CVE), Semgrep, and dedicated ReDoS scanners can identify known-vulnerable package versions and dangerous regex patterns before they reach production.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

Related Articles

high

How Denial of Service via Unbounded Intermediate Arrays happens in JavaScript and how to fix it

CVE-2026-69152 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package (versions prior to 1.1.18/2.1.4/3.0.6/5.0.9) that allows attackers to crash a Node.js application by crafting glob patterns that generate unbounded intermediate arrays, effectively bypassing the earlier CVE-2026-14257 mitigation. The fix upgrades `brace-expansion` from 1.1.14 to 1.1.18 in `frontend/package-lock.json`, closing the bypass and restoring safe memory bounds during pattern expansion.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was discovered in js-yaml affecting both the 3.x and 4.x branches, where parsing YAML documents containing `!!omap` tags triggers quadratic CPU consumption. The fix upgrades js-yaml from `^4.1.1` to `5.2.0` in the project's GitHub Actions workflow dependencies, closing the attack surface for any untrusted YAML input processed by CI/CD tooling.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.

high

How Denial of Service via Specific Input Sequence happens in JavaScript (marked) and how to fix it

CVE-2026-41680 is a high-severity Denial of Service vulnerability in the marked Markdown parsing library, affecting versions prior to 18.0.2. By supplying a crafted input sequence to the parser, an attacker can cause the application to hang or exhaust resources, making the frontend unavailable. Upgrading marked from 18.0.0 to 18.0.2 in both `package.json` and `package-lock.json` closes the vulnerability without affecting valid Markdown rendering.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability in js-yaml (GHSA-5p4m-2wfm-xmqj) caused quadratic CPU consumption when resolving `!!omap` YAML types in both the 3.x and 4.x branches. The fix upgrades js-yaml from 3.14.2 to 3.15.1 and from 4.1.1 to 4.3.1, eliminating the algorithmic complexity exploit while leaving all valid YAML inputs unaffected.

high

How Denial of Service via Unbounded Data Happens in JavaScript and how to fix it

CVE-2025-58754 is a high-severity Denial of Service vulnerability in the popular axios HTTP client library, caused by the absence of a data size check on incoming response or request payloads. An attacker who can influence the size of data processed by axios could exhaust server memory or CPU, bringing down dependent Node.js applications. The fix upgrades axios from version 1.8.4 to 1.18.0, closing the unbounded data processing path.