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

CVE-2026-18149: Unresolved Response Body Hang in Undici RetryHandler

Alon Barad
Alon Barad
Software Engineer

Sep 29, 2026·8 min read·25 visits

Executive Summary (TL;DR)

An unmitigated resource leak in undici's RetryHandler causes application-level hangs and Denial of Service when handling aborted responses followed by non-retryable errors.

A resource management vulnerability in the Undici HTTP client (CWE-772) occurs when the retry interceptor receives a partial body payload followed by a non-retryable response error on a subsequent connection attempt, resulting in orphaned streams and potential Denial of Service (DoS).

Vulnerability Overview

Undici is a high-performance HTTP/1.1 and HTTP/2 client for Node.js. Applications rely on its robust connection pooling and automatic retry handling capabilities to maintain network resilience. The vulnerability designated as CVE-2026-18149 targets the core logic of Undici's retry interceptor, specifically how it manages the lifecycle of stream resources when connection errors interact with subsequent HTTP protocol exceptions.

The vulnerability is formally categorized under CWE-772: Missing Release of Resource after Effective Lifetime. It occurs when a client application initiates a request, and Undici's automatic retry mechanism fails to settle or terminate a previously exposed, truncated response body stream. This state desynchronization occurs if a subsequent retry fails with a non-retryable HTTP status code, leaving the initial body stream permanently orphaned.

From an operational perspective, the downstream client remains in a perpetual waiting state, blocking concurrent connections and eventually causing resource exhaustion. Because the handler substitutes the connection's active parser without destroying the original stream, the standard timeout rules configured at the body level fail to execute. This allows an attacker with control of a remote target server to systematically crash or freeze a client application with minimal bandwidth expenditure.

Root Cause Analysis

To trace the root cause, it is necessary to examine the interaction between Undici's connection parser and its middleware handler layer. When an HTTP server issues a 200 OK response with a declared Content-Length header, Undici receives the headers and executes onResponseStart(). This action successfully propagates the headers downstream and creates a readable body stream that is delivered to the client application.

If the server terminates the socket connection before transmitting the full body payload, the HTTP parser registers an premature-end-of-stream error. Because the request is eligible for retry, Undici's internal retry engine attempts to re-establish a connection and replay the request. At this juncture, the downstream application code is already waiting on the first stream's response body, holding active Promise callbacks that expect either complete data or an explicit error signal.

The flaw materializes when the retried request completes with a non-retryable HTTP status, such as a 400 Bad Request. When processing this second response, the unpatched RetryHandler immediately updates its internal state tracking, assuming it must present this new response to the client. It completely replaces the stream controller context for the downstream application without executing any termination or error handling routines on the original response stream, leaving the initial body permanently suspended in memory.

A critical exacerbating factor is that standard socket body-timeouts are bound directly to the active connection parser. Once the parser context is reassigned to the new, terminated retry socket, the orphaned body stream no longer has an active timer monitoring its lifecycle. The client application remains blocked on the unresolved stream, and the Node.js event loop retains the associated callbacks, preventing garbage collection of the stream objects and TCP structures.

Code Path & Patch Analysis

The vulnerability is mitigated by enforcing explicit state checks during response lifecycle transitions within the lib/handler/retry-handler.js file. The patch targets the critical onResponseStart function and the internal shouldRetry error processing pathway. By introducing a guard clause that checks the status of this.headersSent, the handler is prevented from silently overriding the stream context when a response has already been initiated downstream.

Below is the technical diff showing the logical correction applied across the vulnerable handlers in undici:

// File: lib/handler/retry-handler.js (Vulnerable vs. Patched)
 
// Vulnerable Implementation:
onResponseStart(controller, statusCode, headers, statusMessage) {
  if (this.retryOpts.throwOnError) {
    if (this.retryOpts.statusCodes.includes(statusCode) === false) {
      this.headersSent = true
      this.checkpointResponseEnd(headers)
      this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
    }
  }
}
 
// Patched Implementation:
onResponseStart(controller, statusCode, headers, statusMessage) {
  if (this.retryOpts.throwOnError) {
    if (this.retryOpts.statusCodes.includes(statusCode) === false) {
      if (this.headersSent) {
        // The downstream handler already received the response from an
        // earlier attempt. Forwarding this response would replace the
        // downstream body and leave the original body pending forever.
        this.handler.onResponseError?.(this.controllerProxy, err)
      } else {
        this.headersSent = true
        this.checkpointResponseEnd(headers)
        this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
      }
    }
  }
}

When evaluating the patched logic, the integration of this.headersSent as a conditional guard is crucial. If the headers were already transmitted during an earlier, partially successful attempt, forwarding the new error response is skipped. Instead, the library invokes onResponseError with a designated error object, which triggers proper stream cleanup.

This callback execution propagates an error (typically UND_ERR_REQ_RETRY) directly into the active stream controller. The stream is terminated with this exception, resolving any pending Promises such as those returned by body.text(), body.json(), or manual stream read loops. This ensures that the application code receives an actionable error instead of hanging indefinitely, thereby releasing the associated system resources and allowing the event loop to function normally.

Exploitation & Reproduction

Exploitation of this vulnerability requires an attacker to control a server that is targeted by an Undici-based client. Alternatively, a malicious actor could intercept traffic via a Man-in-the-Middle position if connection encryption is absent or misconfigured. The attacker does not need to execute any binary payloads or construct highly complex shellcodes; instead, they manipulate the state machine of the HTTP client.

To successfully trigger the vulnerability, the attacker's server must orchestrate two distinct response stages. First, the server must accept an incoming connection, return a standard success header (such as 200 OK) specifying a non-zero Content-Length, send only a portion of the body, and immediately tear down the socket. Second, when the client attempts to execute its configured automatic retry, the server must respond with an HTTP status code that is deemed non-retryable by the client, such as 400 Bad Request.

Below is a state diagram representing the lifecycle of the vulnerability under an attack scenario:

The security impact accumulates rapidly with repeated client requests. Every subsequent request directed to the malicious endpoint spawns an additional hung Promise and an unclosed socket stream handler. Because these streams are not tracked by active connection timeouts, they persist until the Node.js application process runs out of available heap space or file descriptors, culminating in a total denial of service.

Impact Assessment

The primary operational consequence of CVE-2026-18149 is application-level Denial of Service. In enterprise environments where microservices communicate continuously via HTTP APIs, a single compromised or malicious upstream dependency can render entire calling systems unresponsive. Because Node.js operates on a single-threaded event loop, the rapid accumulation of unresolved promises and open file descriptors degrades the event loop's overall execution speed and concurrency handling capabilities.

The vulnerability has been assigned a CVSS score of 5.9 (Medium) with the vector string CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H. The medium-level rating reflects the high complexity of the attack, which relies on a specific sequence of network behaviors and timing. However, the impact on availability is rated as High, given that a simple sequence of two short responses can completely incapacitate the client process.

According to current threat intelligence, the Exploit Prediction Scoring System (EPSS) score stands at approximately 0.0036, indicating a low immediate probability of widespread automation-based exploitation. Furthermore, this vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities catalog. However, the lack of active exploitation should not discourage immediate remediation, as the attack vectors are highly reliable and easily replicated by targeting known microservice communication pathways.

Remediation & Hardening

Complete remediation of this vulnerability requires upgrading the undici package to safe versions. Development teams should audit their project dependency trees and ensure that any references to undici are updated to either 7.29.1 (for 7.x deployments) or 8.10.2 (for 8.x deployments). These versions contain the definitive state-checks that ensure all orphaned stream handlers are cleanly rejected and closed when error states occur during retries.

If an immediate package upgrade is not feasible due to strict legacy dependencies or release freeze policies, several mitigation strategies can reduce the risk. Security engineers should evaluate the use of the retry interceptor. If the client is fetching resources from untrusted external sources, disabling automatic retry logic or limiting the retry scope strictly to safe, idempotent endpoints will neutralize the attack vector.

In addition, applications should adopt defensive coding patterns by wrapping all network body parsing calls in strict timeout blocks. Rather than relying exclusively on Undici's internal connection or body timeout parameters, developers should employ standard JavaScript mechanisms such as AbortSignal.timeout() or custom Promise.race() patterns. This ensures that even if an underlying library stream fails to settle, the application execution path is forcibly aborted and cleaned up after a deterministic period.

Fix Analysis (2)

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.36%
Top 73% most exploited

Affected Systems

Node.js applications using Undici HTTP client within 7.x and 8.x version branches.

Affected Versions Detail

Product
Affected Versions
Fixed Version
undici
Node.js
>= 7.11.0 < 7.29.17.29.1
undici
Node.js
>= 8.0.0 < 8.10.28.10.2
AttributeDetail
CWE IDCWE-772
Attack VectorNetwork (AV:N)
CVSS Score5.9 (Medium)
EPSS Score0.0036 (0.36%)
ImpactDenial of Service (DoS)
Exploit StatusProof-of-Concept (PoC) available
KEV StatusNot listed

MITRE ATT&CK Mapping

T1498Network Denial of Service
Denial of Service
T1498.001Connection Request Flooding
Denial of Service
CWE-772
Missing Release of Resource after Effective Lifetime

The software does not release a resource after its effective lifetime has ended, which can lead to resource exhaustion.

Vulnerability Timeline

Security patch commits released by OpenJS development team
2026-09-03
GitHub Security Advisory (GHSA-pmjh-fq2x-6v4x) published
2026-09-04
CVE-2026-18149 vulnerability assigned and detailed
2026-09-04

References & Sources

  • [1]GitHub Security Advisory GHSA-pmjh-fq2x-6v4x
  • [2]NVD CVE-2026-18149 Details
  • [3]CVE.org CVE-2026-18149
  • [4]OpenJS Foundation Security Advisories

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

•22 minutes ago•CVE-2026-126297
7.5

CVE-2026-126297: Algorithmic Complexity Denial of Service in NLTK ConllCorpusReader

CVE-2026-126297 (GHSA-v8f3-6phw-6mh9) is an uncontrolled resource consumption and algorithmic complexity vulnerability in the Natural Language Toolkit (NLTK) prior to version 3.10.4. Located in the Semantic Role Labeling (SRL) parser within `nltk/corpus/reader/conll.py`, the flaw allows an attacker to trigger quadratic O(N^2) CPU processing overhead by providing crafted CoNLL-formatted corpus inputs containing decoy span columns or nested full-width verb spans. Applications parsing untrusted linguistic corpora can experience prolonged CPU exhaustion and application-level Denial of Service (DoS).

Amit Schendel
Amit Schendel
1 views•12 min read
•about 1 hour ago•CVE-2026-126298
7.5

CVE-2026-126298: Quadratic Algorithmic Complexity Denial of Service in NLTK Tree.chomsky_normal_form

CVE-2026-126298 (also tracked as GHSA-r53h-rw34-8h97) is an algorithmic complexity Denial of Service (DoS) vulnerability in the Natural Language Toolkit (NLTK) Python library affecting all versions prior to 3.10.4. The vulnerability resides in the nltk.tree.transforms.chomsky_normal_form function and its corresponding public method Tree.chomsky_normal_form(). When transforming a parse tree into Chomsky Normal Form using the default right-factoring strategy, the function repeatedly invokes list.pop(0) inside a loop iterating over all children of a node. In CPython, removing the initial element of a standard list requires an O(N) memory shift operation, causing the overall binarization algorithm to degrade to O(N^2) quadratic time complexity relative to the width of a node. Furthermore, converting horizontal node width into vertical tree depth bypasses initial parsing depth bounds, triggering subsequent stack exhaustion (RecursionError) when recursive methods traverse the mutated structure. Unauthenticated remote attackers who can submit bracketed tree strings to applications invoking this transformation can exhaust single-threaded CPU resources or induce unhandled application exceptions.

Alon Barad
Alon Barad
3 views•12 min read
•about 2 hours ago•CVE-2026-126299
7.5

CVE-2026-126299: Quadratic Algorithmic Complexity Denial of Service in NLTK CCG Lexicon Parser

An algorithmic complexity vulnerability (CWE-407) in the Natural Language Toolkit (NLTK) before version 3.10.4 allows remote unauthenticated attackers to cause a denial of service via crafted Combinatory Categorial Grammar (CCG) lexicon strings. The parser in nltk.ccg.lexicon.fromstring performs repeated immutable string slicing, concatenation, and regex tail capturing, resulting in O(n^2) CPU consumption when processing wide, flat category definitions.

Alon Barad
Alon Barad
4 views•10 min read
•about 3 hours ago•CVE-2026-126295
7.1

CVE-2026-126295: Path Traversal and Unconfined File Access in NLTK BllipParser

CVE-2026-126295 (also tracked as GHSA-fg9w-wfp5-j933) is a path confinement bypass vulnerability in the Natural Language Toolkit (NLTK) affecting versions 3.10.0 prior to 3.10.4. The flaw resides in nltk/parse/bllip.py within the BllipParser class and its factory method BllipParser.from_unified_model_dir(). Unlike sibling tool wrappers in NLTK that enforce data-root confinement via the nltk.pathsec module, BllipParser passes caller-supplied filesystem paths directly into the third-party bllipparser native C extension without validation. When an application passes untrusted input as model paths or directories to BllipParser, an attacker can force the native extension to open arbitrary files on the local filesystem. This leads to information disclosure through parser diagnostic oracles, local model tampering via shared directories, and denial of service by hanging worker threads on blocking special files (such as FIFOs or device nodes) or exhausting memory via oversized files.

Amit Schendel
Amit Schendel
2 views•11 min read
•about 4 hours ago•CVE-2026-126300
5.3

CVE-2026-126300: Algorithmic Complexity Denial of Service in NLTK TreebankWordTokenizer.span_tokenize

CVE-2026-126300 (GHSA-ffr9-mgrr-wcvr) is an inefficient algorithmic complexity vulnerability (CWE-407) in the Natural Language Toolkit (NLTK) Python library affecting versions 3.2.5 prior to 3.10.4. The flaw resides in the quote-restoration routine of TreebankWordTokenizer.span_tokenize and NLTKWordTokenizer.span_tokenize, where repeated calls to list.pop(0) over matched quote tokens introduce quadratic O(n^2) time complexity. An unauthenticated remote attacker who supplies input containing long sequences of double-quote characters can exhaust CPU resources and degrade service availability.

Amit Schendel
Amit Schendel
3 views•10 min read
•about 5 hours ago•GHSA-GPWC-27CW-RH9R
5.3

CVE-2026-126301: Quadratic CPU Exhaustion in NLTK LegalitySyllableTokenizer

The Natural Language Toolkit (NLTK) versions 3.6 through 3.10.3 contain an algorithmic complexity vulnerability in the LegalitySyllableTokenizer.tokenize method. When processing tokens composed of continuous runs of vowels, an unguarded string reversal and set membership lookup loop executes in quadratic time relative to the input token length. Remote attackers capable of submitting arbitrary text to applications utilizing this tokenizer can trigger sustained single-core CPU exhaustion and application-level denial of service.

Amit Schendel
Amit Schendel
5 views•10 min read