Introduction
In this repository's package-lock.json, Trivy flagged a high-severity vulnerability in the undici HTTP client library at version 7.25.0. The undici package is a fast, spec-compliant HTTP/1.1 client for Node.js that's widely used—including as the underlying fetch implementation in modern Node.js versions. The vulnerability, tracked as CVE-2026-13697, exists in how undici processes Cache-Control response headers, creating a pathway for both information disclosure and denial of service attacks.
What makes this particularly concerning is that undici often exists deep in dependency trees. Even if your direct dependencies don't explicitly list undici, it may be pulled in transitively through frameworks, testing libraries, or API clients. This is exactly the scenario we encountered here—the vulnerability was flagged in the dependency tree with the assessment "present in dependency tree, not confirmed reachable."
The Vulnerability Explained
CVE-2026-13697 targets the Cache-Control header parsing logic within undici. When an HTTP response contains malformed Cache-Control directives, the vulnerable versions of undici fail to properly validate and sanitize these values before processing them.
How Cache-Control Parsing Should Work
A typical Cache-Control header looks like this:
Cache-Control: max-age=3600, public, no-transform
The parser should extract these directives and their values safely, rejecting or sanitizing malformed input.
The Attack Vector
An attacker controlling an HTTP response (such as through a compromised API endpoint, man-in-the-middle attack, or malicious redirect) could craft a response with specially malformed Cache-Control directives:
Cache-Control: max-age=9999999999999999999999, private, [malicious-payload]
In vulnerable versions, this malformed input could:
- Information Disclosure: Cause the parser to expose internal state, memory contents, or cached data from other requests
- Denial of Service: Trigger excessive resource consumption, infinite loops, or crashes in the parsing logic
Real-World Impact
Consider a scenario where your Node.js application makes API calls to external services:
// Your application fetching data from an external API
const response = await fetch('https://api.external-service.com/data');
const data = await response.json();
If api.external-service.com is compromised or an attacker can intercept the response, they could inject malformed Cache-Control headers that exploit this vulnerability. The impact ranges from application crashes (affecting availability) to potential leakage of sensitive information from your application's memory or cache.
The Fix
The fix involves two key changes to ensure the vulnerable undici version is replaced throughout the entire dependency tree.
Before the Fix
The package-lock.json specified undici at the vulnerable version:
"node_modules/undici": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
"integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
After the Fix
The version was upgraded to 7.29.0:
"node_modules/undici": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
"integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
The Critical Addition: npm Overrides
Simply updating the direct dependency isn't sufficient. The fix adds an npm override in package.json:
"overrides": {
"tar": "7.5.21",
"brace-expansion": "5.0.9",
"undici": "7.29.0"
}
This override is crucial because it forces all instances of undici in the dependency tree—including those pulled in by transitive dependencies—to use version 7.29.0. Without this override, a nested dependency could still resolve to the vulnerable 7.25.0 version.
Additional Change: @capacitor/core
The fix also removed the peer: true designation from @capacitor/core:
- "peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
This change ensures the package is properly included in the dependency resolution, preventing potential version conflicts that could undermine the security fix.
Prevention & Best Practices
1. Use npm Overrides for Security Patches
When patching transitive dependencies, always use npm overrides (npm 8.3+) or yarn resolutions:
{
"overrides": {
"vulnerable-package": "^patched-version"
}
}
2. Implement Automated Dependency Scanning
Integrate tools like Trivy, Snyk, or npm audit into your CI/CD pipeline:
# Example GitHub Actions step
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
3. Validate External HTTP Responses
Even with patched libraries, implement defensive coding practices:
// Add response validation
const response = await fetch(url);
const cacheControl = response.headers.get('cache-control');
// Validate header format before processing
if (cacheControl && !isValidCacheControl(cacheControl)) {
console.warn('Suspicious Cache-Control header detected');
// Handle appropriately
}
4. Keep Dependencies Updated
Establish a regular cadence for dependency updates. Consider using tools like Dependabot or Renovate for automated PR creation.
5. Monitor Security Advisories
Subscribe to security advisories for critical dependencies:
- Node.js Security Working Group
- npm Security Advisories
- GitHub Security Advisories
Key Takeaways
- Transitive dependencies require explicit overrides: The vulnerable undici version could exist in nested dependencies even after updating direct dependencies—npm overrides ensure comprehensive patching
- HTTP client libraries are high-value targets: undici's role in processing untrusted server responses makes header parsing vulnerabilities particularly dangerous
- "Not confirmed reachable" still requires action: Even when a vulnerability isn't confirmed reachable in your specific code paths, the risk of future code changes or indirect exploitation warrants patching
- Cache-Control headers are untrusted input: Any data from HTTP responses, including headers, should be treated as potentially malicious
- Version pinning in overrides provides defense-in-depth: The explicit
"undici": "7.29.0"override prevents dependency resolution from accidentally downgrading to vulnerable versions
How Orbis AppSec Detected This
- Source: HTTP response headers from external servers, specifically Cache-Control directives processed by the undici HTTP client
- Sink: undici's internal Cache-Control parsing logic in versions prior to 7.29.0
- Missing control: Proper validation and sanitization of malformed Cache-Control directive values before processing
- CWE: CWE-400 (Uncontrolled Resource Consumption) and CWE-200 (Exposure of Sensitive Information)
- Fix: Upgraded undici to version 7.29.0 via npm overrides to ensure all dependency tree instances use the patched version
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-13697 in undici demonstrates how vulnerabilities in HTTP client libraries can have far-reaching security implications. The malformed Cache-Control directive parsing flaw could enable both information disclosure and denial of service attacks against any Node.js application using affected versions.
The fix—upgrading to undici 7.29.0 with npm overrides—is straightforward but highlights an important lesson: transitive dependencies require explicit management. Simply updating your direct dependencies isn't always sufficient; you need mechanisms like npm overrides to ensure security patches propagate throughout your entire dependency tree.
As developers, we must treat all external input—including HTTP headers—as potentially malicious and ensure our dependencies are regularly updated and scanned for vulnerabilities.