Skip to content

GHSA-qccp-gfcp-xxvc: Upgrade urllib3 to 2.7.0 and raise Python floor to 3.10 - #36

Draft
sgrammargs wants to merge 1 commit into
mainfrom
remediate-urllib3-vulnerabilities
Draft

GHSA-qccp-gfcp-xxvc: Upgrade urllib3 to 2.7.0 and raise Python floor to 3.10#36
sgrammargs wants to merge 1 commit into
mainfrom
remediate-urllib3-vulnerabilities

Conversation

@sgrammargs

@sgrammargs sgrammargs commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Why

Upgrades urllib3 from 2.2.3 to 2.7.0, fixing the seven security advisories.

Fixed in CVE / Advisory Summary
2.7.0 GHSA-qccp-gfcp-xxvc / CVE-2026-44431 (High) Sensitive headers forwarded across origins in proxied low-level redirects
2.7.0 GHSA-mf9v-mfxr-j63j / CVE-2026-44432 (High) Decompression-bomb safeguards bypassed in parts of the streaming API — not in the original report; found during research
2.6.3 GHSA-38jv-5279-wg99 / CVE-2026-21441 (High) Decompression-bomb safeguards bypassed when following HTTP redirects (streaming API)
2.6.0 GHSA-2xpw-w6gg-jr37 / CVE-2025-66471 (High) Streaming API improperly handles highly compressed data
2.6.0 GHSA-gm62-xv2j-4w53 / CVE-2025-66418 (High) Unbounded number of links in the decompression chain
2.5.0 GHSA-pq67-6m6q-mj2v / CVE-2025-50181 (Medium) Redirects not disabled when retries are disabled on PoolManager instantiation
2.5.0 GHSA-48p4-8xcf-vxj5 / CVE-2025-50182 (Medium) Redirects not controlled in browsers and Node.js

This SDK is directly exposed — these are not theoretical transitive findings. urllib3 is a direct dependency and ynab/rest.py uses the exact affected surfaces:

  • Streaming API: preload_content=False is passed on every request (rest.py:193, :202, :219, :230, :239, :256). All four decompression-bomb advisories target this path.
  • Proxied redirects: urllib3.ProxyManager and urllib3.contrib.socks.SOCKSProxyManager are constructed with proxy_headers (rest.py:111, :115), and configuration.py:480 builds auth headers via urllib3.util.make_headers. That is the surface GHSA-qccp-gfcp-xxvc covers.
  • Retries/redirects: configuration.retries is passed straight into the pool manager (rest.py:91), so a consumer setting it reaches the CVE-2025-50181 path.
  • One exception: GHSA-48p4-8xcf-vxj5 is not reachable here — it applies to the Emscripten/Pyodide (browser and Node.js) backend, which a server-side Python SDK never uses. Included because the same upgrade covers it.

Why 2.7.0 is safe

Reviewed CHANGES.rst across the entire 2.3.0 → 2.7.0 range, not just the target release. Two items needed a closer look, and neither affects this codebase:

  • 2.6.0 removed HTTPResponse.getheaders() / getheader() — this would have been a genuine break, but 2.6.1 restored both. Independently, the SDK's RESTResponse.getheaders() / getheader() (rest.py:56-62) are its own wrappers that read self.response.headers / .headers.get() directly, so they never depended on the removed methods either way.
  • 2.6.0 changed the urllib3.response.ContentDecoder API for custom decompressors, and warns that an ambient Brotli/brotlicffi install should be ≥ 1.2.0. This repo defines no custom decompressors, and poetry.lock contains no brotli, brotlicffi, or zstandard — so neither caveat applies.

Other behavior changes reviewed and judged benign: 2.6.3 caps Retry-After at 6 hours (strictly safer); 2.7.0 switches deprecation notices from DeprecationWarning to FutureWarning (visibility only) and raises the minimum pyOpenSSL to 19.0.0 (not used here). The security fixes themselves only reject malformed or malicious input — well-formed requests are unaffected, which the before/after check below confirms.

What changed

urllib3 is already a direct dependency, so no constraints group or override was needed — the version was raised in [tool.poetry.dependencies] and applied with a scoped poetry update urllib3 rather than a bare poetry lock).

The Python floor move is forced, not discretionary. urllib3 2.7.0 declares requires_python >= 3.10, and Poetry refuses to resolve it against ^3.8:

urllib3 requires Python >=3.10, so it will not be installable for Python >=3.8,<3.10
So, because ynab depends on urllib3 (>= 2.7.0, < 3.0.0), version solving failed.

Python 3.8 reached EOL in Oct 2024 and 3.9 in Oct 2025, so no still-supported version is dropped. Every urllib3 ≥ 2.3.0 requires ≥ 3.9, so any fix here required dropping 3.8 regardless of target.

File Change
pyproject.toml python = "^3.10", urllib3 = ">= 2.7.0, < 3.0.0"
templates/pyproject.mustache Same two edits — without this, the next SDK regeneration silently reverts both, since pyproject.toml is generated from this template
poetry.lock urllib3 2.2.3 → 2.7.0
.github/workflows/build-test.yml Matrix ["3.8", "3.9", ...]["3.10", "3.11", "3.12"]
.tool-versions 3.9.103.10.20 (the old pin was below the declared floor and isn't an installable-by-default version locally)
requirements.txt urllib3 >= 2.1.0>= 2.7.0 (this is what tox.ini installs from)
README.md "Python 3.8+" → "Python 3.10+"

No cascade occurred, and I checked rather than assumed. The scoped update moves exactly one package:

CHANGED  urllib3: 2.2.3 -> 2.7.0
1 package(s) changed

The only other lockfile movement is marker bookkeeping with no version changes: exceptiongroup's marker narrows from python_version < "3.11" to == "3.10", and annotated-types drops a typing-extensions sub-dependency that only applied below 3.9 (now unreachable). Raising the floor permits newer releases of the other 31 packages but does not force them, so they stay pinned.

⚠️ One thing for the reviewer to be aware of: the dev toolchain is currently held at its last 3.8-compatible release (mypy 1.14.1, pytest 8.3.5, coverage 7.6.1, pydantic 2.10.6, flake8 5.0.4, tox 4.25.0, virtualenv 20.29.3). Those become eligible to jump now, so a future bare poetry lock will sweep them all forward at once. This PR deliberately does not.

version is left at 4.3.0. Dropping Python support is semver-major and needs a 5.0.0 bump before release — kept separate to match the repo's existing "Bumping version for X" commit convention.

Verified

Against this repo's pinned toolchain — Python 3.10.20 (installed to match the updated .tool-versions) and Poetry 2.3.2 (matching the existing lock header, so lock-version = "2.1" and the generator comment are preserved):

  • Test suite: 162 passed — identical to the main baseline. But this is weak evidence and should not be read as proof the upgrade works: the only test touching rest.py mocks urllib3 out entirely (MagicMock() in tests/test_response_deserialize.py), no test issues a real request, and rest.py sits at 40% coverage. Hence the functional check below.

  • Reproduced two advisory PoCs against a local HTTP server, before and after. Ran the same script under 2.2.3 and 2.7.0 with the SDK importable in both:

    Check urllib3 2.2.3 urllib3 2.7.0
    SDK real GET + POST through RESTClientObject (incl. preload_content=False) sdk_get=True sdk_post=True status=200 sdk_get=True sdk_post=True status=200 (byte-identical)
    CVE-2025-50181PoolManager(retries=False) vs a 302 status=200 → redirect followed (vulnerable) status=302not followed (fixed)
    CVE-2025-66418 — response with 7 chained Content-Encoding: gzip links decoded b'payload'7 links accepted (vulnerable) DecodeErrorchain rejected (fixed)

Generated with Claude Code

urllib3 2.2.3 is affected by seven advisories, six of which were flagged
for this repo. The SDK is directly exposed: ynab/rest.py passes
preload_content=False on every request (the streaming API targeted by the
decompression-bomb issues) and builds ProxyManager/SOCKSProxyManager with
auth headers (the proxied-redirect header leak).

urllib3 2.7.0 is the first release fixing all of them, and it requires
Python >= 3.10. Poetry cannot resolve it against python = "^3.8", so the
floor moves to ^3.10. Python 3.8 (EOL Oct 2024) and 3.9 (EOL Oct 2025) are
both past end-of-life. This is a breaking change for the published package
and needs a major version bump before release; the version is left at 4.3.0
to match the convention of bumping separately.

templates/pyproject.mustache is updated alongside pyproject.toml so the
next SDK regeneration does not revert either constraint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedpypi/​urllib3@​2.2.3 ⏵ 2.7.097 +1100 +40100100100

View full report

@sgrammargs
sgrammargs requested a review from bradymholt August 6, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant