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-109924

CVE-2026-109924: Unbounded Memory Allocation in Node.js Undici EventSource Stream Parser

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 24, 2026·11 min read·3 visits

Executive Summary (TL;DR)

Node.js undici versions prior to 6.29.1, 7.30.1, and 8.12.2 contain an unbounded memory allocation flaw in the EventSource stream parser. Remote SSE servers can crash client applications via heap exhaustion by streaming fragmented chunks or continuous data lines without event boundaries.

A resource allocation vulnerability (CWE-770) exists in the Server-Sent Events (SSE) parser within the Node.js undici HTTP client library. In affected versions, the EventSourceStream implementation fails to restrict the count of internal Buffer chunk references and accumulated string nodes prior to event dispatch boundaries. A malicious or compromised SSE endpoint can exploit this behavior by streaming fragmented, unterminated lines or continuous sequences of short data fields without empty separator lines, bypassing configured byte-size limits and causing a fatal Out-of-Memory (OOM) crash in the client Node.js process.

Vulnerability Overview

undici is the official HTTP/1.1 and HTTP/2 client library maintained by the Node.js project. Beyond serving as a standalone package in the npm ecosystem, undici powers the native fetch and EventSource web standards implementations integrated directly into the Node.js runtime. The EventSource interface enables client applications to establish persistent HTTP connections to receive Server-Sent Events (SSE) streamed using the text/event-stream MIME type.

In undici versions 6.5.0 through 6.29.0, 7.0.0 through 7.30.0, and 8.0.0 through 8.12.1, the streaming SSE parser located in lib/web/eventsource/eventsource-stream.js contains an allocation of resources without limits vulnerability (CWE-770). While the parser enforces raw byte-length limits via configuration parameters such as maxEventSize and maxResponseSize, it does not constrain the quantity of discrete JavaScript object wrappers, Buffer instances, or V8 internal string nodes created while processing incoming network streams.

When a Node.js application connects via EventSource to an untrusted or compromised remote server, the remote peer can structure its HTTP response stream to maximize object allocation overhead while remaining well below the raw byte thresholds. Because each small chunk or individual field line incurs substantial metadata overhead in the V8 JavaScript engine, the host process exhausts its available heap memory and terminates abruptly with an unrecoverable Out-of-Memory (OOM) fatal error.

Root Cause Analysis

The root cause of CVE-2026-109924 lies in how the EventSourceStream class manages state across stream transforms before an SSE message is finalized. According to the W3C Server-Sent Events specification, an SSE stream consists of text lines separated by newline characters (\r\n, \n, or \r). Individual fields (data:, event:, id:, retry:) are parsed line by line and accumulated into a pending event state object. The pending event is only dispatched to the application listener when the parser encounters a blank line (two consecutive newline sequences, such as \n\n).

Three specific architectural flaws in EventSourceStream allow memory consumption to grow unbounded prior to event dispatch:

First, the parser buffers incoming stream chunks in an internal array (this.chunks) until a newline delimiter is encountered. When checking whether an unterminated line exceeds permitted limits, the parser previously evaluated only this.currentLineSize, which tracks the sum of payload bytes across chunks. If a remote peer transmits an unfinished line fragmented across millions of 1-byte TCP segments or stream chunks, this.chunks.push(chunk) retains a full Node.js Buffer object wrapper and array reference for every single byte. The V8 heap memory consumed by these object wrappers is orders of magnitude larger than the raw byte count tracked by this.currentLineSize.

Second, when processing completed data: lines, the parser appends each line to the pending event's data property using string concatenation (event.data += \\n${value}`). In V8, repeated concatenation constructs a tree of internal ConsString(concatenated string) heap nodes rather than a flat byte array. Furthermore,this.maxEventSizedefaulted tobuffer.kStringMaxLength(up to 1 GB on 64-bit architectures). If a server continuously transmits minimaldata:\n lines without ever sending a blank line (\n\n) to flush the event, the accumulation of V8 string nodes and garbage collection tracking structures exhausts the Node.js heap long before the raw character count approaches maxEventSize. In addition, non-data fields (id:, event:, retry:`) were not accounted for properly against the unified event size ceiling.

Third, wire-level size enforcement via maxResponseSize operates on the compressed HTTP payload prior to content decoding. When a server responds with Content-Encoding: gzip or deflate, a highly compressible sequence of repeated data:\n lines measuring only a few kilobytes on the network inflates into hundreds of megabytes of uncompressed stream chunks inside the client process. This decompression amplification bypasses wire-level byte checks and feeds directly into the vulnerable EventSourceStream parser.

Code Analysis

Prior to the security patch, lib/web/eventsource/eventsource-stream.js relied exclusively on byte counters to bound memory usage during line buffering and field parsing. In the pre-patch implementation, incoming chunks lacking a newline delimiter were appended directly to this.chunks as long as this.currentLineSize remained under the byte limit:

// Vulnerable pre-patch chunk buffering and data field handling
if (this.currentLineSize > this.getMaxEventSizeLineLength()) {
  callback(createMaxEventSizeExceededError())
  return
}
 
// When no newline is found in the chunk, the Buffer reference is pushed without counting items
this.chunks.push(chunk)
 
// During line processing, only raw byte length of 'data' fields was checked
if (isFieldName(line, fieldLength, DATA)) {
  const valueBytes = line.length - valueStart
  const eventDataSize = this.eventDataSize + (event.data === undefined ? 0 : 1) + valueBytes
 
  if (this.maxEventSize > 0 && eventDataSize > this.maxEventSize) {
    throw createMaxEventSizeExceededError()
  }
 
  this.eventDataSize = eventDataSize
  const value = line.subarray(valueStart).toString('utf8')
  if (event.data === undefined) {
    event.data = value
  } else {
    event.data += `\n${value}`
  }
}

The remediation, implemented in commits such as 789ef26461c648480433846607154f52d74ec9ab, addresses these issues by introducing strict structural item limits alongside comprehensive byte accounting across all field types.

// Patched implementation in lib/web/eventsource/eventsource-stream.js
const minParserItems = 1024
const maxParserItems = 131072
const bytesPerParserItem = 128
 
// Inside EventSourceStream constructor:
const parserItemLimit = this.maxEventSize > 0
  ? Math.min(maxParserItems, Math.max(minParserItems, Math.floor(this.maxEventSize / bytesPerParserItem)))
  : 0
 
// Cap both retained data lines per event and buffered chunks per line
this.maxDataLines = parserItemLimit
this.maxBufferedChunks = parserItemLimit
this.dataLineCount = 0
this.eventFieldSizes = {
  data: 0,
  event: 0,
  id: 0,
  retry: 0
}

When buffering incoming chunks or appending data: lines, the patched code now verifies both the structural item count and the combined byte size across all event metadata fields:

// Enforce maximum buffered chunk count before pushing to this.chunks
if (this.maxBufferedChunks > 0 && this.chunks.length >= this.maxBufferedChunks) {
  callback(createMaxEventSizeExceededError())
  return
}
this.chunks.push(chunk)
 
// Enforce maximum data line count per event during field processing
if (this.maxDataLines > 0 && this.dataLineCount >= this.maxDataLines) {
  throw createMaxEventSizeExceededError()
}
this.dataLineCount++

Additionally, the patch adds a _destroy lifecycle method to EventSourceStream that explicitly zeroes this.chunks.length = 0 and resets parser counters when the stream is aborted or encounters an error. This ensures that retained Buffer references are immediately eligible for garbage collection. The fix is complete against both chunk fragmentation and field accumulation vectors because it bounds both the total byte volume and the discrete object count (1024 to 131072 items depending on maxEventSize).

Exploitation Methodology

Exploiting CVE-2026-109924 requires the target Node.js application to initiate an outbound EventSource connection to an HTTP endpoint controlled or compromised by the attacker. No authentication or user interaction on the client host is required once the connection is established. This scenario commonly arises in microservice architectures, webhook consumers, AI/LLM streaming API clients, or federated applications that allow users to configure custom remote URLs or subscribe to external event streams.

Attack Vector 1: Unterminated Split-Line Chunk Flood

In the first exploitation method, the attacker forces the client to accumulate millions of Buffer wrapper objects inside this.chunks without ever triggering line parsing:

  1. The victim Node.js process initiates an EventSource request to the attacker's HTTP server.
  2. The server responds with status 200 OK and header Content-Type: text/event-stream, disabling Nagle's algorithm (TCP_NODELAY) or using HTTP chunked transfer encoding.
  3. The server continuously transmits 1-byte chunks containing a single ASCII character (e.g., 0x41) without sending 0x0A (\n) or 0x0D (\r).
  4. Because no line delimiter is present, EventSourceStream appends each Buffer instance to this.chunks. The V8 heap is exhausted by object metadata long before this.currentLineSize reaches the default maximum byte threshold.

Attack Vector 2: Compressed Empty Data Field Flood

In the second exploitation method, the attacker leverages HTTP compression to rapidly flood the parser with completed data: lines while withholding the event dispatch delimiter:

  1. The client connects to the malicious server with standard compression headers (Accept-Encoding: gzip, deflate).
  2. The server responds with Content-Type: text/event-stream and Content-Encoding: gzip.
  3. The server streams a pre-computed gzip payload consisting of millions of consecutive data:\n lines (6 bytes per line uncompressed) without any blank lines (\n\n).
  4. Because repetitive strings compress at ratios exceeding 100:1, a network transfer of less than 100 KB passes any wire-level maxResponseSize filter.
  5. Upon zlib decompression inside the client, the parser processes millions of data: lines, allocating V8 string references and concatenating them onto this.event.data until the process terminates with Fatal error: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory.

> [!NOTE] > While no standalone weaponized exploit scripts have been published on ExploitDB or PacketStorm, the regression tests committed alongside the official patch in the nodejs/undici repository directly demonstrate the mechanics of both gzip expansion and multi-chunk split-line exhaustion.

Impact Assessment

The security impact of CVE-2026-109924 is strictly confined to system availability. The vulnerability does not permit arbitrary code execution, memory disclosure, or data tampering, which is reflected in the Confidentiality (C:N) and Integrity (I:N) metrics of its CVSS v3.1 vector (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H, Base Score 5.9).

However, the Availability impact is High (A:H). In the Node.js single-threaded event loop architecture, an unhandled Out-of-Memory exception in the V8 JavaScript engine causes immediate and fatal termination of the entire OS process. Unlike standard JavaScript Error exceptions or rejected Promises that can be caught via try/catch blocks or error event listeners on the EventSource instance, V8 heap exhaustion cannot be intercepted or recovered from within application code.

If a multi-tenant backend service uses a single Node.js process to handle concurrent requests for many users and establishes an EventSource connection to an untrusted endpoint on behalf of one user, triggering this vulnerability terminates the server process for all active tenants. Repeated exploitation against services configured with automatic process restarts (such as systemd, PM2, or Kubernetes pod liveness probes) results in a sustained denial-of-service loop.

From a threat intelligence perspective, CVE-2026-109924 currently carries a low Exploit Prediction Scoring System (EPSS) score of 0.00321 (23.84th percentile) and is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The High Attack Complexity (AC:H) rating accounts for the prerequisite that the victim client must be induced to connect to a malicious SSE server.

Remediation and Mitigation

The OpenJS Foundation and the undici maintainers have released patched versions across all supported major release branches (v6, v7, and v8) that enforce strict caps on buffered chunks and retained data lines.

Official Patch Upgrades

Development and operations teams should upgrade undici to the fixed version corresponding to their active release branch:

  • For undici 6.x (>= 6.5.0 < 6.29.1): Upgrade to 6.29.1 or later.
  • For undici 7.x (>= 7.0.0 < 7.30.1): Upgrade to 7.30.1 or later.
  • For undici 8.x (>= 8.0.0 < 8.12.2): Upgrade to 8.12.2 or later.

To update the dependency using npm and verify the resolved dependency tree, execute the following commands:

# Update undici to the latest patched release within semver constraints
npm update undici
 
# Verify that no vulnerable transitive versions remain installed
npm list undici

> [!IMPORTANT] > Because undici is bundled internally within the Node.js runtime to provide global EventSource and fetch APIs, applications relying on the built-in Node.js EventSource global (rather than importing undici from node_modules) should also track and apply corresponding Node.js runtime security releases that incorporate these updated undici versions.

Workarounds and Defense-in-Depth

According to the official vendor advisory (GHSA-4qv8-2p9j-2685), no application-level code workarounds exist for affected versions of undici when parsing streams from untrusted SSE servers. Because V8 heap exhaustion occurs inside the internal transform stream before events are emitted to userland callbacks, application code cannot inspect or abort the pending event accumulation directly.

Where immediate dependency patching is blocked, organizations can reduce risk through the following operational controls:

  1. Restrict Outbound SSE Destinations: Enforce strict URL allowlists so that EventSource clients only connect to verified, trusted internal or partner endpoints.
  2. Enforce Egress Timeouts and AbortControllers: Attach an AbortSignal with a strict timeout to outbound connections or monitor process resident set size (process.memoryUsage().heapUsed) to proactively abort long-running streams before fatal heap limits are reached.
  3. Upstream Proxy Filtering: Route outbound SSE traffic through an egress proxy configured to strip Accept-Encoding: gzip, deflate on text/event-stream requests (mitigating decompression amplification) and enforce maximum connection duration limits.

Official Patches

Node.js / GitHub Security AdvisoryOfficial GitHub Security Advisory GHSA-4qv8-2p9j-2685 for undici
Node.jsundici v6.29.1 patched release
Node.jsundici v7.30.1 patched release
Node.jsundici v8.12.2 patched release

Fix Analysis (4)

Technical Appendix

CVSS Score
5.9/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.32%
Top 76% most exploited

Affected Systems

Node.js applications using the undici npm package (versions 6.5.0 to 6.29.0, 7.0.0 to 7.30.0, and 8.0.0 to 8.12.1)Node.js runtimes utilizing affected versions of undici for native EventSource connections

Affected Versions Detail

Product
Affected Versions
Fixed Version
undici
Node.js / OpenJS Foundation
>= 6.5.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 TypeAllocation of Resources Without Limits or Throttling (CWE-770)
Affected Componentundici EventSourceStream (lib/web/eventsource/eventsource-stream.js)
Attack VectorNetwork (Malicious Server-Sent Events HTTP Response Stream)
CVSS v3.1 Score5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H)
EPSS Score0.32% (23.84th Percentile)
Primary ImpactDenial of Service via V8 JavaScript Heap Out-of-Memory Crash
Exploit StatusProof-of-Concept in Repository Regression Tests
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-770
Allocation of Resources Without Limits or Throttling

The software allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.

Vulnerability Timeline

Security patch commits authored and merged into the nodejs/undici repository.
2026-10-14
GitHub Security Advisory GHSA-4qv8-2p9j-2685 and CVE-2026-109924 published by OpenJS Foundation CNA.
2026-10-15
National Vulnerability Database (NVD) publishes CVE-2026-109924 record.
2026-10-15
Official CVE record metadata updated in cvelistV5 repository.
2026-10-16
NVD completes vulnerability analysis and assigns CVSS v3.1 score of 5.9.
2026-10-19

References & Sources

  • [1]GitHub Security Advisory GHSA-4qv8-2p9j-2685
  • [2]National Vulnerability Database - CVE-2026-109924
  • [3]CVE.org Official Record - CVE-2026-109924
  • [4]OpenJS Foundation CNA Security Advisories
  • [5]CVEListV5 Official JSON Record for CVE-2026-109924
  • [6]GitHub Patch Commit 789ef26461c648480433846607154f52d74ec9ab

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

•about 1 hour ago•CVE-2026-109869
7.5

CVE-2026-109869: Denial of Service via Orphaned Stream in Node.js Undici Fetch Implementation

CVE-2026-109869 is a high-severity denial-of-service vulnerability in undici, the default HTTP/1.1 client and Fetch implementation for Node.js. When a server responds with more than five Content-Encoding directives and prematurely closes the connection before transmitting the body, undici allocates an internal Readable stream before enforcing the content-encoding limit. Upon rejecting the fetch promise, the stream is orphaned without an error listener or abort signal, causing the premature socket closure to emit an unhandled stream error that terminates the Node.js process.

Alon Barad
Alon Barad
5 views•10 min read
•about 1 hour ago•CVE-2026-114612
9.9

CVE-2026-114612: Privilege Escalation to Proxy Admin via Cryptographic Context Reuse in LiteLLM

LiteLLM versions 1.87.0 through 1.104.0-rc.1 contain a cryptographic domain separation vulnerability (CWE-269, CWE-345, CWE-441) that allows an authenticated low-privilege user to escalate privileges to full proxy administrator. The application reuses a single symmetric cryptographic context and key derivation path for both at-rest secret encryption and ephemeral UI/CLI session authentication tokens without providing Associated Authenticated Data (AAD). By submitting crafted JSON session claims into user-accessible encrypted metadata fields, an attacker can use the API-key generation flow as an encryption oracle, extract the resulting ciphertext, and replay it as a bearer token to achieve administrative control and subsequent remote command execution via the Model Context Protocol (MCP) stdio interface.

Alon Barad
Alon Barad
7 views•9 min read
•about 2 hours ago•CVE-2026-109461
5.9

CVE-2026-109461: Origin Validation Error in @azure/msal-node Leads to Information Disclosure

An origin validation vulnerability in @azure/msal-node allows attackers to process unchecked authorization code response payloads or request query parameters in ConfidentialClientApplication, potentially directing sensitive client credentials to external endpoints.

Amit Schendel
Amit Schendel
9 views•4 min read
•about 3 hours ago•CVE-2026-113719
4.5

CVE-2026-113719: Arbitrary File Deletion via Path Traversal in virtualenv Seed Package Metadata

A path traversal vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to cause arbitrary file and directory deletion when a virtual environment target directory containing untrusted distribution metadata is reused or reseeded.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 4 hours ago•CVE-2026-113715
2.0

CVE-2026-113715: Configuration Key Injection via Unicode Line Boundary in virtualenv

In virtualenv versions prior to 21.14.9, creating isolated Python virtual environments using the standard library creator (`--creator venv`) allows configuration key injection into `pyvenv.cfg` if an attacker can control or influence the destination directory path containing Unicode line break characters.

Amit Schendel
Amit Schendel
5 views•5 min read
•about 5 hours ago•CVE-2026-113723
7.1

CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv

A command injection vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to execute arbitrary commands when a user sources a generated shell activation script containing a crafted prompt value.

Alon Barad
Alon Barad
6 views•5 min read