CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-109383

CVE-2026-109383: Improper Certificate Validation in Node.js Undici Agent Dispatcher

Alon Barad
Alon Barad
Software Engineer

Oct 27, 2026·10 min read·6 visits

Executive Summary (TL;DR)

The Node.js undici HTTP client silently drops custom TLS validation functions (such as checkServerIdentity) passed via the Agent({ tls: { ... } }) option because it uses JSON.parse(JSON.stringify()) to clone options. This bypasses custom certificate pinning, allowing network attackers with valid CA-signed certificates to intercept traffic. Upgrade to undici 6.29.1, 7.30.1, or 8.12.2, or pass TLS callbacks via the connect option instead of tls.

CVE-2026-109383 (GHSA-2m5v-pch6-74hx) is an improper certificate validation vulnerability (CWE-295) in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js. In affected versions, the Agent constructor clones configuration options using a JSON-based deep clone function (util.deepClone), which executes JSON.parse(JSON.stringify(options)). Because JSON serialization omits function properties, custom certificate validation callbacks passed via the tls configuration option—such as tls.checkServerIdentity—are silently stripped before being passed to origin connection pools. Consequently, connections fall back to standard Node.js TLS validation, bypassing custom certificate pinning or hostname verification rules and allowing an Adversary-in-the-Middle (AitM) with a CA-trusted certificate to intercept and modify traffic.

Vulnerability Overview

undici is the official HTTP/1.1 and HTTP/2 client library maintained by the Node.js project. Beyond standalone package usage via undici.request() and undici.Agent, it serves as the foundational networking engine powering the built-in global fetch() API and global dispatchers across modern Node.js runtimes. Within the library's architecture, the Agent class acts as the primary dispatcher responsible for routing outbound requests to origin-specific connection pools (Pool and Client instances).

When establishing HTTPS connections, Node.js developers frequently configure custom TLS parameters on the Agent instance to enforce strict transport security policies. A common defensive pattern in high-security microservices and API clients is certificate pinning or custom Subject Alternative Name (SAN) verification, implemented by passing a custom checkServerIdentity(servername, cert) callback function inside the tls configuration object.

In undici versions 4.0.0 through 6.29.0, 7.0.0 through 7.30.0, and 8.0.0 through 8.12.1, a flaw in how the Agent constructor clones incoming configuration options causes all function-valued properties within the tls options dictionary to be silently discarded. Tracked as CVE-2026-109383 (GHSA-2m5v-pch6-74hx) and classified under CWE-295 (Improper Certificate Validation), this defect disables custom certificate verification callbacks without raising a warning or runtime error.

Because the custom checkServerIdentity callback is omitted before the options reach the underlying Node.js tls.connect() socket layer, the runtime reverts to its default certificate verification routine. While standard Certificate Authority (CA) chain validation and basic hostname matching remain active, any supplemental restrictions defined in the dropped callback—such as leaf certificate fingerprint pinning or SPKI pinning—are bypassed. An Adversary-in-the-Middle (AitM) capable of presenting a certificate signed by any CA in the runtime's trust store can therefore intercept, decrypt, and modify TLS traffic that the application was explicitly configured to reject.

Root Cause Analysis

The root cause of CVE-2026-109383 resides in the option normalization and cloning logic inside lib/dispatcher/agent.js. When a new Agent instance is initialized, the constructor accepts an options object containing dispatcher settings (maxOrigins, factory), socket connection parameters (connect), and legacy or direct TLS settings (tls). To prevent external mutations of the caller's configuration object from affecting internal dispatcher state after instantiation, Agent creates a deep copy of the residual options.

In the vulnerable implementation, the constructor signature destructured factory, maxOrigins, and connect explicitly, while capturing all remaining configuration keys—including tls—via the rest parameter ...options. While the connect parameter was explicitly checked and shallow-copied (connect = { ...connect }), the rest object options was passed directly into the internal utility method util.deepClone(options).

In lib/core/util.js, deepClone is implemented as a wrapper around standard JSON serialization:

function deepClone (obj) {
  return JSON.parse(JSON.stringify(obj))
}

According to the ECMAScript specification for JSON.stringify(), JavaScript Function objects, Symbol values, and undefined properties are not valid JSON data types. When JSON.stringify() encounters a property whose value is a function during object traversal, it silently omits the key-value pair from the serialized JSON string. When JSON.parse() reconstructs the object, any function callbacks originally present inside options.tls have been completely stripped.

When the Agent subsequently invokes this[kFactory](origin, this[kOptions]) to instantiate a per-origin Pool or Client, the sanitized this[kOptions].tls object is forwarded down the call stack. Serializable primitives and buffers (such as ca, cert, key, and servername strings) survive the serialization round-trip intact, masking the bug during normal operation. However, executable hooks like checkServerIdentity and pskCallback are lost, severing the custom validation chain.

Code Analysis

Examining the vulnerable constructor in lib/dispatcher/agent.js prior to commit f75eac00ac177f7b2e6c8bfbd52d4113164286e6 highlights the discrepancy between how connect options and tls options were handled:

// Vulnerable code in lib/dispatcher/agent.js
class Agent extends DispatcherBase {
  constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, ...options } = {}) {
    if (typeof factory !== 'function') {
      throw new InvalidArgumentError('factory must be a function.')
    }
 
    if (maxOrigins !== null && (!Number.isInteger(maxOrigins) || maxOrigins <= 0)) {
      throw new InvalidArgumentError('maxOrigins must be a number greater than 0.')
    }
 
    if (connect && typeof connect !== 'function') {
      connect = { ...connect }
    }
 
    // BUG: 'tls' is inside 'options'. util.deepClone strips all functions.
    this[kOptions] = { ...util.deepClone(options), maxOrigins, connect }
    this[kFactory] = factory
    this[kClients] = new Map()
    this[kOrigins] = new Set()
  }

The patch in commit f75eac00ac177f7b2e6c8bfbd52d4113164286e6 resolves the issue by extracting tls explicitly in the constructor's parameter destructuring list, matching the pattern already used for connect. If tls is provided and is not itself a function, a shallow object copy (tls = { ...tls }) is performed using the object spread operator, and the resulting reference is merged into this[kOptions] after util.deepClone(options) has executed:

File: lib/dispatcher/agent.js
@@ -22,7 +22,7 @@ function defaultFactory (origin, opts) {
 }
 
 class Agent extends DispatcherBase {
-  constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, ...options } = {}) {
+  constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, tls, ...options } = {}) {
     if (typeof factory !== 'function') {
       throw new InvalidArgumentError('factory must be a function.')
     }
@@ -41,7 +41,11 @@ class Agent extends DispatcherBase {
       connect = { ...connect }
     }
 
-    this[kOptions] = { ...util.deepClone(options), maxOrigins, connect }
+    if (tls && typeof tls !== 'function') {
+      tls = { ...tls }
+    }
+
+    this[kOptions] = { ...util.deepClone(options), maxOrigins, connect, tls }
     this[kFactory] = factory
     this[kClients] = new Map()
     this[kOrigins] = new Set()

To verify the fix, the maintainers added a unit test in test/node-test/agent.js that spins up a local HTTPS server and configures an Agent with a tls.checkServerIdentity callback that increments a counter and unconditionally returns a custom Error:

test('forwards TLS certificate validation callbacks via tls option', async (t) => {
  const expectedError = new Error('rejected by custom checkServerIdentity')
  let identityChecks = 0
  const agent = new Agent({
    tls: {
      ca: fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'ca.pem'), 'utf8'),
      servername: 'agent1',
      checkServerIdentity () {
        identityChecks++
        return expectedError
      }
    }
  })
  t.after(() => agent.close())
 
  await assert.rejects(
    request(`https://localhost:${server.address().port}`, { dispatcher: agent }),
    /rejected by custom checkServerIdentity/
  )
  assert.strictEqual(identityChecks, 1)
})

Regarding fix completeness, shallow-copying tls via { ...tls } preserves top-level function properties on the tls configuration object, which covers all standard Node.js tls.connect() callback hooks (checkServerIdentity, pskCallback, lookup). In downstream Client (lib/dispatcher/client.js) and connect (lib/core/connect.js) implementations, tls and connect are already combined via shallow object spread ({ ...tls, ...connect }), meaning no secondary deepClone call occurs further down the dispatcher pipeline.

Exploitation

Exploiting CVE-2026-109383 requires a specific combination of application configuration and network positioning, reflected in the CVSS High Attack Complexity (AC:H) metric. First, the target Node.js application must use an affected version of undici and configure custom certificate validation via new Agent({ tls: { checkServerIdentity: ... } }) (or via a dispatcher that delegates to Agent, such as setGlobalDispatcher or EnvHttpProxyAgent on direct routes). Second, the attacker must occupy an Adversary-in-the-Middle (AitM) position between the Node.js client and the target HTTPS server, such as via BGP hijacking, DNS spoofing, ARP poisoning, or control over a corporate/cloud proxy.

Crucially, because dropping checkServerIdentity causes Node.js to fall back to default TLS validation rather than disabling TLS validation altogether (rejectUnauthorized remains true by default), the attacker cannot simply present a self-signed certificate. To succeed, the attacker must present a leaf certificate that is valid for the target domain and chains up to a Root or Intermediate Certificate Authority present in the Node.js runtime's trust store (for example, a compromised public CA, a mis-issued domain certificate, or a corporate TLS inspection root CA installed on the host).

Consider a financial service backend written in Node.js that communicates with an external payment gateway. To defend against rogue CAs or unauthorized corporate proxy interception, the developers pin the SHA-256 fingerprint of the gateway's leaf certificate using tls.checkServerIdentity:

const crypto = require('node:crypto');
const { Agent, request } = require('undici');
 
const PINNED_FINGERPRINT = 'SHA256:8F:3A:...';
 
const agent = new Agent({
  tls: {
    checkServerIdentity(hostname, cert) {
      if (cert.fingerprint256 !== PINNED_FINGERPRINT) {
        return new Error('Certificate pinning check failed');
      }
    }
  }
});
 
// In vulnerable versions, checkServerIdentity is never executed
await request('https://api.payment-gateway.example/v1/transfer', {
  dispatcher: agent,
  method: 'POST',
  body: JSON.stringify({ amount: 10000, recipient: 'acct_123' })
});

When the application executes request(), the Agent spawns a Pool for https://api.payment-gateway.example using the cloned options from this[kOptions]. Because checkServerIdentity was stripped during Agent construction, tls.connect() validates the attacker's intercepted connection using only the default Node.js tls.checkServerIdentity(hostname, cert). Since the attacker's certificate matches api.payment-gateway.example and chains to a trusted root CA, the TLS handshake succeeds. The application transmits sensitive HTTP headers, bearer tokens, and request bodies in plaintext to the attacker.

Impact Assessment

CVE-2026-109383 is assigned a CVSS v3.1 base score of 7.4 (High) with the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N. The Attack Vector is Network (AV:N), requiring no privileges (PR:N) and no user interaction (UI:N). The Attack Complexity is rated High (AC:H) because successful exploitation depends on both the presence of a network adversary and the attacker's ability to present a certificate trusted by the underlying system or Node.js CA bundle.

When these preconditions are met, the Confidentiality (C:H) and Integrity (I:H) impacts are High. An attacker who bypasses certificate pinning can decrypt all outbound requests and inbound responses over the compromised TLS session, exposing API keys, session credentials, and sensitive application data. Furthermore, the attacker can modify HTTP payloads in transit, injecting malicious responses or altering transaction parameters.

Availability Impact is rated None (A:N), as the vulnerability does not provide a mechanism to crash the Node.js process or exhaust system resources beyond what an AitM attacker could already achieve by dropping network packets. At the time of writing, the EPSS score for CVE-2026-109383 is 0.00184 (7.93rd percentile), and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The low EPSS score aligns with the specific configuration precondition required for exposure.

Remediation

The primary remediation for CVE-2026-109383 is to upgrade undici to a patched release that preserves the tls configuration object without passing it through util.deepClone(). The OpenJS Foundation and undici maintainers have released fixes across all supported major release branches:

  • For undici 6.x (and legacy 4.x/5.x users): Upgrade to undici >= 6.29.1 (fixed via commit 8c59f6b302898cb2e9b0f7962cd7212b6e597c9f).
  • For undici 7.x: Upgrade to undici >= 7.30.1 (fixed via commit 04cb74b1db131ff8f5b4557dce811cbf5f4664b4).
  • For undici 8.x: Upgrade to undici >= 8.12.2 (fixed via commit f75eac00ac177f7b2e6c8bfbd52d4113164286e6).

> [!NOTE] > Applications that rely on the built-in global fetch() or undici bundled inside the Node.js runtime binary should monitor Node.js security releases and update their Node.js runtime to a version that incorporates the patched undici dependency.

For environments where upgrading undici or the Node.js runtime cannot be performed immediately, a reliable configuration workaround exists. In vulnerable versions of Agent, the connect option dictionary is already destructured prior to util.deepClone(options) and shallow-copied via connect = { ...connect }. Furthermore, undici deprecates the top-level tls option in favor of passing TLS settings directly inside connect.

Developers can mitigate the vulnerability immediately without upgrading by moving checkServerIdentity and all other TLS properties from the tls key into the connect key when instantiating Agent:

// SAFE WORKAROUND: Use 'connect' instead of 'tls'
const agent = new Agent({
  connect: {
    ca: customCaCert,
    servername: 'example.com',
    checkServerIdentity(servername, cert) {
      // Callback is preserved across all undici versions
      return customPinningValidation(servername, cert);
    }
  }
});

For applications utilizing ProxyAgent, custom TLS verification callbacks should be passed via requestTls and proxyTls options, which are similarly preserved during agent initialization.

Official Patches

Node.js / UndiciOfficial GitHub Security Advisory for GHSA-2m5v-pch6-74hx / CVE-2026-109383
Node.js / UndiciUndici v8.12.2 Release Notes
Node.js / UndiciUndici v7.30.1 Release Notes
Node.js / UndiciUndici v6.29.1 Release Notes
OpenJS FoundationOpenJS Foundation CNA Security Advisories Bulletin

Fix Analysis (4)

Technical Appendix

CVSS Score
7.4/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
EPSS Probability
0.18%
Top 92% most exploited

Affected Systems

Node.js Undici HTTP Client Library (versions 4.0.0 through 6.29.0)Node.js Undici HTTP Client Library (versions 7.0.0 through 7.30.0)Node.js Undici HTTP Client Library (versions 8.0.0 through 8.12.1)

Affected Versions Detail

Product
Affected Versions
Fixed Version
undici
Node.js / OpenJS Foundation
>= 4.0.0, < 6.29.16.29.1
undici
Node.js / OpenJS Foundation
>= 7.0.0, < 7.30.17.30.1
undici
Node.js / OpenJS Foundation
>= 8.0.0, < 8.12.28.12.2
AttributeDetail
Vulnerability TypeCWE-295: Improper Certificate Validation
CVSS v3.1 Score7.4 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N)
EPSS Score0.184% (7.93rd Percentile)
Affected Componentundici Agent Dispatcher (lib/dispatcher/agent.js)
Root CauseJSON.parse(JSON.stringify(options)) strips function callbacks in tls options
Security ImpactBypass of custom TLS certificate pinning (checkServerIdentity) via AitM
Patched Versionsundici 6.29.1, 7.30.1, 8.12.2
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1557Adversary-in-the-Middle
Credential Access
T1557Adversary-in-the-Middle
Collection
CWE-295
Improper Certificate Validation

The software does not validate, or incorrectly validates, a certificate, allowing an attacker to spoof a trusted entity by interfering in the communication path between the host and client.

Known Exploits & Detection

GitHubOfficial regression test case demonstrating checkServerIdentity bypass prior to patch in test/node-test/agent.js

Vulnerability Timeline

Primary fix commit (f75eac00ac177f7b2e6c8bfbd52d4113164286e6) merged into the nodejs/undici repository.
2026-10-14
Patched releases undici v6.29.1, v7.30.1, and v8.12.2 published.
2026-10-15
CVE-2026-109383 and GitHub Security Advisory GHSA-2m5v-pch6-74hx officially published by OpenJS Foundation CNA.
2026-10-15
NVD completes CVSS v3.1 analysis and CPE configuration mapping.
2026-10-19

References & Sources

  • [1]GitHub Security Advisory GHSA-2m5v-pch6-74hx
  • [2]NVD CVE-2026-109383 Detail
  • [3]CVE.org Record: CVE-2026-109383
  • [4]OpenJS Foundation CNA Security Advisories
  • [5]Primary Fix Commit (Main Branch - v8)
  • [6]Backport Fix Commit (v7 Branch)
  • [7]Backport Fix Commit (v6 Branch)

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•41 minutes ago•CVE-2026-116693
8.7

CVE-2026-116693: Regular Expression Denial of Service in Datadog Node.js Tracer (dd-trace)

CVE-2026-116693 is a high-severity Regular Expression Denial of Service (ReDoS) vulnerability affecting the official Datadog Application Performance Monitoring (APM) client library for Node.js, dd-trace. The defect resides in the default regular expression defined by the DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP configuration parameter, which is used to sanitize incoming and outgoing HTTP URL query strings prior to recording trace span metadata. Due to overlapping sub-patterns and unanchored repetition blocks in the JSON Web Token (JWT) and URL-encoded string matching branches, the V8 JavaScript engine's regular expression subsystem (Irregexp) can be forced into catastrophic or high-degree polynomial backtracking. Because regular expression replacement occurs synchronously on the primary Node.js event loop, an unauthenticated remote attacker can send crafted HTTP requests with malicious query strings to consume 100% of the process CPU core, stalling concurrent request processing and rendering the application unavailable.

Alon Barad
Alon Barad
3 views•10 min read
•about 2 hours ago•CVE-2026-116692
9.8

CVE-2026-116692: Remote Code Execution in MLflow via Third-Party Scorer Deserialization

CVE-2026-116692 (GHSA-26p8-2jq9-3vq9) is a remote code execution vulnerability in MLflow affecting versions 3.12.0rc0 prior to 3.17.0. The flaw stems from an overly permissive module prefix check during the deserialization of third-party GenAI evaluation scorers in mlflow/genai/scorers/base.py. By setting an experiment's artifact root to a local package directory within the Python module search path and uploading a crafted Python file as a run artifact, an attacker can invoke the /ajax-api/3.0/mlflow/scorer/invoke endpoint to dynamically import and execute arbitrary code on the MLflow tracking server.

Amit Schendel
Amit Schendel
5 views•9 min read
•about 3 hours ago•CVE-2026-116691
6.5

CVE-2026-116691: Cleartext LLM Provider API Key Disclosure in MLflow CrewAI Autologging

CVE-2026-116691 (GHSA-j7qq-vr8c-xvw3) is a sensitive credential disclosure vulnerability in the MLflow platform affecting versions 2.19.0 through 3.16.x. When CrewAI autologging is enabled via mlflow.crewai.autolog(), the tracing instrumentation fails to sanitize nested CrewAI LLM objects before serializing them into OpenTelemetry trace spans. Consequently, plaintext LLM provider API keys are extracted from object dictionaries and string representations, then transmitted and stored in MLflow tracking servers or external OpenTelemetry Protocol (OTLP) telemetry backends.

Alon Barad
Alon Barad
4 views•12 min read
•about 4 hours ago•CVE-2026-116690
7.5

CVE-2026-116690: Arbitrary File Read via Prompt Model Version Source in MLflow

MLflow versions 3.10.0 through 3.16.1 contain a path traversal vulnerability (CWE-22) in the model version creation handler. Specifically, the prompt-specific execution branch in `_create_model_version()` fails to validate schemeless relative paths and evaluates the `mlflow.prompt.is_prompt` tag based on key presence rather than value. An unauthenticated remote attacker on default deployments, or an authenticated low-privilege user on basic-auth deployments, can register a model version with a relative local path as its artifact source and subsequently retrieve arbitrary files from the server filesystem via the `/api/2.0/mlflow/model-versions/get-artifact` endpoint.

Alon Barad
Alon Barad
10 views•9 min read
•about 6 hours ago•CVE-2026-109829
7.4

CVE-2026-109829: Persistent Cache Poisoning and Cross-User Information Disclosure in Node.js Undici

A cache key origin desynchronization and intermediate Cache-Control suppression vulnerability exists in the Node.js Undici HTTP client library. When an originless Agent composes the redirect and cache interceptors in the order compose(redirect(), cache()) with a shared cache store, cross-origin responses and sensitive redirected resources are stored under the initial request's cache key.

Alon Barad
Alon Barad
8 views•11 min read
•about 7 hours ago•CVE-2026-109855
7.5

CVE-2026-109855: Denial of Service via Unhandled Stream Error in Node.js Undici HTTP/2 WebSockets

CVE-2026-109855 is a high-severity Denial of Service (DoS) vulnerability in undici, the primary HTTP/1.1 and HTTP/2 client library for Node.js maintained under the OpenJS Foundation. In versions 8.11.0 and 8.11.1, the WebSocket client fails to properly terminate underlying ClientHttp2Stream instances when an HTTP/2 WebSocket handshake fails validation. When the remote server subsequently closes or resets the stream, Node.js emits an unhandled error event on the orphaned stream, crashing the client process.

Alon Barad
Alon Barad
9 views•9 min read