Skip to content

pip 24.0.post4 (Py3.7): vendored urllib3 1.26.20.post4; requests CVE-2024-47081, CVE-2026-25645 - #16

Merged
icanhasmath merged 1 commit into
Python-3.7-compatfrom
24.0.post4-security
Oct 29, 2026
Merged

icanhasmath merged 1 commit into
Python-3.7-compatfrom
24.0.post4-security

Conversation

@icanhasmath

Copy link
Copy Markdown

Security release pip 24.0.post4 for the Python 3.7 line, to be bundled by CPython 3.7.17.8's ensurepip. pip 24.0.post3 still vendored urllib3 1.26.17 and an unpatched requests 2.31.0.

Changes

Vendored urllib3 1.26.17 → ActiveState urllib3 1.26.20.post4. This picks up upstream 1.26.18/1.26.19 and the fork's 1.26-line ports:

CVE Advisory Issue
CVE-2023-45803 GHSA-g4mx-q9vg-27p4 request body kept after a 303 redirect to GET (upstream 1.26.18)
CVE-2024-37891 GHSA-34jh-p97f-mpxf Proxy-Authorization kept on cross-origin redirects (upstream 1.26.19)
CVE-2025-66418 GHSA-gm62-xv2j-4w53 unbounded Content-Encoding decoder chain
CVE-2025-66471 GHSA-2xpw-w6gg-jr37 streaming API fully decompresses highly compressed data
CVE-2026-21441 GHSA-38jv-5279-wg99 redirect bodies decompressed while draining
CVE-2026-44431 GHSA-qccp-gfcp-xxvc sensitive headers forwarded on cross-origin redirects
CVE-2026-97687 GHSA-8988-9cw3-xx77 HTTPS proxy TLS used the target's TLS settings
CVE-2026-97689 GHSA-vxq7-64xx-v4gw unbounded chunk-size and trailer lines
CVE-2025-50181 GHSA-pq67-6m6q-mj2v PoolManager retries did not limit redirects
  • How it was vendored: I computed pip 24.0's own vendoring diff (upstream urllib3 1.26.17 vs. pip/_vendor/urllib3) and replayed it onto the fork's src/urllib3 from the 1.26.20.post4 sdist. The only hunk that didn't apply was a import six → from pip._vendor import six fix in contrib/securetransport.py; that targets an upstream 1.26.17 bug fixed in 1.26.18, and the fork already imports ..packages.six. The vendored tree is byte-identical to the urllib3 in pip 26.0.1.post3 (pip 26.0.1.post3 (Py3.9): vendored urllib3 1.26.20.post4; requests CVE-2026-25645 #15).
  • Python 3.7: the fork keeps python_requires=">=2.7, !=3.0.*, …, !=3.5.*".
  • Version check: urllib3.__version__ stays 1.26.20, so requests' version check doesn't warn.

Vendored requests 2.31.0 (the last requests for Python 3.7, so it stays at 2.31.0):

  • CVE-2024-47081 (GHSA-9hjg-9r4m-mvj7): backports psf/requests 96ba401c (2.32.4). get_netrc_auth() split the netloc at the first :, so http://example.com:@attacker.com/ looked up, and sent, example.com's .netrc credentials to attacker.com. It now uses the parsed host name. pip calls it from MultiDomainBasicAuth for index and find-links URLs, so pip was exposed.
  • CVE-2026-25645 (GHSA-gc5v-m9x4-r6x2): the same backport as pip 26.0.1.post3 (Py3.9): vendored urllib3 1.26.20.post4; requests CVE-2026-25645 #15 (psf/requests 66d21cb07b plus companion e331a288f3). extract_zipped_paths() now extracts to a new mkstemp file, and HTTPAdapter.cert_verify() uses the default CA bundle path directly. pip didn't reach the vulnerable path, so this is defense in depth.
  • CVE-2024-35195 (GHSA-9wx4-h78v-vm56), not ported:
    • The bug: before 2.32.0, a pool first used with verify=False was reused by later verified requests to the same host.
    • Why pip is not exposed: pip never mixes the two on one adapter. --trusted-host hosts are mounted on their own InsecureHTTPAdapter, which forces verify=False, and everything else goes through the verifying adapter.
    • Why not port it anyway: upstream's fix reworked HTTPAdapter.get_connection() and needed 2.32.1–2.32.3 to stop breaking custom adapters like pip's _SSLContextAdapterMixin.

pip-level advisories: every pip advisory that affects 24.0 is already fixed on this line, so nothing new is needed:

Certifi stays at 2026.10.23.

Validation

pip wheel built with python -m build, tested in python:3.7-slim (Python 3.7.17):

  • Versions: pip reports 24.0.post4, with vendored urllib3 1.26.20 (the fork), requests 2.31.0 and certifi 2026.10.23. Importing them under -W error::Warning raises no RequestsDependencyWarning.
  • Certifi: 118 roots, and emSign Root CA - G1 is the only emSign root.
  • CVE-2024-47081 check: with a .netrc that has entries for example.com and attacker.com, get_netrc_auth("http://example.com:@attacker.com/") returns attacker.com's entry. 24.0.post3 returned example.com's credentials. http://example.com:8080/ still gets example.com's entry.
  • CVE-2026-25645 check: with a planted cacert.pem in the temp directory, extract_zipped_paths() returns a new file with the zip's content, not the planted file, and a different path on every call.
  • Network: pip download six==1.16.0 and a pip install --dry-run requests==2.31.0 resolution against PyPI both work.
  • Running from a zip: python pip-24.0.post4-py3-none-any.whl/pip download six==1.16.0 works.
  • Diff from the 24.0.post3 release asset: the wheel differs only in pip/__init__.py, vendor.txt, requests/adapters.py, requests/utils.py and nine urllib3 files, plus RECORD and METADATA. The urllib3 files in the wheel are identical to the vendored tree in this PR.
  • Wheel sha256: 5bb693cb7b5bada4be8e0620c2fdc714f6f8c4c0cc2dda83f27912039739e974 (local build).

After merge: tag 24.0.post4 and publish the release. Then bundle it in CPython 3.7.17.8.

…2024-47081, CVE-2026-25645

- Vendored urllib3 1.26.17 -> ActiveState urllib3 1.26.20.post4, vendored
  by replaying pip 24.0's own vendoring diff against upstream 1.26.17 onto
  the fork's src/urllib3 (pip._vendor imports, Brotli disabled,
  LICENSE.txt). The result is byte-identical to the urllib3 vendored in pip
  26.0.1.post3. urllib3.__version__ stays "1.26.20" so requests' version
  check does not warn.
- Vendored requests 2.31.0: backport CVE-2024-47081 (get_netrc_auth uses
  the parsed hostname, psf/requests 96ba401c, 2.32.4) and CVE-2026-25645
  (extract_zipped_paths uses mkstemp, psf/requests 66d21cb07b, plus the
  companion e331a288f3 cert_verify change).
- CVE-2024-35195 not ported: pip never mixes verify=False and verify=True
  on one adapter.

@ezequielp-activestate ezequielp-activestate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. I found no blockers.

What I checked

  • Vendored urllib3: src/pip/_vendor/urllib3 is byte-identical to the 26.0.1.post3 tag, which I checked earlier against the 1.26.20.post4 sdist plus pip's vendoring changes. The decompression limits (max_length in the Deflate, Gzip and Brotli decoders) are present.
  • CVE-2024-47081: the get_netrc_auth() hunk is psf/requests 96ba401c line for line.
  • CVE-2026-25645: extract_zipped_paths() and cert_verify() match the 26.0.1.post3 backport (66d21cb07b + e331a288f3).
  • CVE-2024-35195 not ported: I agree. session.py mounts the verifying adapter on https://, and the only adapters with verify=False are InsecureHTTPAdapter and InsecureCacheControlAdapter, whose cert_verify() always forces it. The only other place pip sets verify is session.verify = options.cert. So no single adapter's pools see both modes.
  • Built from this branch with python -m build, tested on python:3.7:
    • Versions: pip 24.0.post4, requests 2.31.0, urllib3 1.26.20, certifi 2026.10.23. Importing them under -W error::Warning raises nothing.
    • netrc: get_netrc_auth("http://example.com:@attacker.com/") returns attacker.com's entry, and http://example.com:8080/ still returns example.com's.
    • Zip extraction: with a planted cacert.pem in the temp directory, extract_zipped_paths() returns the zipped content at a new path on every call.
    • Network: pip download six==1.16.0, pip install --dry-run requests==2.31.0, and pip download run from inside the wheel all work.
  • Diff from the 24.0.post3 release asset: only the 13 files listed in the description plus dist-info. My local wheel's sha256 differs from yours (556b4c3e…) only because of build timestamps.

🟡 Non-blocking, upstream follow-up in the same function: psf/requests 47914226c2 ("Fix empty netrc entry usage", 2.33.x) makes get_netrc_auth() ignore an entry with no login and no password. Without it, a bare default line in .netrc returns ('', '') for every host, so pip sends empty Basic credentials instead of falling through to keyring or a prompt. It has no CVE and leaks nothing. It's a one-line change (if _netrc and any(_netrc):) if you want it here and in 26.0.1.post3.

@icanhasmath
icanhasmath merged commit f7dc586 into Python-3.7-compat Oct 29, 2026
2 of 23 checks passed
icanhasmath added a commit to ActiveState/cpython that referenced this pull request Oct 29, 2026
pip 24.0.post3 still vendored urllib3 1.26.17 and an unpatched requests
2.31.0. ActiveState pip 24.0.post4 (ActiveState/pip#16) vendors
ActiveState urllib3 1.26.20.post4 (CVE-2023-45803, CVE-2024-37891,
CVE-2025-50181, CVE-2025-66418, CVE-2025-66471, CVE-2026-21441,
CVE-2026-44431, CVE-2026-97687, CVE-2026-97689) and backports the
requests fixes for CVE-2024-47081 and CVE-2026-25645. certifi stays at
2026.10.23. The wheel is the release asset, sha256
749401b66b9dee893c560b12b8ee3b6f2d2d3600bdc047350c1a9d6db9047c17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants