SERGRDZSERGIO RODRIGUEZ

Bounded offline regression · 05 October 2026

CVE-2026-97689

Before the body,
the parser reads a line.

A small body read starts with a chunk-size line. In urllib3 2.7.0, that earlier read had no length limit. The 2.8.0 fix puts a cap on it.

Published upstream issue · 32 local cases passed · Application impact untested

01 / The boundary

The chunk-size line arrives before the chunk body.

The line read changed.

The maintainer advisory describes excessive memory use while buffering chunk-size lines. The patch bounds both size and trailer lines.

In the local fixtures, 2.7.0 calls readline() without a limit. Version 2.8.0 requests at most 65,537 bytes and rejects a line longer than 65,536 bytes. The extra byte detects a line over the limit; the count includes its CRLF.

Counted file reads verify rejection before body delivery. No memory-exhaustion run.

02 / Versions

The September fix comes after the earlier redirect fix.

Which versions changed?

Upstream advisory scope
Affected range≥ 1.10.3 and < 2.8.0
First fixed release2.8.0
Advisory published15 September 2026
Upstream weaknessCWE-770 / resource allocation without limits

The earlier redirect study compared 2.6.3 and 2.7.0 for CVE-2026-44431. Version 2.7.0 fixed that header issue and remains affected by this streaming issue.

Upgrade to a supported release containing the 2.8.0 fix. Check dependency constraints and the release notes for other fixes and compatibility changes.

03 / Local evidence

Eight fixtures through each streaming API, on each release.

The boundary holds in the fixed release.

32 cases exercise the unmodified wheels’ stream() and read_chunked() methods over counted in-memory files. PyPI hashes identify each wheel; its response.py bytes match the pinned upstream release commit.

Observed in both APIs; line sizes include CRLF
Fixture2.7.02.8.0
Ordinary three-byte bodyAcceptedAccepted
65,535 / 65,536-byte size lineAcceptedAccepted
65,537 / 65,538-byte size lineAcceptedRejected before any body read
65,536-byte trailer lineAcceptedAccepted
65,538-byte trailer lineAcceptedRejected after the body was yielded
65,537-byte chunk bodyAcceptedAccepted in 1,024-byte reads

The two-byte-over size fixture shows an earlier stop: 2.7.0 returns all 65,538 line bytes, while 2.8.0 returns 65,537 and closes the response. The maximum fixture input is 65,551 bytes.

What this establishes

The fixed framing cap and rejection order work in these controlled cases. Body size has a separate budget. Trailer validation happens later, so an exception can follow body bytes already delivered to a caller.

How the checks stay local

Each wheel runs in a separate Python process with site packages disabled. A Python audit guard blocks socket creation and DNS. Case-time network attempts are zero. urllib3’s import-time IPv6 socket probe is also blocked and recorded separately.

What is still untested?

Memory exhaustion, real network buffering, TLS, the requests wrapper, decompression, and application exploitability. This is a small parser regression; it does not measure process RSS or reproduce a remote attack.

Read all observations ↓ · Run the checks ↗ · Afterimage source ↗

Upstream credit: pquentin, coordinator; illia-v, remediation reviewer. Independent regression and presentation by Sergio Rodriguez, developed with AI assistance.

04 / Primary sources

Checked 05 October 2026.

Advisory, history, and release identity.

  1. Maintainer advisory / CVE-2026-97689 ↗Scope, impact and original attribution
  2. urllib3 2.7.0 / pinned response.py ↗Affected implementation
  3. urllib3 2.8.0 / pinned response.py ↗Fixed implementation and length checks
  4. Upstream fix commit ↗Implementation and upstream test changes
  5. urllib3 2.8.0 release notes ↗Release context and compatibility notes
  6. Exact release identities ↓PyPI URLs, hashes, tag objects and commits
  7. Review record ↓Source facts, regression scope and limits