Skip to content

3.9.25.1: all upstream security fixes since 3.9.25 + eMudhra ssl distrust, ensurepip pip 26.0.1.post1 - #88

Merged
icanhasmath merged 80 commits into
3.9.25.xfrom
3.9.25.1-security
Oct 28, 2026
Merged

icanhasmath merged 80 commits into
3.9.25.xfrom
3.9.25.1-security

Conversation

@icanhasmath

Copy link
Copy Markdown

ActiveState security release 3.9.25.1 of the end-of-life 3.9 line (upstream's last release was 3.9.25 on 2025-10-31).

Brings 3.9 up to date with every upstream security fix released since 3.9.25, including the eMudhra / emSign CA key compromise (python.org advisory).

What's in it

Hand-ported for 3.9

Each port is explained under "3.9.25.x:" in the body of the commit it affects:

Not included

pythongh-90949, the Python API for tuning expat's allocation and billion-laughs limits, is a feature. The protection itself (CVE-2025-59375) is built into libexpat 2.7.2+ and is on by default, and this branch bundles libexpat 2.9.0. 3.9's pyexpat predates module state, so the API wasn't ported. CI-only, release-tooling and MSI-removal commits from 3.10 are also skipped.

Windows build customizations stay as Platform inventory patches and are not part of this branch.

Testing

Linux x86_64 with OpenSSL 3.0.13, out-of-tree build, with zlib, bz2, lzma and ctypes available:

  • Full python -m test -j8 -u all,-network,-gui,-cpu,-largefile,-audio,-urlfetch: 396 OK, 0 failed.
  • After the version bump and pip swap: test_sys test_platform test_sysconfig test_venv test_ensurepip test_ssl test_pyexpat pass.
  • A fresh venv gets pip 26.0.1.post1 with certifi 2026.10.19, and pip download from PyPI works.

Windows was not built.

After merge: tag v3.9.25.1 on the merge commit.

gpshead and others added 30 commits October 27, 2026 14:18
…Headers

pythongh-143916: Reject control characters in wsgiref.headers.Headers  (pythonGH-143917)

* Add 'test.support' fixture for C0 control characters
* pythongh-143916: Reject control characters in wsgiref.headers.Headers

(cherry picked from commit f7fceed)
(cherry picked from commit 22e4d55)

Co-authored-by: Seth Michael Larson <seth@python.org>
(cherry picked from commit 2f84024)
…ypes (python#144115)

(cherry picked from commit f25509e)
(cherry picked from commit 2c9c746)
(cherry picked from commit 34d76b0)
…thon#144094)

(cherry picked from commit 95746b3)

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
(cherry picked from commit 7852d72)
…ator (python#144180)

pythongh-144125: email: verify headers are sound in BytesGenerator
(cherry picked from commit 052e55e)

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Denis Ledoux <dle@odoo.com>
Co-authored-by: Denis Ledoux <5822488+beledouxdenis@users.noreply.github.com>
Co-authored-by: Petr Viktorin <302922+encukou@users.noreply.github.com>
Co-authored-by: Bas Bloemsaat <1586868+basbloemsaat@users.noreply.github.com>
(cherry picked from commit 8cdf620)
…ial of service in http.server (pythonGH-142216) (python#142299)

[3.14] pythongh-119452: Fix a potential virtual memory allocation denial of service in http.server (pythonGH-142216)

The CGI server on Windows could consume the amount of memory specified
in the Content-Length header of the request even if the client does not
send such much data. Now it reads the POST request body by chunks,
therefore the memory consumption is proportional to the amount of sent
data.
(cherry picked from commit 0e4f4f1)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
(cherry picked from commit f2088a5)
…earing (pythonGH-142146) (python#142213)

* pythongh-142145: Remove quadratic behavior in node ID cache clearing (pythonGH-142146)

* Remove quadratic behavior in node ID cache clearing

Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>

* Add news fragment

---------
(cherry picked from commit 08d8e18)

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>

* [3.14] pythongh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (pythonGH-142794) (python#142818)

pythongh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (pythonGH-142794)
(cherry picked from commit 1cc7551)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>

* pythongh-142145: relax the no-longer-quadratic test timing (pythonGH-143030)

* pythongh-142145: relax the no-longer-quadratic test timing

* require cpu resource
(cherry picked from commit 8d2d7bb)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>

* merge NEWS entries into one

---------

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
(cherry picked from commit c97e875)
…ent (pythonGH-119454) (python#142142)

pythongh-119451: Fix a potential denial of service in http.client (pythonGH-119454)

Reading the whole body of the HTTP response could cause OOM if
the Content-Length value is too large even if the server does not send
a large amount of data. Now the HTTP client reads large data by chunks,
therefore the amount of consumed memory is proportional to the amount
of sent data.
(cherry picked from commit 5a4c4a0)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
(cherry picked from commit 5dc1016)
…nprefix

pythongh-74453: Add stronger security warning to os.path.commonprefix (pythonGH-144401)
(cherry picked from commit 4e15b8d)

Co-authored-by: Seth Michael Larson <seth@python.org>
(cherry picked from commit e3109d6)
pythongh-144484: Warn users not to use wsgiref in production
(cherry picked from commit 7e777c5)

Co-authored-by: Seth Michael Larson <seth@python.org>
(cherry picked from commit e1a8a03)
…pythonGH-119343) (python#142151)

Reading a specially prepared small Plist file could cause OOM because file's
read(n) preallocates a bytes object for reading the specified amount of
data. Now plistlib reads large data by chunks, therefore the upper limit of
consumed memory is proportional to the size of the input file.
(cherry picked from commit 694922c)

(cherry picked from commit 568342c)
…() fails (pythonGH-144843) (python#144862)

pythongh-144833: Fix use-after-free in SSL module when SSL_new() fails (pythonGH-144843)

In newPySSLSocket(), when SSL_new() returns NULL, Py_DECREF(self)
was called before _setSSLError(get_state_ctx(self), ...), causing
a use-after-free. Additionally, get_state_ctx() was called with
self (PySSLSocket*) instead of sslctx (PySSLContext*), which is
a type confusion bug.

Fix by calling _setSSLError() before Py_DECREF() and using
sslctx instead of self for get_state_ctx().
(cherry picked from commit c91638c)

Co-authored-by: Ramin Farajpour Cami <ramin.blackhat@gmail.com>
(cherry picked from commit 4be624a)
…45140)

pythongh-143916: Allow HTAB in wsgiref header values
(cherry picked from commit 66da7bf)

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Victor Stinner <vstinner@python.org>
(cherry picked from commit c592227)
…ythonGH-143936) (python#144350)

Fix a bug in the folding of comments when flattening an email message
using a modern email policy. Comments consisting of a very long sequence of
non-foldable characters could trigger a forced line wrap that omitted the
required leading space on the continuation line, causing the remainder of
the comment to be interpreted as a new header field. This enabled header
injection with carefully crafted inputs.
(cherry picked from commit 17d1490)

Co-authored-by: Seth Michael Larson seth@python.org
Co-authored-by: Denis Ledoux dle@odoo.com

- Issue: Fix folding of long comments of unfoldable characters in email headers python#143935

Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Co-authored-by: Seth Michael Larson <seth@python.org>
(cherry picked from commit 003b831)
…honGH-143931) (pythonGH-146359)

(cherry picked from commit 82a24a4)

Co-authored-by: Seth Michael Larson <seth@python.org>
(cherry picked from commit ad4d5ba)
… coverage (pythonGH-146476) (pythonGH-148045) (pythonGH-148051) (pythonGH-148053)

(cherry picked from commit cc02351)
(cherry picked from commit 89bfb8e)
(cherry picked from commit 3681d47)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit 591ed89)
… path (pythonGH-137584) (python#148177)

Co-authored-by: Fionn <1897918+fionn@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
(cherry picked from commit e70ff77)
…del` in `pyexpat.c` (CVE 2026-4224) (pythonGH-145987) (python#146002)

* [3.10] pythongh-145986: Avoid unbound C recursion in `conv_content_model` in `pyexpat.c` (CVE 2026-4224) (pythonGH-145987)

Fix C stack overflow (CVE-2026-4224) when an Expat parser
with a registered `ElementDeclHandler` parses inline DTD
containing deeply nested content model.

---------
(cherry picked from commit eb0e8be)
(cherry picked from commit e5caf45)

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>

* Update Misc/NEWS.d/next/Security/2026-03-14-17-31-39.gh-issue-145986.ifSSr8.rst

---------

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
(cherry picked from commit af856a7)
…of dash-prefix check (pythonGH-148170) (python#148521)

(cherry picked from commit d22922c)
(cherry picked from commit c5767a7)
pythonGH-148396) (python#148505)

Fix dangling input pointer after `MemoryError` in _lzma/_bz2/_ZlibDecompressor.decompress

(cherry picked from commit 8fc66ae)
(cherry picked from commit 47128e6)
…eLoader uses io.open_code (pythonGH-145507) (python#145516)

* pythongh-145506: Fixes CVE-2026-2297 by ensuring SourcelessFileLoader uses io.open_code (pythonGH-145507)
(cherry picked from commit a51b1b5)

Co-authored-by: Steve Dower <steve.dower@python.org>

* Regenerate importlib_external.h

* Fix blurb entry

The `:cve:` role is not available on this branch.

---------

Co-authored-by: Steve Dower <steve.dower@python.org>
Co-authored-by: Stan Ulbrych <stan@ulbrych.org>
Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
(cherry picked from commit 876858c)
… long name handling (python#145816)

pythongh-141707: Skip TarInfo DIRTYPE normalization during GNU long name handling
(cherry picked from commit 42d754e)

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Eashwar Ranganathan <eashwar@eashwar.com>
(cherry picked from commit 72dde10)
…s in wsgiref.handlers for security (python#144371) (python#145673)

(cherry picked from commit 10cf7e2)
…nGH-151982) (python#151996)

pythongh-151981: Make tarfile._Stream.seek break at EOF (pythonGH-151982)
(cherry picked from commit f50bf13)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Stan Ulbrych <stan@python.org>
(cherry picked from commit eb63c0f)
…pythonGH-149648) (python#149797)

pythongh-87451: Apply CVE-2021-4189 PASV fix to ftplib.ftpcp() (pythonGH-149648)

ftpcp() called parse227() directly and passed the source server's
self-reported PASV IPv4 address to the target server's PORT command,
bypassing the CVE-2021-4189 fix that was applied only to FTP.makepasv().
A malicious source FTP server could use this to redirect the target
server's data connection to an arbitrary host:port (SSRF).

ftpcp() now uses the source server's actual peer address, honoring the
existing trust_server_pasv_ipv4_address opt-out, the same as makepasv().

Thanks to Qi Ding at Aurascape AI for the report. (GHSA-w8c5-q2xf-gf7c)
(cherry picked from commit eac4fe3)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
(cherry picked from commit 2bbcf3f)
…ythonGH-146… (python#148350)

[3.10] pythongh-146211: Reject CR/LF in HTTP tunnel request headers (pythonGH-146212)
(cherry picked from commit 05ed7ce)

Co-authored-by: Illia Volochii <illia.volochii@gmail.com>
(cherry picked from commit 58703ec)
…http.cookies.Morsel.update()` (python#145600) (python#146027)

* pythongh-145599, CVE 2026-3644: Reject control characters in `http.cookies.Morsel.update()` (python#145600)

Reject control characters in `http.cookies.Morsel.update()` and `http.cookies.BaseCookie.js_output`.

Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Victor Stinner <victor.stinner@gmail.com>
(cherry picked from commit 57e88c1)

* Update Misc/NEWS.d/next/Security/2026-03-06-17-03-38.gh-issue-145599.kchwZV.rst

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>

---------

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
(cherry picked from commit 556aa09)
…ython#150600) (python#151133)

* [3.12] pythongh-150599: Prevent bz2 decompressor reuse after errors (python#150600) (python#151054)

(cherry picked from commit 5755d0f)

* We can just make it a plain assignment, 1a9cdaf was 3.15+ anyway.

(cherry picked from commit 938ec03)
StanFromIreland and others added 20 commits October 27, 2026 14:27
…stination but comes back (pythonGH-156000) (python#156046)

(cherry picked from commit 9768834)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
(cherry picked from commit fc32a70)
…e wrap_socket() (pythonGH-158503) (python#158533)

* [3.10] pythongh-156793: Validate SSLContext.wrap_bio() parameters like wrap_socket() (pythonGH-158503) (pythonGH-158513)

* [3.12] pythongh-156793: Validate SSLContext.wrap_bio() parameters like wrap_socket() (pythonGH-158503)
(cherry picked from commit 1697ea3)

* Raise a DeprecationWarning instead of ValueError
(cherry picked from commit 869069d)

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Seth Larson <seth@python.org>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: T. Wouters <thomas@python.org>

* pythongh-156793: Start the SSL handshake in the 3.10 hostname validation tests

---------

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Seth Larson <seth@python.org>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Pablo Galindo Salgado <pablogsal@gmail.com>
(cherry picked from commit 5867d4e)
…se (pythonGH-134271) (python#159065)

pythongh-87720 Fix additional header refolding-quoting edge case (pythonGH-134271)

In this case, the higher level syntactic unit fit on the remainder of
the line in un-encoded format, and that higher level unit was
not a ptext or vtext, so the encoding check failed.  To fix this, we
look inside the current unit to see if has anything that was
originally encoded.
(cherry picked from commit 5a22a62)

Co-authored-by: R. David Murray <rdmurray@bitdance.com>
(cherry picked from commit 26ee788)
(cherry picked from commit 50e5a0e)
… Windows (pythonGH-159445) (python#159573)

Make knownfiles empty on Windows so that only the registry is used by default. Files passed to init() or assigned to knownfiles explicitly are still read.
(cherry picked from commit 04a29bf)
(cherry picked from commit 35dd324)
(cherry picked from commit f3c1882)
… (python#160261)

(cherry picked from commit 0071fb5)
(cherry picked from commit 25f598d)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
(cherry picked from commit b370d7a)

3.10.22.x: 3.10 builds the bundled expat from setup.py, not from
LIBEXPAT_OBJS in Makefile.pre.in, so xcs.c is added to setup.py's
expat_sources (and xcs.h to expat_depends) instead.

(cherry picked from commit 95dadd7)

3.9.25.x: 3.9's pyexpat predates the module-state error helpers
(set_xml_error / SET_XML_ERROR), so the 64-bit line/column switch is
applied to 3.9's set_error() directly: it now reads
EXPAT_GetCurrent{Line,Column}Number (XML_GetError{Line,Column}Number
are aliases of the current-position getters in expat), formats them
with %llu, and stores them through a new set_error_attr_location()
that uses PyLong_FromUnsignedLongLong().
…p.cookiejar (pythonGH-160301) (python#160649)

pythongh-160300: Reject cookies with a negative version in http.cookiejar (pythonGH-160301)

DefaultCookiePolicy only applied its domain checks to cookies with
version 0 or a positive version.

ANT-2026-TA36I7V7
(cherry picked from commit d59d968)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
(cherry picked from commit 6229895)
(cherry picked from commit cd2a617)
…rfile.tar_filter` (pythonGH-160098) (python#160343)

pythongh-160097: Reject links replacing the destination in `tarfile.tar_filter` (pythonGH-160098)
(cherry picked from commit e7e0b35)

Co-authored-by: Stan Ulbrych <stan@python.org>
(cherry picked from commit bc93f8a)
(cherry picked from commit f2435de)
…keys (CVE-2026-115557) (pythonGH-160042) (pythonGH-160572)

The ssl module now rejects TLS certificate chains that contain any of a set of
compromised eMudhra intermediate CA keys.  This is an extraordinary measure and
a last line of defense: removing trust is primarily the job of the platform root
store, and the built-in denylist only blocks connections while that update has
not yet reached the OS, a container image, or a bundled trust store in various
Python packages.  Entries are added to the denylist only under a very high
threshold.

The denylist matches on the public key (the SHA-256 digest of the RFC-5280
SubjectPublicKeyInfo), so it also covers the cross-signed certificates that
reuse one of the distrusted keys.

(cherry picked from commit 7dd8b47)

Co-authored-by: Christian Heimes <christian@python.org>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit af52b41)

3.10.22.x: 3.10 keeps the test certificates directly in Lib/test (no
certdata/ subdirectory) and builds _ssl from setup.py, so distrusted.zip
lands in Lib/test and distrusted_spki.h is added to the _ssl depends in
setup.py instead of MODULE__SSL_DEPS.

(cherry picked from commit 2691076)
….9.25.1) (python#160591)

(cherry picked from commit e4f63e6, via the 3.10.22.x branch)

3.9.25.x: the entry goes into Doc/whatsnew/3.9.rst under a new
"Notable changes in 3.9.25.1" section.
The security backports from the 3.10 branch carry tests written against
3.10's test.support layout. 3.9 has no test.support.os_helper; the same
helpers (TESTFN, unlink, temp_dir, change_cwd, EnvironmentVarGuard,
can_symlink, skip_unless_symlink) live directly on test.support, so
os_helper.X is spelled support.X in test_plistlib, test_shutil,
test_tarfile and test_webbrowser.

test_tarfile's pythongh-141707 test also uses the per-class format
attribute that pythongh-91387 added to GNUReadTest / PaxReadTest in 3.10;
those two attributes are added here. Tests only, no library change.
The pip 23.0.1 bundled in ensurepip carries a vendored certifi that
still trusts the compromised eMudhra emSign root. Upstream's fixed pip
(26.2.2) requires Python >= 3.10, so 3.9 takes the ActiveState security
release of pip 26.0.1, the last pip line that supports 3.9:
https://github.com/ActiveState/pip/releases/tag/26.0.1.post1

It vendors certifi 2026.10.19 (GHSA-6q48-6c7w-9gw2) and patches
CVE-2026-3219, CVE-2026-6357, CVE-2026-8643 and CVE-2026-13346.

Wheel sha256 6cd40409a936257162fe17e1585488a1804798057330fbba5b152a858919226d,
matching the release notes.
ActiveState security release of the end-of-life 3.9 line: the security
fixes from upstream 3.10.20, 3.10.21, 3.10.22 and 3.11.18 that apply to
3.9, including the pythongh-160040 eMudhra distrust in ssl, plus ensurepip's
bundled pip upgraded to ActiveState's 26.0.1.post1.
The pythongh-119452 backport (http.server CGI POST read on non-fork platforms,
i.e. Windows) stops reading on 'except TimeoutError'. socket.timeout
only became an alias of TimeoutError in 3.10; on 3.9 it is a separate
OSError subclass, so the timeout escaped the loop, the handler died and
the client got an empty response for an oversized Content-Length.
Caught by test_httpservers.test_large_content_length_truncated on the
Windows CI jobs. socket.timeout is the same object as TimeoutError on
3.10+, so this matches upstream behavior.
…nGH-143922, pythonGH-153067) (pythonGH-153137) (pythonGH-153287)

Combined backport of pythonGH-143922, which rejected all control characters,
and pythonGH-153067, which narrowed the check to NUL, CR and LF.  Other
control characters are valid in quoted strings and are sent quoted.

(cherry picked from commit 6262704)
(cherry picked from commit d0921ef)
(cherry picked from commit 2981822)

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 71926d9)

3.10.22.x: 3.10 has no IMAP argument quoting (the _non_astring_char /
_quoted machinery is 3.13+), so only the NUL/CR/LF rejection is ported.
The test keeps the rejection check and checks that other control
characters are still accepted, without the 3.13 quoting assertion.

(cherry picked from commit c1d1d62)
(cherry picked from commit b234a2b)

3.10.22.x / 3.9.25.x: test import conflict only (asynchat/asyncore come from
the stdlib on these lines, not test.support).

(cherry picked from commit acaf81f)
…ythonGH-150752)

RFC 9112 defines Content-Length as 1*DIGIT and chunk-size as 1*HEXDIG, but int() also accepts a sign, underscores, surrounding whitespace and an 0x prefix, so malformed framing values were parsed instead of rejected.

(cherry picked from commit 2eb2af8)
(cherry picked from commit 833a27a)
…http.client (pythonGH-159156)

pythongh-150752 started validating the Content-Length value against the
RFC 9112 grammar (1*DIGIT), but the compat32 header parser keeps
trailing SP/HTAB in the field value, so "Content-Length: 5 " is now
treated as a missing header. HTTPResponse then assumes that the
connection will close and read() blocks until the server closes it,
which never happens on a keep-alive connection.

RFC 9112, section 5.1 says that optional whitespace around a field
value is excluded by parsers. Strip SP/HTAB before the grammar check;
values such as "+5", "5_0" and "5 0" are still rejected.

(cherry picked from commit 9f52b44)
(cherry picked from commit dbefda3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setuptools 79.0.1 is affected by CVE-2026-59890 (GHSA-h35f-9h28-mq5c,
MANIFEST.in exclusion bypass via Unicode normalization). Upstream fixed
it in 83.0.0, which requires Python >= 3.10, so 3.9 takes the ActiveState
security release of setuptools 82.0.1, the last line that supports 3.9:
https://github.com/ActiveState/setuptools/releases/tag/v82.0.1.post1

That release has no wheel asset; this wheel was built from the
v82.0.1.post1 tag (838d0d2) with pip wheel --no-build-isolation under
3.9.25.1. Its file list matches PyPI's setuptools 82.0.1 wheel and the
only content changes are the CVE fix (setuptools/command/egg_info.py,
setuptools/unicode_utils.py, setuptools/tests/test_manifest.py). sha256
b0fbd65ea42a7efb10eb58c80e03c82fa9b017c4b857533ccfccf1b87c47120a.

GHSA-xmj3-9gf7-h52w (low, Windows legacy wheel-as-egg install, fixed
upstream in 84.1.0) is not fixed in 82.0.1.post1.

setuptools 82.0.0 removed pkg_resources, so venvs seeded by ensurepip
no longer have it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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.

No blockers. Approving.

Coverage. I compared this branch against upstream:

  • 3.10.20 → 3.10.22: every non-release commit is either here or already in 3.9.25, except CI, workflow, docs-build and MSI-removal commits.
  • 3.11.18: all its security commits are here: pythongh-87720, 159428, 158876 (expat 2.9.0), 160300, 160097 and 160040.
  • Security NEWS on 3.12 / 3.13 / 3.14 / main since October 2025: every entry is ported, in 3.9.25 already, or does not apply to 3.9:
    • pythongh-160279 (venv runs pip with the venv on the import path): does not apply. 3.9's venv still runs ensurepip with -I, and ensurepip passes -I on to its pip subprocess.
    • pythongh-160420 (zoneinfo drive letters on Windows): does not apply. 3.9 loads tzdata through importlib.resources.open_binary, whose _normalize_path rejects C:\file before it is joined to a path.
    • pythongh-151544 / CVE-2026-12003 (Setup.local landmark): does not apply. The Windows case is in getpath.py, and 3.9's PC/getpathp.c has no such landmark.
    • pythongh-148808: does not apply; sock_recvfrom_into doesn't exist in 3.9.
    • pythongh-157579 (tempfile cleanup race): that is the separate 3.9.25.2 PR.
  • Upstream follow-ups: each ported issue's later commits are present too: the pythongh-143921 narrowing, the pythongh-150751 whitespace follow-up and the re-applied pythongh-119452. The 3.9-only socket.timeout fix in http/server.py is right, since socket.timeout is not TimeoutError before 3.10.

Vendored code and data.

  • Modules/expat/ is identical to 3.11.18 except expat_config.h and pyexpatns.h:
    • XML_POOR_ENTROPY is defined in setup.py and the vcxproj files instead, as on 3.10.
    • The two missing XML_SetAllocTracker* renames are harmless: in the built pyexpat.so those symbols are local, and only PyInit_pyexpat is exported.
  • Modules/_ssl/distrusted_spki.h and the _ssl.c distrust code are identical to 3.11.18 and to the 3.10.22.1 PR, apart from build plumbing.
  • The hand-merged pythongh-156293 SNI fix is equivalent to upstream: the same strong references to the socket's context and callback, with Py_INCREF in place of Py_NewRef.
  • The pip wheel is byte-identical to the ActiveState pip 26.0.1.post1 release asset (6cd40409…226d).
  • The setuptools wheel's 319 .py files are byte-identical to the ActiveState setuptools v82.0.1.post1 tag, with Requires-Python: >=3.9.

Build and tests. Built base and head out of tree on Debian bookworm (OpenSSL 3.5.7). Head reports 3.9.25.1, expat 2.9.0. Full python -m test -j16 -u all,-network,-gui,-cpu,-largefile,-audio,-urlfetch:

  • base: 385 OK, 15 failed
  • head: 387 OK, 13 failed

Head's failures are a strict subset of base's. The 13 shared ones (test_import, test_pydoc, test_runpy and so on) come from my read-only source mount. Head additionally passes test_ssl and test_socket, which fail on base.

🟡 Mention the setuptools change in the PR description. The last commit replaces setuptools 79.0.1 with 82.0.1.post1, for CVE-2026-59890. setuptools 82 removed pkg_resources, so every venv that ensurepip seeds on 3.9.25.1 loses it. The commit message and whatsnew say so, but the PR description lists only the pip upgrade, and this is the change most likely to break existing 3.9 users. It may also be worth weighing a patched 79.0.1 for this security-only line. If 82.0.1.post1 stays, the release notes should tell users who need pkg_resources to pip install "setuptools<82".

Not assessed: a Windows build, and the network-dependent tests.

@icanhasmath

Copy link
Copy Markdown
Author

Accepting the standard core setuptools drops pkg_resources.

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.