Introduction
The lacartoons-addon project, a Stremio addon built with Express.js, contained a critical vulnerability hiding in its dependency tree. The path-to-regexp package at version 0.1.7—used internally by Express's router module—was susceptible to CVE-2024-45296, a ReDoS vulnerability that could allow attackers to crash the service with carefully crafted URL patterns.
Looking at the package-lock.json, we can see the vulnerable dependency chain:
"node_modules/router/node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
}
This matters because any Express application exposing routes to user input—which is virtually all of them—becomes a potential target. For a streaming addon handling requests from Stremio clients, a single malicious request could render the entire service unresponsive.
The Vulnerability Explained
What is ReDoS?
Regular Expression Denial of Service (ReDoS) exploits a fundamental weakness in how regex engines process certain patterns. When a regex contains nested quantifiers or overlapping alternatives, the engine may need to try an exponentially growing number of paths to determine if a string matches.
The Vulnerable Pattern in path-to-regexp
The path-to-regexp library converts Express route strings like /user/:id into regular expressions for matching incoming requests. In version 0.1.7, the generated regex patterns contained constructs vulnerable to catastrophic backtracking.
Consider how Express routes work:
// In a typical Express application using stremio-addon-sdk
app.get('/catalog/:type/:id/:extra?.json', (req, res) => {
// Handle catalog requests
});
The path-to-regexp library transforms this route pattern into a regex. In vulnerable versions, patterns with optional parameters (:extra?) and complex path structures generated regex with nested quantifiers.
How the Attack Works
An attacker could craft a URL designed to trigger exponential backtracking:
GET /catalog/movie/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
When the regex engine attempts to match this against a route pattern, it may enter a state where it tries millions or billions of combinations before determining the string doesn't match. Each additional character can double the processing time.
For the lacartoons-addon specifically, routes like:
// Stremio addon routes typically include
/stream/:type/:id.json
/catalog/:type/:id.json
/meta/:type/:id.json
Any of these endpoints could be targeted with malicious input, causing the Node.js event loop to block while the regex engine churns through backtracking paths.
Real-World Impact
For this Stremio addon:
- Service Unavailability: A single malicious request could freeze the addon for seconds or minutes
- Cascading Failures: Multiple concurrent attacks could exhaust all available CPU
- No Authentication Required: The attack works against public endpoints
- Amplification: Attackers can automate requests, multiplying the impact
The Fix
The fix involves upgrading path-to-regexp to patched versions that eliminate the vulnerable regex patterns. Let's examine the specific changes in package-lock.json:
Before (Vulnerable)
"node_modules/router/node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
}
After (Fixed)
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
"license": "MIT"
}
Key Changes Explained
-
Version Upgrade: The vulnerable 0.1.7 is replaced with 0.1.10, which contains fixes for CVE-2024-45296
-
Dependency Tree Restructuring: The fix also removed a standalone
path-to-regexpat version 8.4.2:
diff -"node_modules/path-to-regexp": { - "version": "8.4.2",
This consolidates the dependency and ensures all route matching uses the patched version. -
Node.js Engine Requirement: The fix adds an engine constraint:
json "engines": { "node": ">=18.0.0" }
This ensures the application runs on Node.js versions with improved regex performance and security features. -
License Clarification: Changed from ISC to MIT license, aligning with the dependency's license.
How the Patched Version Fixes ReDoS
Version 0.1.10 of path-to-regexp rewrites the regex generation logic to:
- Avoid nested quantifiers that cause exponential backtracking
- Use atomic grouping patterns where possible
- Limit the complexity of generated expressions
- Add safeguards against pathological input patterns
Prevention & Best Practices
1. Keep Dependencies Updated
# Regularly audit dependencies
npm audit
# Update to patched versions
npm update
# Use automated tools like Dependabot or Renovate
2. Monitor for CVEs in Your Dependency Tree
The vulnerability existed in a transitive dependency (router → path-to-regexp), not a direct dependency. Use tools that scan the entire dependency tree:
# Trivy scanning (as used in this detection)
trivy fs --scanners vuln .
# npm's built-in audit
npm audit --all
3. Implement Request Timeouts
Even with patched dependencies, add defense in depth:
const express = require('express');
const app = express();
// Set request timeout
app.use((req, res, next) => {
req.setTimeout(5000, () => {
res.status(408).send('Request Timeout');
});
next();
});
4. Input Validation Before Route Matching
Validate URL length and character sets before Express processes routes:
app.use((req, res, next) => {
if (req.url.length > 2048) {
return res.status(414).send('URI Too Long');
}
next();
});
5. Use Rate Limiting
Limit requests to mitigate automated attacks:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per window
});
app.use(limiter);
Key Takeaways
- Transitive dependencies matter: The vulnerability was in
path-to-regexp0.1.7, a dependency of Express's router, not a direct project dependency - Stremio addons using stremio-addon-sdk inherit Express vulnerabilities: Any addon built on this SDK should verify their path-to-regexp version
- ReDoS attacks require no authentication: Public endpoints in the lacartoons-addon like
/catalog/:type/:id.jsonwere all potential attack vectors - Trivy correctly identified CVE-2024-45296 in package-lock.json: Automated scanning caught this before exploitation
- The fix required careful dependency tree management: Simply updating one package wasn't enough; the entire dependency resolution needed adjustment
How Orbis AppSec Detected This
- Source: HTTP request URLs processed by Express route matching in the lacartoons-addon
- Sink:
path-to-regexpregex execution within Express's router module (transitive dependency viastremio-addon-sdk) - Missing control: No protection against backtracking regex patterns in route matching; vulnerable path-to-regexp version 0.1.7 in dependency tree
- CWE: CWE-1333 (Inefficient Regular Expression Complexity)
- Fix: Upgraded path-to-regexp from 0.1.7 to 0.1.10 in the Express router dependency, eliminating vulnerable regex patterns that enabled catastrophic backtracking
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-2024-45296 in path-to-regexp demonstrates how a vulnerability in a deeply nested dependency can expose your entire application to denial of service attacks. The lacartoons-addon, like many Express-based applications, was vulnerable simply by using standard routing patterns.
The fix was straightforward—a dependency upgrade—but finding it required scanning the complete dependency tree. This underscores the importance of automated security scanning that goes beyond direct dependencies to examine the full package-lock.json.
For Node.js developers: regularly audit your dependencies, implement defense-in-depth measures like request timeouts and rate limiting, and consider the security implications of every package in your dependency tree, no matter how deeply nested.