Back to Blog
high SEVERITY8 min read

How Cache-Control Header Injection Happens in Node.js HTTP Libraries and How to Fix It

CVE-2026-13697 is a high-severity vulnerability in the undici HTTP client library where the cache interceptor mishandles malformed Cache-Control directives, potentially leading to information disclosure and denial of service attacks. Upgrading from undici 7.28.0 to 7.29.0 (or 8.9.0 for v8 users) patches this vulnerability by implementing stricter validation of Cache-Control headers. This fix is critical for any Node.js application that relies on undici for HTTP requests, especially those handlin

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

Answer Summary

CVE-2026-13697 is a cache-handling vulnerability in undici (Node.js HTTP client) where the cache interceptor mishandles malformed Cache-Control directives, potentially leaking sensitive information or causing denial of service. The vulnerability exists in undici versions before 7.29.0 and 8.9.0. The fix involves upgrading undici and adding dependency overrides to ensure strict validation of Cache-Control headers, preventing attackers from manipulating caching behavior through specially crafted headers.

Vulnerability at a Glance

cweCWE-345 (Insufficient Verification of Data Authenticity), CWE-444 (Inconsistent Interpretation of HTTP Requests)
fixUpgrade undici to 7.29.0 or 8.9.0 with stricter Cache-Control directive validation
riskInformation disclosure (cached sensitive data exposure), Denial of Service via cache poisoning
languageJavaScript/Node.js
root causeInadequate parsing and validation of Cache-Control header directives in undici's cache interceptor
vulnerabilityCache-Control Header Injection / Information Disclosure via Malformed Directives

How Cache-Control Header Injection Happens in Node.js HTTP Libraries and How to Fix It

Introduction

In the node-app repository, a high-severity vulnerability (CVE-2026-13697) was discovered in the undici HTTP client library's cache interceptor. The vulnerability stems from inadequate parsing and validation of the Cache-Control HTTP header, allowing attackers to craft malformed directives that bypass cache validation logic. This could result in sensitive data being cached inappropriately or cache poisoning attacks that cause denial of service.

The vulnerable code path handles user-influenced HTTP responses, and the cache interceptor processes the Cache-Control header without sufficiently strict validation. When an attacker sends a response with a specially crafted Cache-Control header containing malformed directives, the interceptor's parsing logic fails to properly reject or sanitize these directives, leading to unexpected caching behavior.

This matters for developers because:
- Sensitive data exposure: Cached responses containing authentication tokens, personal information, or API keys could be served to unintended clients
- Cache poisoning: Attackers can manipulate cached responses to serve malicious content to subsequent requests
- Denial of service: Malformed directives could cause the cache to behave unexpectedly, leading to performance degradation or crashes


The Vulnerability Explained

What Happens During the Attack

The undici cache interceptor is responsible for respecting HTTP caching semantics as defined in RFC 7234. The Cache-Control header contains directives that control caching behavior—directives like max-age=3600, private, no-store, etc.

The problem: When the cache interceptor receives a response with a malformed Cache-Control header—for example, one with improperly formatted directives or unexpected syntax—it fails to properly validate or reject the malformed input. Instead of treating the header as invalid and refusing to cache, the interceptor may:

  1. Partially parse the header, ignoring malformed portions while caching based on the valid portions
  2. Misinterpret directives, treating private as public or vice versa due to parsing errors
  3. Cache when it shouldn't, storing responses that should never be cached according to strict RFC compliance

Attack Scenario

Consider this real-world attack:

HTTP/1.1 200 OK
Cache-Control: max-age=3600, private=invalid-syntax, no-store
Content-Type: application/json

{
  "user_id": 12345,
  "api_token": "secret_token_xyz",
  "email": "user@example.com"
}

A strict parser should reject this header entirely because private=invalid-syntax is malformed (the private directive takes no parameters). However, if undici's cache interceptor doesn't validate this properly, it might:
- Ignore the malformed private=invalid-syntax directive
- Cache the response based on max-age=3600
- Result: Sensitive user data with an API token gets cached and served to other users or requests

An attacker could also craft headers that exploit the parsing logic in reverse:

Cache-Control: max-age=3600, public, no-store=ignored

If the parser processes directives left-to-right without proper precedence handling, it might cache the response (seeing max-age and public) while ignoring the no-store directive.

The Real Impact

For applications using undici (which is the HTTP client for many Node.js frameworks and tools):
- API responses containing authentication tokens could be cached and leaked to other users
- Personal data from API responses could persist in the cache longer than intended
- Cache poisoning attacks could serve stale or malicious data to clients
- Performance degradation if the cache behaves unexpectedly due to malformed directives


The Fix

What Changed

The fix involves upgrading undici from 7.28.0 to 7.29.0 (or 8.9.0 for v8 users). The upgrade includes stricter validation of Cache-Control header directives in the cache interceptor.

Before the fix (package.json and package-lock.json with undici 7.28.0):

"undici": {
  "version": "7.28.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
  "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
}

After the fix (undici 7.29.0):

"undici": {
  "version": "7.29.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
  "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
}

Additionally, a dependency override was added to package.json to ensure all transitive dependencies use the patched version:

{
  "homepage": "https://github.com/lovasoa/dezoomify",
  "overrides": {
    "undici": "7.29.0"
  }
}

Why These Changes Matter

  1. Version bump (7.28.0 → 7.29.0): The undici maintainers fixed the cache interceptor's validation logic to properly reject malformed Cache-Control directives. The new version implements RFC 7234 compliance more strictly.

  2. Dependency override: By adding the override, we ensure that even if other dependencies in the project specify an older version of undici, npm will use 7.29.0. This prevents transitive dependency conflicts from reintroducing the vulnerability.

  3. Integrity hash update: The sha512 hash changed because the package contents changed. This is expected and confirms we're using a different (patched) version.

How the Fix Prevents the Vulnerability

The patched version (7.29.0) includes:
- Stricter directive parsing: Malformed directives like private=invalid-syntax are now properly rejected
- Proper directive precedence: Directives are processed according to RFC 7234 specifications, preventing contradictory directives from causing unexpected behavior
- Validation of directive values: Parameters to directives are validated (e.g., max-age must be a numeric value)
- Fail-safe caching: If the Cache-Control header is invalid or malformed, the response is treated as non-cacheable by default

Now, when the cache interceptor encounters the malformed header from our attack scenario:

Cache-Control: max-age=3600, private=invalid-syntax, no-store

It will:
1. Parse max-age=3600 ✓ (valid)
2. Parse private ✓ (valid, no parameters expected)
3. Reject private=invalid-syntax ✗ (invalid syntax)
4. Reject the entire header as malformed and treat the response as non-cacheable

This ensures sensitive data is never cached inappropriately.


Key Takeaways

  • Cache-Control header injection is subtle: Malformed directives can bypass validation logic in HTTP clients, leading to unexpected caching behavior
  • Undici versions before 7.29.0 are vulnerable: If you're using undici 7.28.0 or earlier (or 8.x versions before 8.9.0), you must upgrade immediately
  • Dependency overrides are crucial: Using "overrides" in package.json ensures all transitive dependencies use the patched version, preventing version conflicts
  • Sensitive data requires explicit cache headers: Never rely on default caching behavior for responses containing authentication tokens or personal information
  • Static analysis caught this early: Security scanners like Trivy detected this vulnerability by matching package versions against the CVE database, preventing exploitation in production

How Orbis AppSec Detected This

Source: HTTP response headers from external APIs and services (specifically the Cache-Control header field)

Sink: The cache interceptor in undici's HTTP client library that processes the Cache-Control directive without strict RFC 7234 validation

Missing control: Insufficient validation of Cache-Control header syntax; malformed directives were not properly rejected, allowing them to influence caching decisions

CWE:
- CWE-345: Insufficient Verification of Data Authenticity
- CWE-444: Inconsistent Interpretation of HTTP Requests ('HTTP Request Smuggling')

Fix: Upgrade undici to version 7.29.0 or 8.9.0, which implements stricter Cache-Control header parsing and validation according to RFC 7234 specifications, and add a dependency override to ensure all transitive dependencies 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 demonstrates how subtle parsing vulnerabilities in HTTP libraries can lead to serious security issues. By mishandling malformed Cache-Control directives, undici's cache interceptor created an opportunity for information disclosure and cache poisoning attacks. The fix—upgrading to undici 7.29.0 or 8.9.0—implements stricter validation that prevents these attacks.

The key lesson for developers: never assume that third-party libraries handle untrusted input safely. Always keep dependencies updated, monitor security advisories, and implement application-level validation for critical security decisions like caching. By combining dependency management with defense-in-depth strategies, you can significantly reduce your attack surface.

If you're using undici in your Node.js applications, upgrade now. If you're using other HTTP clients, check their security advisories and ensure you're running the latest patched versions.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #991

Related Articles

critical

LDAP Filter Injection in da_unique_email_validator Fixed

The registration-time email uniqueness validator, `da_unique_email_validator`, formatted the submitted email address straight into an LDAP search filter with Python's `%` operator, so filter metacharacters in the email were interpreted as filter syntax. The fix wraps the value in `ldap.filter.escape_filter_chars()` (and imports the `ldap.filter` submodule explicitly), so a submitted address is always treated as a literal attribute value. Any deployment with `ldap login` enabled and a bind accoun

high

installPlugin(): Unvalidated npm Package Names Reach npm install

A plugin manager service exposed an `installPlugin(plugin: PluginInfo)` method that passed `plugin.packageName` and `plugin.version` straight into the platform's npm install routine with no validation, no blocklist, and no integrity verification of the fetched tarball. Because npm treats a non-semver "version" as a fetch specifier — a tarball URL, a git ref, a local path — an attacker who could influence the plugin listing could get arbitrary code installed and executed with full Electron/Node p

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

critical

eval() in Async Function Constructor Enables Runtime Escape

The eval.mjs command handler used raw `eval()` to execute JavaScript expressions, creating a critical code injection path if owner credentials are compromised. The fix replaces `eval()` with the `AsyncFunction` constructor and explicitly shadows `process`, `require`, and other runtime globals as parameters, preventing evaluated code from reaching the Node.js runtime even when authentication boundaries fail.

high

How Regular Expression Denial of Service (ReDoS) Happens in Node.js trim-newlines and How to Fix It

CVE-2021-33623 exposed a Regular Expression Denial of Service (ReDoS) vulnerability in the npm package `trim-newlines` versions 1.0.0 and earlier. The vulnerable `.end()` method used an inefficient regex pattern that could cause severe performance degradation when processing malicious input. Upgrading to version 4.0.1 patches the regex implementation and eliminates the attack surface.

critical

How CSS Injection via Weak Pattern Validation happens in Vue.js and how to fix it

A critical CSS injection vulnerability in `testpage/App.vue` allowed attackers to bypass weak HTML5 pattern validation and load malicious stylesheets. The fix replaces direct variable assignment with a hardened `setCustomStylesheetHref()` method using strict regex validation.