Back to Blog
high SEVERITY6 min read

How Cache-Control Header Mishandling Happens in Node.js HTTP Clients and How to Fix It

CVE-2026-13697 is a high-severity vulnerability in undici, the popular Node.js HTTP client, where the cache interceptor fails to properly validate malformed `Cache-Control: private` directives. This could allow sensitive cached responses to be served to unauthorized users. The fix upgrades undici from 7.28.0 to 7.29.0 (and 6.27.0 to 6.28.0) across the dependency tree, including using npm overrides to patch transitive dependencies.

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

Answer Summary

CVE-2026-13697 is a high-severity cache poisoning vulnerability in undici (Node.js HTTP client) where the cache interceptor mishandles malformed Cache-Control `private` directives, potentially serving sensitive cached responses to unauthorized parties. The fix is to upgrade undici to version 7.29.0 (or 6.28.0 for the v6 line) where the Cache-Control parsing logic correctly rejects malformed directives. This relates to CWE-525 (Use of Web Browser Cache Containing Sensitive Information).

Vulnerability at a Glance

cweCWE-525
fixUpgrade undici to 7.29.0/6.28.0 where Cache-Control parsing is corrected
riskSensitive cached responses served to unauthorized users due to malformed Cache-Control parsing
languageJavaScript (Node.js)
root causeundici's cache interceptor fails to correctly parse malformed `Cache-Control: private` directives
vulnerabilityCache-Control Header Parsing Bypass

Introduction

In the nix/native-modules dependency tree, Trivy flagged a high-severity vulnerability (CVE-2026-13697) in the undici HTTP client library. The issue sits in undici's cache interceptor — the component responsible for deciding whether an HTTP response should be stored and later served from cache based on Cache-Control headers.

The vulnerable versions (undici 7.28.0 and 6.27.0) were declared in nix/native-modules/package-lock.json, pulled in both as a direct optional dependency and as a transitive dependency through @electron/get via node-gyp. The flaw could allow an attacker who controls HTTP response headers to bypass the Cache-Control: private directive by sending a malformed variant, causing the cache interceptor to store and serve sensitive responses to other users.

The Vulnerability Explained

What Goes Wrong

Undici's cache interceptor parses Cache-Control response headers to determine caching behavior. When a server sends Cache-Control: private, the interceptor should never store that response in a shared cache — it's meant only for the specific user who requested it.

However, CVE-2026-13697 reveals that malformed Cache-Control: private directives (such as those with unusual whitespace, trailing characters, or non-standard formatting) were not being correctly identified by undici's parsing logic. Instead of rejecting or properly interpreting the malformed directive, the cache interceptor would fall through to its default behavior and cache the response anyway.

The Vulnerable Dependency Declaration

In the lock file, the vulnerable versions were pinned:

"node_modules/node-gyp/node_modules/undici": {
  "version": "6.27.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
  "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg=="
}
"node_modules/undici": {
  "version": "7.28.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
  "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="
}

Attack Scenario

Consider this scenario specific to this application:

  1. The application uses @electron/get (which depends on undici) to download Electron binaries or assets over HTTP.
  2. An attacker performing a man-in-the-middle attack (or controlling a CDN/proxy) injects a response with a malformed Cache-Control header like Cache-Control: private="\x00" or Cache-Control: private, (with irregular formatting).
  3. Undici's cache interceptor fails to recognize this as a private directive and caches the response.
  4. Subsequent requests from other contexts or users receive the cached response, which may contain session-specific data, authentication tokens, or tampered content.

For an Electron build pipeline, this could mean serving a poisoned binary from cache to subsequent builds — a supply chain attack vector.

The Fix

The fix involves two coordinated changes across package.json and package-lock.json:

1. Upgrading the Direct Dependency (package-lock.json)

The top-level optional undici dependency was bumped:

Before:

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

After:

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

The nested node-gyp dependency was also bumped from 6.27.0 to 6.28.0.

2. Forcing Transitive Dependency Resolution (package.json)

Critically, the fix adds an npm overrides section to ensure the transitive dependency through @electron/get also uses the patched version:

Before:

{
  "dependencies": {
    "electron": "42.3.0",
    "node-abi": "^4.31.0",
    "node-pty": "1.1.0"
  }
}

After:

{
  "dependencies": {
    "electron": "42.3.0",
    "node-abi": "^4.31.0",
    "node-pty": "1.1.0"
  },
  "overrides": {
    "@electron/get": {
      "undici": "7.29.0"
    }
  }
}

This is essential because without the override, @electron/get would continue resolving to the vulnerable undici version regardless of the top-level upgrade. The overrides field in package.json forces npm to substitute the specified version for any matching transitive dependency under @electron/get.

Why Both Changes Are Necessary

  • package-lock.json: Updates the resolved versions and integrity hashes so npm ci installs the patched versions.
  • package.json overrides: Ensures future npm install runs don't regress the transitive dependency back to a vulnerable version.

Prevention & Best Practices

1. Audit Transitive Dependencies Regularly

Direct dependencies are only part of the story. Use npm audit, Trivy, or similar tools to scan your entire dependency tree:

npm audit
trivy fs --scanners vuln .

2. Use npm Overrides for Stubborn Transitive Dependencies

When a direct dependency hasn't updated its own dependency, overrides in package.json lets you force a safe version:

"overrides": {
  "vulnerable-package": ">=patched-version"
}

3. Pin Lock Files and Review Changes

Always commit package-lock.json and review dependency version changes in PRs. Automated tools like Dependabot or Orbis AppSec can flag these proactively.

4. Understand Cache-Control Semantics

If your application implements any caching layer, ensure your parser handles:
- Malformed directives (extra whitespace, null bytes, trailing commas)
- Case variations (Private vs private)
- Quoted-string values with unusual content

5. Defense in Depth

Don't rely solely on Cache-Control for security. Implement additional safeguards:
- Use Vary headers appropriately
- Set no-store for truly sensitive responses
- Validate cached responses before serving

Key Takeaways

  • Malformed Cache-Control: private directives in undici < 7.29.0 bypass cache privacy, potentially exposing sensitive responses to unauthorized recipients.
  • Transitive dependencies require explicit overrides — upgrading only the top-level undici wouldn't patch the @electron/get → undici path without the overrides field in package.json.
  • Build-time HTTP clients are attack surfaces too — even dependencies used only during npm install or Electron packaging can introduce cache poisoning if they fetch resources over HTTP.
  • Lock file integrity hashes changed from sha512-YmfV3Y... to sha512-IDxfle..., confirming the actual binary content of the package was updated, not just metadata.
  • The fix is scoped and safe — only the version pins changed; no application logic was modified, preserving behavior for all valid inputs.

How Orbis AppSec Detected This

  • Source: HTTP response headers received by undici's fetch/request pipeline during dependency resolution and asset downloads
  • Sink: undici's internal cache interceptor parsing logic that evaluates Cache-Control directives to determine cacheability
  • Missing control: Strict validation of malformed Cache-Control: private directive variants before making cache storage decisions
  • CWE: CWE-525 (Use of Web Browser Cache Containing Sensitive Information)
  • Fix: Upgraded undici to 7.29.0 (and 6.28.0 for the v6 line) where the cache interceptor correctly identifies and rejects malformed private directives, and added npm overrides to patch the transitive dependency through @electron/get.

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 that even well-maintained libraries like undici can have subtle parsing flaws with significant security implications. A malformed Cache-Control: private header shouldn't be a vector for cache poisoning, but insufficient input validation in the cache interceptor made it one.

The fix is straightforward — upgrade to patched versions — but the execution requires attention to the full dependency tree. The use of npm overrides to patch transitive dependencies is a pattern every Node.js developer should know. Keep your dependencies current, audit your lock files, and don't assume that build-time dependencies are exempt from security scrutiny.

References

Frequently Asked Questions

What is a Cache-Control parsing bypass vulnerability?

It occurs when an HTTP client or proxy fails to correctly interpret Cache-Control directives, potentially caching responses that should remain private or serving stale/sensitive data to unauthorized recipients.

How do you prevent cache poisoning in Node.js?

Use up-to-date HTTP client libraries, validate Cache-Control headers strictly, implement defense-in-depth with response validation, and regularly audit transitive dependencies for known vulnerabilities.

What CWE is cache mishandling?

CWE-525 (Use of Web Browser Cache Containing Sensitive Information) and CWE-444 (Inconsistent Interpretation of HTTP Requests) are both relevant to cache-related vulnerabilities.

Is upgrading the direct dependency enough to prevent this vulnerability?

Not always — transitive dependencies may pin older vulnerable versions. Using npm overrides (as done in this fix) ensures all instances in the dependency tree are patched.

Can static analysis detect cache handling vulnerabilities?

Yes, tools like Trivy can detect known vulnerable package versions in lock files, and SAST tools can flag improper cache header handling patterns in application code.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1231

Related Articles

critical

How Insecure API Key Transmission Happens in JavaScript Browser Extensions and How to Fix It

A critical vulnerability in `utils/common.js` allowed API keys to be transmitted over unencrypted HTTP connections to remote servers, exposing them to network interception. The `buildModelApiRequest` function at line 490 constructed API requests without validating the transport protocol, enabling man-in-the-middle attacks. The fix enforces HTTPS for all remote API endpoints while preserving HTTP access for local development servers on loopback addresses.

critical

How URL Injection via Unvalidated User Input happens in Node.js and how to fix it

A critical URL injection vulnerability in the QQ info lookup feature allowed attackers to manipulate API request parameters by sending specially crafted messages. Without proper input validation, user-controlled data was directly embedded into external API URLs, potentially exposing sensitive authentication credentials (skey and pskey) to attacker-controlled servers.

high

How Denial of Service via Exponential-Time Complexity Happens in Node.js Dependencies and How to Fix It

A high-severity denial of service vulnerability (CVE-2026-14257) was discovered in the brace-expansion package within the zeroshot-oecp Docker container's dependency tree. The vulnerability allows attackers to craft malicious input patterns that trigger exponential-time processing, potentially freezing or crashing Node.js applications. This fix upgrades the nested brace-expansion dependency to version 5.0.9 using a targeted Dockerfile modification.

medium

How XML Entity Expansion Denial of Service happens in Node.js and how to fix it

A critical denial of service vulnerability (CVE-2026-33036) was discovered in fast-xml-parser versions prior to 5.5.6 and 4.5.5, allowing attackers to bypass entity expansion limits and crash Node.js applications through malicious XML payloads. This fix upgrades the dependency in the scripts directory to patched versions, protecting build pipelines and any runtime XML processing from resource exhaustion attacks.

critical

How Arbitrary Code Execution via Command Injection Happens in Node.js shell-quote and How to Fix It

A critical command injection vulnerability (CVE-2026-9277) was discovered in the popular Node.js `shell-quote` package (versions prior to 1.8.4) where unescaped line terminators allowed attackers to inject and execute arbitrary shell commands. The fix upgrades `shell-quote` from version 1.8.1 to 1.8.4, which properly escapes line terminator characters (such as `\n`, `\r`, `\u2028`, and `\u2029`) before passing strings to the shell. This dependency was present in the project's `package-lock.json`

high

How Octal/Decimal IP Parsing Ambiguity happens in JavaScript and how to fix it

CVE-2026-69192 is a high-severity vulnerability in the `ip-address` npm package (versions before 10.3.1) where IPv4 addresses with leading-zero octets — like `010.0.0.1` — are parsed as decimal by the library but interpreted as octal by OS-level resolvers, creating a dangerous mismatch. This discrepancy can allow attackers to bypass IP-based access controls and trust boundaries, potentially enabling Server-Side Request Forgery (SSRF) attacks. Upgrading to `ip-address@10.3.1` in the SAP BW Query