CVE-2026-97689 Overview
CVE-2026-97689 is a resource exhaustion vulnerability in urllib3, a widely used HTTP client library for Python. The flaw affects versions from 1.10.3 up to (but not including) 2.8.0. The HTTPResponse.read_chunked and HTTPResponse.stream methods allocate unbounded memory when parsing chunked transfer-encoded responses. A malicious HTTP server can send a Transfer-Encoding: chunked response followed by a very long run of bytes with no newline, forcing the client to buffer indefinitely. The issue is tracked as [CWE-770: Allocation of Resources Without Limits or Throttling] and resolved in urllib3 2.8.0.
Critical Impact
A malicious HTTP server can exhaust client process memory by returning a chunked response with an unterminated chunk-size line, causing denial of service in any Python application using vulnerable urllib3 versions.
Affected Products
- urllib3 versions 1.10.3 through 2.7.x
- Python applications and services depending on vulnerable urllib3 releases
- Downstream libraries embedding urllib3 (for example, requests)
Discovery Timeline
- 2026-09-29 - CVE-2026-97689 published to NVD
- 2026-09-29 - Last updated in NVD database
Technical Details for CVE-2026-97689
Vulnerability Analysis
The defect resides in urllib3's streaming chunk parser inside src/urllib3/response.py. When a response uses Transfer-Encoding: chunked, the parser must read a chunk-size line terminated by CRLF before reading the chunk payload. The vulnerable implementation reads bytes until a newline or end-of-file without enforcing any maximum line length. A malicious upstream server can therefore stream an unbounded stream of non-newline bytes that the client buffers into memory.
The standard library http.client bounds this read using _MAXLINE (64 KiB). urllib3's streaming path bypassed that safeguard, creating divergent behavior between .read() and streaming code paths. Applications that consume large responses through HTTPResponse.stream or HTTPResponse.read_chunked are directly exposed.
Root Cause
The root cause is missing input length validation on the chunk-size line. The parser accumulated bytes until it encountered a delimiter, trusting the server to eventually send one. Without an upper bound, memory grows linearly with attacker-controlled input until the process is killed by the OS or exhausts available memory.
Attack Vector
Exploitation requires a victim client to make an HTTP request to an attacker-controlled or attacker-influenced server. The server responds with Transfer-Encoding: chunked and then emits a very long sequence of bytes without a newline. Server-side request forgery, malicious redirects, and compromised upstream APIs all provide realistic paths to trigger this condition. No authentication or user interaction is required on the vulnerable client.
# Read in 64 KiB chunks
_READ_CHUNK_SIZE = 2**16
# Maximum length of a chunk-size line (and the trailing CRLF line) when reading a
# chunked-transfer-encoded response. Matches ``http.client._MAXLINE`` so urllib3's
# streaming path bounds these reads exactly like the stdlib ``.read()`` path does,
# preventing a malicious server from forcing unbounded buffering (memory exhaustion)
# via an unterminated chunk-size line.
_MAX_CHUNK_LINE_LENGTH = 2**16
class ContentDecoder:
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
Source: urllib3 patch commit cd770b0
Detection Methods for CVE-2026-97689
Indicators of Compromise
- Python processes showing rapid, sustained memory growth followed by OOM termination while performing outbound HTTP requests
- Outbound HTTP responses carrying Transfer-Encoding: chunked from untrusted hosts with abnormally large initial reads and no CRLF observed on the wire
- Application logs containing MemoryError traces originating in urllib3/response.py or HTTPResponse.stream
Detection Strategies
- Inventory Python environments and identify installed urllib3 versions using pip show urllib3 or Software Bill of Materials tooling; flag any version below 2.8.0
- Inspect network telemetry for chunked responses where the first line exceeds 64 KiB without a CRLF delimiter
- Correlate resident set size (RSS) spikes in Python workers with concurrent outbound connections to untrusted domains
Monitoring Recommendations
- Enable per-process memory ceilings and OOM alerts on services that make outbound HTTP calls
- Log dependency versions during application startup so patch coverage can be verified centrally
- Monitor egress traffic to newly observed or low-reputation destinations that could redirect clients to attacker-controlled servers
How to Mitigate CVE-2026-97689
Immediate Actions Required
- Upgrade urllib3 to version 2.8.0 or later across all Python environments, containers, and CI/CD pipelines
- Rebuild and redeploy application container images so pinned or cached wheels do not reintroduce vulnerable versions
- Audit transitive dependencies (for example, requests, boto3, elasticsearch) and update lockfiles to pull the patched urllib3
Patch Information
The fix is committed in urllib3 commit cd770b0 and shipped in the urllib3 2.8.0 release. The patch introduces _MAX_CHUNK_LINE_LENGTH = 2**16, matching http.client._MAXLINE, so the streaming path enforces the same bound as the standard library. Full advisory details are available in GHSA-vxq7-64xx-v4gw.
Workarounds
- Restrict outbound HTTP requests to allowlisted domains that terminate on trusted infrastructure
- Disable following untrusted redirects in HTTP clients until urllib3 is upgraded
- Enforce per-request memory or timeout limits at the application layer to bound the blast radius of a malicious response
# Upgrade urllib3 to the patched release
pip install --upgrade 'urllib3>=2.8.0'
# Verify the installed version
python -c "import urllib3; print(urllib3.__version__)"
# Audit environments for vulnerable versions
pip list --format=freeze | grep -i '^urllib3=='
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

