← Back to Feed CACHED · 2026-09-04 17:52:54 · CACHE_KEY CVE-2026-84961
CVE-2026-84961 · CWE-295 · Disclosed 2026-09-04

undici's BalancedPool constructor passes its entire options object through an internal deep-clone that…

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

Your custom lock was installed but the deadbolt was quietly left out during assembly

CVE-2026-84961 affects undici's BalancedPool class in versions 7.24.1 through 7.29.0 and 8.0.0 through 8.10.1. The constructor passes its options object through an internal deep-clone that uses JSON serialization. Because JSON.stringify cannot represent functions, any caller-supplied connect or tls option containing a checkServerIdentity callback, a custom ca certificate chain expressed via a function reference, or similar non-serializable TLS settings is silently dropped before reaching the TLS layer. The connection then falls back to Node.js's default Mozilla CA trust store, accepting certificates that the application explicitly intended to reject.

The vendor rated this HIGH (7.4) with AC:H, which is honest about the MITM prerequisite but overstates the real-world blast radius. The bug only fires when three conditions align simultaneously: the app uses BalancedPool specifically (not Pool, Client, or Agent), it passes function-valued TLS options, *and* an attacker holds a man-in-the-middle position. The intersection of those three conditions in production is narrow. Most undici consumers use fetch(), request(), or plain Pool — none of which route through the broken clone path. The vendor severity is fair for the theoretical ceiling but oversells the practical risk for typical Node.js deployments.

"BalancedPool silently drops TLS callbacks, but MITM + niche usage pattern keeps real-world risk moderate"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Target uses BalancedPool with custom TLS validation

The target Node.js application must instantiate BalancedPool and pass a connect or tls option containing function-valued properties such as checkServerIdentity or a dynamic ca loader. This is the only class affected — Pool, Client, Agent, and the global fetch() API are not vulnerable. This pattern is most common in microservices doing certificate pinning or connecting to endpoints secured by an internal CA.
Conditions required:
  • Target application uses undici BalancedPool
  • Application passes function-valued TLS options (checkServerIdentity, ca callback)
  • Affected undici version (7.24.1–7.29.0 or 8.0.0–8.10.1)
Where this breaks in practice:
  • BalancedPool is a specialized class; most undici usage goes through fetch(), request(), or Pool
  • Custom TLS validation callbacks are a niche pattern, primarily used in zero-trust or cert-pinning architectures
  • Default undici usage inherits Node.js's standard TLS behavior, which is unaffected
Detection/coverage: npm audit / yarn audit will flag the vulnerable undici version. SBOM scanners (Snyk, Socket, Dependabot) detect the library version.
STEP 02

Attacker achieves man-in-the-middle position

The attacker must intercept network traffic between the Node.js application and its upstream origin(s). This requires control of a network segment (e.g., compromised switch, ARP spoofing on the LAN, rogue Wi-Fi, BGP hijack, or DNS poisoning). Cloud-to-cloud traffic over TLS typically transits provider backbones where MITM is significantly harder.
Conditions required:
  • Network-level access between application and upstream origin
  • Ability to intercept and modify TCP/TLS handshake
Where this breaks in practice:
  • MITM on production server-to-server traffic is substantially harder than client-to-server
  • Cloud provider backbone and VPC isolation reduce the viable attack surface
  • Network detection tools (IDS/IPS, anomaly detection) may flag ARP spoofing or DNS hijack attempts
Detection/coverage: Network IDS (Suricata, Zeek) can detect ARP spoofing and DNS anomalies. Certificate transparency logs may flag unexpected certificates.
STEP 03

Present valid-looking certificate from public CA

With the custom checkServerIdentity callback silently dropped, the TLS stack falls back to Node.js's default trust store (Mozilla CA bundle). The attacker presents a certificate for the target hostname signed by any publicly trusted CA. Since the application intended to pin to a specific CA or validate additional properties, this certificate would normally be rejected — but the dropped callback means it is accepted.
Conditions required:
  • Attacker obtains a certificate for the target hostname from any public CA
  • Domain validation or other CA issuance succeeds
Where this breaks in practice:
  • Obtaining a legitimate certificate for an internal hostname is non-trivial
  • Many internal services use non-routable hostnames that public CAs will not issue certificates for
  • Certificate Transparency logs would record the issuance, enabling detection
Detection/coverage: Certificate Transparency monitoring (e.g., crt.sh, Facebook CT monitor) can detect unexpected certificate issuance for monitored domains.
STEP 04

Intercept or modify TLS-protected traffic

With the MITM established and the fraudulent certificate accepted, the attacker can decrypt, inspect, and optionally modify all traffic between the application and upstream. This could expose API keys, authentication tokens, PII, or allow injection of malicious responses. The blast radius is limited to the specific BalancedPool connection — other connections in the same application are unaffected.
Conditions required:
  • Successful MITM with accepted certificate
  • Target connection carries sensitive data
Where this breaks in practice:
  • Blast radius is limited to the BalancedPool connections, not all application traffic
  • Application-layer authentication (mutual TLS client certs, signed requests) may still detect tampering
  • Response integrity checks or HMAC-signed payloads would fail
Detection/coverage: Application-level anomaly detection may flag unexpected response content. Mutual TLS (if configured separately) would still fail at the client-cert step.
03 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNot observed. No reports from CISA, GreyNoise, or vendor advisories indicate active exploitation. Not listed on CISA KEV.
Proof of conceptNo public PoC. The advisory describes the technical mechanism (JSON serialization drops functions) but no weaponized exploit or reproduction script has been published. Reported by MegaManSec.
EPSS scoreNot yet scored — CVE disclosed 2026-09-04; EPSS typically populates within 7-14 days. Expected to be low given the MITM + niche usage prerequisites.
KEV statusNot listed on CISA Known Exploited Vulnerabilities catalog as of 2026-09-05.
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N — Network-accessible but high attack complexity (MITM required). No privileges or user interaction needed. High confidentiality and integrity impact, no availability impact. Scope unchanged.
Affected versionsundici 7.24.1–7.29.0 and 8.0.0–8.10.1. Earlier major versions (5.x, 6.x) do not contain the vulnerable deep-clone path in BalancedPool.
Fixed versionsundici 7.29.1 and 8.10.2. Node.js core bundles undici — check process.versions.undici to see if your Node.js build ships a patched version.
Exposure footprintundici has ~150M+ weekly npm downloads. However, BalancedPool usage with custom TLS callbacks represents a small fraction. No Shodan/Censys exposure data applies (library-level bug, not a listening service).
Disclosure date2026-09-04 via GHSA-w293-vg96-wgc3.
Reporter / creditsReported by MegaManSec; patched by mcollina, reviewed by UlisesGascon.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The single most decisive downgrade factor is the narrow intersection of prerequisites: the application must use BalancedPool specifically (not the far more common Pool, Client, or fetch()), must pass function-valued TLS options, and the attacker must hold an active MITM position. Each prerequisite compounds to shrink the reachable population to a small fraction of undici's 150M+ weekly install base.

HIGH Vulnerability mechanism (JSON serialization drops functions) is well-documented in the advisory
MEDIUM Fraction of deployments using BalancedPool with custom TLS callbacks (estimated <5% of undici consumers)
LOW EPSS score and future exploitation likelihood (CVE is 1 day old)

Why this verdict

  • Narrow affected surface: Only BalancedPool with function-valued connect/tls options is affected. The dominant undici usage patterns (fetch(), request(), Pool, Client, Agent) are unaffected. This shrinks the vulnerable population to a small percentage of the 150M+ weekly install base.
  • MITM prerequisite (AC:H): Exploiting this requires an active man-in-the-middle position on production server-to-server traffic. In cloud-native deployments (the dominant undici environment), this means compromising VPC networking or provider backbone — a high bar that implies the attacker already has significant access.
  • No exploitation evidence: No in-the-wild exploitation, no public PoC, no KEV listing. The vulnerability was disclosed responsibly with patches available same-day. The theoretical attack is well-understood but has not been demonstrated.
  • Role multiplier: undici's BalancedPool *could* be used in high-value roles (API gateways, service mesh proxies, CI/CD pipeline HTTP clients). In these roles, a successful MITM could expose secrets or enable supply-chain injection. However, (a) BalancedPool with custom TLS pinning is an uncommon pattern even in these roles, and (b) the MITM prerequisite means the attacker is already inside the network. The worst plausible high-value outcome is per-connection data interception, not fleet compromise or domain takeover — blast radius is connection-scoped, not host- or domain-scoped. This does not trigger the HIGH floor.
  • Silent failure mode is concerning: The callback is dropped *silently* with no error or warning logged, which means affected applications have no indication their TLS validation is ineffective. This is the primary reason the verdict remains MEDIUM rather than LOW.

Why not higher?

The vendor's HIGH rating assumes the attacker can achieve MITM and the target uses the exact affected code path. In practice, the intersection of BalancedPool usage, custom TLS callbacks, and attainable MITM position is narrow. The blast radius is limited to specific connections within one application — there is no lateral movement, privilege escalation, or fleet-scale impact from this vulnerability alone. No high-value role deployment pattern triggers the HIGH floor because the outcome is connection-scoped interception, not domain or fleet compromise.

Why not lower?

The silent failure mode is genuinely dangerous for the applications that *do* use this pattern. Certificate pinning and custom CA validation are security controls deployed specifically in high-sensitivity contexts (financial APIs, healthcare data exchanges, zero-trust microservices). Silently removing those controls without any error undermines the application's security assumptions in exactly the scenarios where TLS validation matters most. The absence of any runtime warning makes detection difficult without explicit version auditing.

05 · Compensating Control

What to do — in priority order.

  1. Switch from BalancedPool to Pool or Agent for TLS-pinned connections — The vulnerability only affects BalancedPool. Replacing it with Pool (single origin) or Agent (automatic connection management) preserves your custom TLS callbacks. This is the fastest mitigation — deploy within the noisgate MEDIUM remediation window of 365 days, though earlier is better if you have confirmed BalancedPool + custom TLS in your codebase.
  2. Audit your codebase for BalancedPool + function-valued TLS options — Search for new BalancedPool in your Node.js projects and check whether the options object contains checkServerIdentity, ca as a function, or other non-serializable TLS properties. If none are found, this CVE does not affect you.
  3. Pin undici version in package-lock.json and upgrade to 7.29.1 or 8.10.2 — The patch is available. Run npm audit fix or explicitly update undici. Verify with npm ls undici that no transitive dependency pulls in a vulnerable version.
  4. Enable Certificate Transparency monitoring for your domains — If your application relies on certificate pinning, CT monitoring (crt.sh, Google CT) provides a detection layer for unexpected certificate issuance that would be needed for exploitation.
What doesn't work
  • WAF / API Gateway in front of the application — This vulnerability is in the application's *outbound* connections to upstream origins, not in inbound request handling. A WAF inspects inbound traffic and cannot see or control the application's outbound TLS handshakes.
  • Network-level TLS inspection (SSL proxy) — A TLS-inspecting proxy between the app and its upstreams would replace the TLS session entirely, but it would also break the custom certificate validation the app was trying to do in the first place. It masks the bug without fixing it.
  • Upgrading Node.js alone — While Node.js bundles undici, many applications install undici as a direct dependency. Upgrading Node.js may not update the application's pinned undici version. Check npm ls undici to confirm which version your application actually loads.
06 · Verification

Crowdsourced verification payload.

Run this on any host where your Node.js application is deployed or in your CI pipeline. Execute with bash check_cve_2026_84961.sh /path/to/your/project. No special privileges required — it reads node_modules and package-lock.json.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_84961.sh — Detect undici versions vulnerable to CVE-2026-84961
# Usage: bash check_cve_2026_84961.sh [/path/to/project]
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

PROJECT_DIR="${1:-.}"
VULNERABLE=0

if [ ! -d "$PROJECT_DIR/node_modules" ]; then
  echo "UNKNOWN — no node_modules found in $PROJECT_DIR"
  exit 2
fi

# Find all installed undici versions
find "$PROJECT_DIR/node_modules" -path '*/undici/package.json' -not -path '*/node_modules/.cache/*' | while read -r pkg; do
  version=$(grep -o '"version": *"[^"]*"' "$pkg" | head -1 | grep -o '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*')
  if [ -z "$version" ]; then
    continue
  fi
  major=$(echo "$version" | cut -d. -f1)
  minor=$(echo "$version" | cut -d. -f2)
  patch=$(echo "$version" | cut -d. -f3)

  vuln="no"
  # Check 7.24.1 through 7.29.0
  if [ "$major" -eq 7 ]; then
    if [ "$minor" -gt 24 ] && [ "$minor" -lt 29 ]; then
      vuln="yes"
    elif [ "$minor" -eq 24 ] && [ "$patch" -ge 1 ]; then
      vuln="yes"
    elif [ "$minor" -eq 29 ] && [ "$patch" -eq 0 ]; then
      vuln="yes"
    fi
  fi
  # Check 8.0.0 through 8.10.1
  if [ "$major" -eq 8 ]; then
    if [ "$minor" -lt 10 ]; then
      vuln="yes"
    elif [ "$minor" -eq 10 ] && [ "$patch" -le 1 ]; then
      vuln="yes"
    fi
  fi

  if [ "$vuln" = "yes" ]; then
    echo "VULNERABLE — undici $version found at $pkg"
    exit 1
  else
    echo "PATCHED — undici $version found at $pkg"
  fi
done

# If the loop didn't exit, check if undici was found at all
if ! find "$PROJECT_DIR/node_modules" -path '*/undici/package.json' -not -path '*/node_modules/.cache/*' | grep -q .; then
  echo "UNKNOWN — undici not found in $PROJECT_DIR"
  exit 2
fi

exit 0
07 · Bottom Line

If you remember one thing.

TL;DR
This CVE affects a narrow slice of undici users: those running BalancedPool with function-valued TLS options on versions 7.24.1–7.29.0 or 8.0.0–8.10.1. Monday morning: run npm ls undici across your Node.js fleet and grep -r 'BalancedPool' src/ in your codebases. If you find the affected pattern, upgrade to undici 7.29.1 or 8.10.2. At a noisgate-reassessed MEDIUM, there is no mitigation SLA — go straight to the 365-day noisgate remediation SLA for the patch. However, if your specific application uses BalancedPool with certificate pinning for a security-critical integration (payment processors, identity providers), treat it as locally elevated and patch within 30 days. For the majority of Node.js deployments that use fetch(), Pool, or Agent, this CVE requires no action beyond confirming you are not affected.

Sources

  1. GitHub Advisory GHSA-w293-vg96-wgc3
  2. undici Releases — nodejs/undici
  3. undici on npm
  4. Snyk — undici package security
  5. Socket.dev — undici security analysis
  6. SecurityOnline — Four undici Vulnerabilities
  7. FIRST EPSS FAQ
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.