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:
- safe-regex — detects potentially catastrophic patterns
- vuln-regex-detector — comprehensive ReDoS analysis
- Semgrep rules for ReDoS: https://semgrep.dev/r?q=redos
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
overridesfield inpackage.jsonis essential when patching transitive dependencies; updating onlypackage-lock.jsonis insufficient if the parent package still requests the old version range. - The
integrityhash added topackage-lock.jsonin 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 justpackage.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
:paramtokens. - Sink: The
path-to-regexpregex compilation and matching logic innode_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-regexpfrom0.1.12to0.1.13inpackage-lock.jsonand added anoverridesentry inpackage.jsonto 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.