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 Denial of Service vulnerability in path-to-regexp 0.1.12 where malformed URL parameters can trigger catastrophic backtracking in the library's regular expression engine, allowing an attacker to hang or crash a Node.js application with a single crafted request. The fix upgrades path-to-regexp to version 0.1.13, which patches the vulnerable regex patterns. This change was applied via a package-level override to ensure the patched version is used throughout the entire dependency

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 npm package path-to-regexp version 0.1.12, affecting Node.js applications that use Express or similar routing frameworks. When path-to-regexp processes malformed URL parameters, its internal regular expressions exhibit catastrophic backtracking, causing the event loop to stall and the application to become unresponsive. The fix is to upgrade path-to-regexp to version 0.1.13, which rewrites the vulnerable regex patterns to eliminate exponential backtracking. In projects where path-to-regexp is a transitive dependency, a package.json `overrides` block forces the patched version across the entire dependency tree.

Vulnerability at a Glance

cweCWE-1333
fixUpgrade path-to-regexp from 0.1.12 to 0.1.13 and add a package.json overrides entry to enforce the patched version across all transitive dependencies
riskAn attacker can send a single crafted HTTP request to stall the Node.js event loop indefinitely, causing a full application outage
languageJavaScript / Node.js
root causepath-to-regexp 0.1.12 uses regular expressions with nested quantifiers that exhibit catastrophic backtracking on malformed URL parameter input
vulnerabilityRegular Expression Denial of Service (ReDoS)

The Vulnerability at a Glance

Field Detail
CVE CVE-2026-4867
CWE CWE-1333 — Inefficient Regular Expression Complexity
Package path-to-regexp 0.1.12
Severity Medium/High
Fix Upgrade to path-to-regexp 0.1.13 + overrides in package.json

Introduction

The package-lock.json file in this project pinned path-to-regexp at version 0.1.12 — a version that contains a subtle but dangerous flaw. When the library parses URL route patterns containing malformed parameters, its internal regular expressions can enter a state of catastrophic backtracking: the regex engine starts exploring an exponentially growing number of match possibilities and never finishes. In a Node.js application, this means the single-threaded event loop stalls, every other request queues up behind it, and the application goes dark — all from one carefully crafted HTTP request.

This is CVE-2026-4867, and it was automatically detected by Trivy scanning the project's package-lock.json.


The Vulnerability Explained

What Is Catastrophic Backtracking?

Regular expression engines work by trying to match a pattern against an input string. When a pattern contains nested quantifiers — for example, (a+)+ or ([^/]+)* — the engine may need to try an exponential number of combinations before concluding that the string does not match. For a short string, this is imperceptible. For a specially crafted string of moderate length (say, 30–50 characters), it can take billions of steps and minutes or hours of CPU time.

path-to-regexp converts human-readable route strings like /user/:id into regular expressions used to match incoming URL paths. In version 0.1.12, the generated regular expressions for certain parameter patterns contained exactly this kind of vulnerable structure.

The Vulnerable Package Version

Before the fix, package-lock.json contained:

"node_modules/path-to-regexp": {
  "version": "0.1.12",
  "license": "MIT"
}

No resolved URL and no integrity hash were recorded, meaning the lock file did not strongly pin the exact artifact. More critically, the version itself (0.1.12) is the vulnerable one.

How an Attacker Exploits This

Consider a Node.js/Express application with a route like:

app.get('/api/users/:userId/posts/:postId', handler);

path-to-regexp converts this into a regex to match against incoming request paths. If an attacker sends a request such as:

GET /api/users/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!/posts/1 HTTP/1.1

The trailing ! (or other characters outside the expected parameter character class) causes the regex engine to backtrack catastrophically as it tries — and repeatedly fails — to match the parameter segment. Because Node.js runs JavaScript on a single thread, this regex evaluation blocks the entire event loop. No other requests can be processed until the evaluation completes or the process is killed.

A single such request is sufficient to cause a complete Denial of Service for all users of the application.

Why This Is Particularly Dangerous

  • No authentication required. The malformed URL hits the routing layer before any auth middleware runs.
  • Single request, full outage. There is no need for sustained traffic — one request can freeze the process.
  • Transitive dependency. Many projects don't directly depend on path-to-regexp; it is pulled in by Express, Koa Router, and other popular frameworks, making it easy to overlook.

The Fix

What Changed

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

package-lock.json — Upgrading the Pinned Version

 "node_modules/path-to-regexp": {
-  "version": "0.1.12",
+  "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"
 }

Version 0.1.13 rewrites the vulnerable regular expression patterns to eliminate nested quantifiers that cause catastrophic backtracking. The addition of resolved and integrity fields also strengthens supply-chain security: npm will now verify the downloaded artifact against a known-good SHA-512 hash, preventing substitution attacks.

package.json — Enforcing the Fix Across the Dependency Tree

+  },
+  "overrides": {
+    "path-to-regexp": "0.1.13"
+  }

This is the critical second change. Because path-to-regexp is typically a transitive dependency (pulled in by Express or another router), simply updating a direct dependency entry would not guarantee that the vulnerable version is replaced everywhere in the tree. The npm overrides field forces npm to use version 0.1.13 for every occurrence of path-to-regexp regardless of which package requested it and what version range it specified.

Without this overrides entry, a nested dependency could still resolve to 0.1.12, leaving the vulnerability present even after the lock file update.

Why This Specific Fix Works

Version 0.1.13 of path-to-regexp replaces the problematic regex construction logic with patterns that have linear time complexity in the worst case. The parameter-matching segments no longer use nested quantifiers, so there is no combinatorial explosion when the input contains unexpected characters. The route matching behavior for valid URLs is identical — this is a security patch, not a behavioral change.


Prevention & Best Practices

1. Keep Routing Dependencies Current

path-to-regexp is one of the most widely used packages in the Node.js ecosystem. Subscribe to security advisories for your direct dependencies and their transitive dependencies using tools like:

  • npm audit (built into npm)
  • Trivy for container and filesystem scans
  • Dependabot or Renovate for automated upgrade PRs

2. Use overrides for Transitive Vulnerabilities

When a vulnerability exists in a transitive dependency that you don't control directly, npm's overrides (npm 8.3+) or Yarn's resolutions field lets you force a safe version:

"overrides": {
  "path-to-regexp": "0.1.13"
}

This is the correct pattern for patching transitive dependency vulnerabilities without waiting for upstream packages to release updates.

3. Validate and Sanitize URL Parameters Early

While the root fix is in the library, defense-in-depth measures help:

// Reject requests with obviously malformed parameter values early
app.use((req, res, next) => {
  if (/[^\w\-\.~%]/.test(req.params.id)) {
    return res.status(400).json({ error: 'Invalid parameter format' });
  }
  next();
});

This won't fully prevent ReDoS (the regex runs before middleware in some frameworks), but it reduces the attack surface.

4. Add Request Timeouts

Configure your HTTP server and any upstream proxy to enforce request timeouts. A stalled event loop will eventually be interrupted:

const server = app.listen(3000);
server.setTimeout(5000); // 5-second timeout

5. Run a ReDoS Analyzer on Your Regex Patterns

If you write custom regular expressions, use tools like:

Relevant Standards

  • CWE-1333: Inefficient Regular Expression Complexity
  • OWASP: Denial of Service Cheat Sheet
  • OWASP Top 10: A05:2021 — Security Misconfiguration (outdated/vulnerable components)

Key Takeaways

  • path-to-regexp 0.1.12 is vulnerable to ReDoS via malformed URL parameters — a single crafted request can freeze your Node.js event loop entirely.
  • The overrides field in package.json is essential when patching transitive dependencies; updating only package-lock.json is insufficient if the parent package still requests the old version range.
  • The integrity hash added to package-lock.json in version 0.1.13 provides supply-chain protection that was absent in the 0.1.12 entry — always ensure lock files include resolved URLs and integrity hashes.
  • ReDoS attacks require no authentication and no sustained traffic — the routing layer is hit before any auth middleware, making this a zero-barrier Denial of Service.
  • Automated scanning of package-lock.json (not just package.json) is necessary to catch vulnerabilities in transitive dependencies like this one.

How Orbis AppSec Detected This

  • Source: Incoming HTTP request URL paths processed by the Express (or compatible) router — specifically, the path segment matched against route patterns containing :param tokens.
  • Sink: The path-to-regexp regex compilation and matching logic in node_modules/path-to-regexp/index.js (version 0.1.12), invoked on every incoming request that hits a parameterized route.
  • Missing control: No safe regex construction was applied to parameter-matching segments; the library used nested quantifiers that allow exponential backtracking on crafted input. No request-level timeout or input pre-validation was present to interrupt a stalled match.
  • 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 enforce the patched 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 some of the most impactful vulnerabilities are not buffer overflows or SQL injections — they are subtle algorithmic flaws buried inside widely-used utility libraries. A single version bump in path-to-regexp (from 0.1.12 to 0.1.13) closes a door that could have let any unauthenticated user take down the entire application with one malformed URL.

The fix here is clean and surgical: update the pinned version in package-lock.json, add a cryptographic integrity hash, and add an overrides block to package.json to ensure no nested dependency can silently pull the vulnerable version back in. This pattern — version pin plus override plus integrity hash — is the gold standard for remediating transitive dependency vulnerabilities in the npm ecosystem.

Keep your dependency tree audited, your lock files committed, and your routing libraries current.


References

Frequently Asked Questions

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

ReDoS is an attack where a crafted input string causes a regular expression engine to explore an exponentially large number of possible matches before failing, consuming 100% CPU and blocking other work.

How do you prevent ReDoS in Node.js?

Use regex libraries or patterns that avoid nested quantifiers and catastrophic backtracking; keep routing dependencies like path-to-regexp up to date; and consider request timeouts or rate limiting as defense-in-depth.

What CWE is ReDoS?

ReDoS is classified as CWE-1333 (Inefficient Regular Expression Complexity).

Is input validation enough to prevent ReDoS?

Not on its own. Input validation helps reduce the attack surface, but the root fix must be in the regular expression itself — either by rewriting the pattern or upgrading to a patched library version.

Can static analysis detect ReDoS?

Yes. Tools like Trivy (which flagged this CVE), Semgrep, and dedicated ReDoS analyzers such as vuln-regex-detector can identify vulnerable regex patterns and outdated package versions automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #112

Related Articles

high

How Dependabot Missing Cooldown happens in GitHub Actions and how to fix it

A high-severity misconfiguration in `.github/dependabot.yml` left this Node.js library without a cooldown period, meaning Dependabot would immediately propose updates to newly published packages — including potentially malicious or unstable ones. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` package ecosystem entries, introducing a mandatory 7-day waiting period before any new package version is surfaced as an update candidate.

critical

How CSRF Protection Failures Happen in FastAPI and How to Fix Them

A critical CORS misconfiguration in `backend/main.py` allowed cookies to be sent alongside wildcard-origin requests, violating the CORS specification and opening the door to cross-site request forgery attacks. The fix conditionally disables `allow_credentials` when the allowed origins list contains a wildcard, bringing the configuration into compliance with browser security rules. This change closes a subtle but dangerous gap that could have let attackers on sibling subdomains forge authenticate

critical

How Missing Rate Limiting Happens in Node.js SSE Handlers and How to Fix It

A critical missing rate-limiting control in `src/sse/handlers/chat.js` allowed any caller to flood the SSE chat endpoint with unlimited requests, risking server resource exhaustion, denial of service, and runaway AI provider API costs. The fix introduces a per-IP sliding-window rate limiter that caps requests at 60 per minute and returns HTTP 429 on violations. Because the endpoint was publicly reachable and only validated API keys — not request frequency — exploitation required nothing more tha

high

How Denial of Service via Exponential-Time Complexity happens in Node.js and how to fix it

CVE-2026-13149 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package, where crafted input strings trigger exponential-time processing that can freeze or crash a Node.js application. The fix upgrades `brace-expansion` from `2.0.2` to `2.1.4` and `minimatch` from `5.1.6` to `5.1.9`, along with npm `overrides` to ensure the patched versions are used throughout the entire dependency tree.

critical

How Unrestricted File Upload happens in Node.js/Express and how to fix it

A critical unrestricted file upload vulnerability was discovered in `mainsystem/routes/admin/profile.js`, where the avatar upload endpoint accepted any file type without validation. An authenticated attacker could upload a malicious server-side script to a web-accessible directory and execute arbitrary code on the server. The fix adds MIME type filtering, an allowlist of safe image formats, and a 2 MB file size limit to the multer middleware.

medium

How Integer Overflow happens in C++ image processing and how to fix it

A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.