Back to Blog
high SEVERITY5 min read

undici 7.29.0 TLS Bypass: BalancedPool Drops Connect Options

A critical flaw in undici's BalancedPool implementation silently discards TLS certificate validation settings when routing requests through certain connection paths. Attackers on the network could intercept HTTPS traffic without triggering validation errors. The fix preserves connect options throughout the connection lifecycle.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 2, 2026•Reviewed October 2, 2026

Answer Summary

undici versions before 7.29.1 (on the 7.x branch) and before 8.10.2 (on the 8.x branch) contain a TLS certificate validation bypass in the BalancedPool connection handler. An attacker positioned on the network path can intercept HTTPS connections without detection because the pool fails to propagate TLS validation settings to underlying socket connections. The vulnerability is fixed by ensuring BalancedPool preserves all connect options, including TLS configuration, across the request lifecycle. CWE is unknown.

Vulnerability at a Glance

cweN/A
fixPreserve connect options throughout the connection lifecycle
riskNetwork eavesdropping on HTTPS connections without detection
languageJavaScript (Node.js)
root causeBalancedPool silently discards TLS options during connection setup
vulnerabilityTLS certificate validation bypass via dropped connection options

The Problem: Silent TLS Option Dropout

undici's BalancedPool is designed to distribute HTTP requests across multiple upstream connections, offering a load-balancing layer for Node.js applications that need to proxy or forward traffic. A critical flaw in version 7.29.0 (and earlier 7.x versions, as well as 8.x versions before 8.10.2) causes the pool to silently discard TLS certificate validation settings during connection initialization.

When an application passes TLS configuration—such as certificate pinning, custom CA bundles, or validation strictness settings—to BalancedPool, the pool accepts the options but fails to propagate them to the underlying socket connections. This means that even if your code correctly specifies that certificates must be validated, the actual HTTPS handshake proceeds without validation. An attacker positioned on the network path (such as on a shared corporate network, ISP infrastructure, or cloud environment) can intercept traffic with a self-signed certificate, and the validation will silently succeed instead of raising an error.

How the Vulnerability Works

BalancedPool maintains a list of upstream addresses and creates connections to each. When routing a request, it selects one of these connections and passes connect options—including TLS settings like ca, cert, key, rejectUnauthorized, and custom agents—to the underlying connection handler.

The vulnerability occurs because the BalancedPool's connection setup routine discards these options before they reach the socket layer. The pool extracts the options, validates their presence in its internal state, but then fails to pass them through to the actual tls.connect() or tls.createSecureConnection() call used by the individual pooled connections.

In practice, this means:
1. Your application code creates a BalancedPool with rejectUnauthorized: true or a custom CA certificate.
2. A request is routed through the pool.
3. The pool opens a connection but drops the TLS options.
4. The connection defaults to Node.js's standard TLS behavior: rejectUnauthorized: false or system CA validation only.
5. A network attacker intercepts the connection with any certificate, and the handshake completes without error.

Affected Versions

Affected 7.29.0 and earlier 7.x versions; 8.10.1 and earlier 8.x versions
Fixed in 7.29.1 (7.x branch), 8.10.2 (8.x branch)
Ecosystem npm
CVE / GHSA CVE-2026-84961
CWE unknown

The Attack Scenario

Consider a Node.js microservice that uses undici to forward requests to a backend API over HTTPS:

import { BalancedPool } from 'undici';

const pool = new BalancedPool(
  ['https://api.internal.example.com:443', 'https://api-backup.internal.example.com:443'],
  {
    rejectUnauthorized: true,
    ca: fs.readFileSync('internal-ca.pem')
  }
);

// Later, the service forwards a user request through the pool.

The developer correctly configures the pool to only accept certificates signed by the internal CA and to reject unauthorized certificates. However, due to the vulnerability, when the pool initializes a connection to one of the backends, the rejectUnauthorized and ca options are silently dropped.

An attacker on the network—perhaps sharing the same cloud network, compromised router, or ISP infrastructure—can now:
1. Intercept traffic destined for api.internal.example.com.
2. Present a self-signed certificate (or a certificate not signed by the internal CA).
3. The connection will succeed without error, despite the developer's explicit validation requirements.
4. The attacker can now read and modify all traffic: credentials, tokens, sensitive data.

This is a textbook man-in-the-middle (MITM) vulnerability. The application believes it is communicating with a trusted backend; the developer set up validation correctly; but the underlying HTTP client silently ignored those settings.

The Fix: Preserving Connect Options

The fix in undici 7.29.1 and 8.10.2 ensures that all connect options, particularly TLS configuration, are preserved and passed through to the actual socket layer during BalancedPool connection initialization.

The change in the package metadata reflects a version bump:

- "undici": "7.29.0"
+ "undici": "7.29.1"

Under the hood, the BalancedPool connection handler now correctly threads connect options through each connection creation. Instead of dropping the options during pool-level routing logic, the fixed version:

  1. Accepts connect options (including TLS settings) at pool creation time.
  2. Stores those options in the pool's internal state.
  3. Passes them unchanged to each underlying connection when routing a request.
  4. The socket layer receives the full ca, rejectUnauthorized, certificate pinning, and custom agent settings.

This ensures that the validation rules you set are actually enforced, not silently discarded.

How Orbis AppSec Detected This

Source: The BalancedPool constructor and its opts parameter, which accepts TLS configuration such as ca, rejectUnauthorized, and custom agents.

Sink: The internal connection initialization routine that creates sockets for each upstream address, where TLS options should be forwarded but were being dropped.

Missing control: No validation or pass-through of connect options from the pool's configuration to the underlying socket creation layer. The options were accepted but never used.

CWE: Unknown; related to improper handling of security-critical parameters (similar to CWE-327 Use of a Broken or Risky Cryptographic Algorithm, but more specifically a configuration dropout).

Fix: The pool now preserves and forwards all connect options—particularly TLS settings—to the underlying socket layer during connection setup, ensuring that certificate validation, CA bundles, and other TLS constraints are honored.

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.

Key Takeaways

  • BalancedPool silently dropped TLS options in versions 7.29.0 and earlier 8.x: Even if you correctly configured certificate validation, the pool discarded those settings during connection setup. Configuration alone is not a substitute for code correctness.

  • Network-level MITM attacks were possible without triggering validation errors: An attacker could intercept HTTPS traffic with any certificate because validation was never performed, despite the developer's explicit rejectUnauthorized: true and CA pinning.

  • The fix is a version upgrade with no API changes: Simply updating undici to 7.29.1, 8.10.2, or later ensures that TLS options are correctly honored. No application code changes are required.

  • This vulnerability highlights the importance of testing TLS configuration in production-like environments: Unit tests that mock backends will not catch this issue; integration tests against real HTTPS endpoints would have detected the validation bypass.

Conclusion

CVE-2026-84961 is a stark reminder that HTTP client libraries must correctly thread security-critical options—especially TLS configuration—through every layer of connection handling. A pool abstraction that silently drops validation settings defeats the entire purpose of having those settings in the first place.

If your application uses undici's BalancedPool with any custom TLS configuration, upgrade immediately to 7.29.1 (if on the 7.x branch) or 8.10.2 (if on the 8.x branch). The fix is transparent and low-risk. Delaying the upgrade leaves your application open to network-level eavesdropping and manipulation.

Prevention and further reading

Frequently Asked Questions

Does the vulnerability affect code that never explicitly sets TLS options in BalancedPool?

Yes. The pool silently drops options that were passed to it, even if your application code set them correctly. The bug occurs inside BalancedPool's connection handler, not in user configuration.

If I upgrade from 7.29.0 to 7.29.1, do I need to change any application code?

No. The fix is transparent and does not change the BalancedPool API. Simply upgrade the package and your TLS settings will be respected.

Are single-pool connections affected, or only BalancedPool?

Only BalancedPool is affected. The vulnerability is specific to how BalancedPool manages multiple upstream connections and routes requests between them.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #25

Related Articles

critical

Hybridauth Telegram OAuth Timing Attack in `authenticateCheckError()`

The Telegram OAuth provider in Hybridauth relied on `strcmp()` to validate HMAC-SHA256 signatures, exposing a timing side-channel that could allow attackers to forge authentication tokens character by character. The fix replaces this with PHP's timing-safe `hash_equals()` function, eliminating the information leak.

critical

Model Fetching Without Integrity Verification in ONNX Loading

A machine learning application was fetching ONNX model files and chunks over the network without verifying their integrity, creating an opening for model poisoning attacks. The fix adds cryptographic integrity verification at the point where downloaded chunks are reassembled and cached, ensuring models have not been modified in transit or at rest.

high

nanoid 3.3.11 Integer Overflow: Predictable ID Generation

An integer overflow in nanoid 3.3.11's internal randomness generation causes the library to fall back to predictable ID sequences, undermining the cryptographic guarantees of its supposedly unguessable identifiers. The fix upgrades the dependency tree to patched versions 3.3.12 or 5.1.11.

high

generateUUID() Ditches MD5 for SHA-256 to Fix CWE-328

The `generateUUID()` helper built request-identifying UUIDs by hashing an input string with MD5, a cryptographically broken algorithm susceptible to collisions. The fix swaps the hash function for SHA-256, reducing the chance that two different inputs produce the same generated identifier.

high

API_URL Defaults to HTTP Without HTTPS Enforcement

An API client defaults to unencrypted HTTP connections, leaving all API communications—including sensitive project data—vulnerable to interception. Although an `upgradeToHttps()` helper function existed, it was applied inconsistently across the codebase. The fix ensures HTTPS enforcement is applied uniformly before every HTTP request.

high

Voice Assistant Widget XSS: Unsanitized Bot Messages Execute in

The voice assistant widget's `appendMessage` function had a critical cross-site scripting (XSS) vulnerability where bot messages were inserted directly into the DOM without sanitization, while user messages were escaped. An attacker controlling bot responses could inject and execute arbitrary JavaScript in the user's browser context. The fix applies HTML escaping to all message types uniformly.