diff --git a/Doc/conf.py b/Doc/conf.py index 7e355bae46f03b..a5e4d246b3041b 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -15,7 +15,8 @@ extensions = ['sphinx.ext.coverage', 'sphinx.ext.doctest', 'pyspecific', 'c_annotations', 'escape4chm', - 'asdl_highlight', 'peg_highlight'] + 'asdl_highlight', 'peg_highlight', + 'sphinx.ext.extlinks'] doctest_global_setup = ''' @@ -239,3 +240,8 @@ # bpo-40204: Disable warnings on Sphinx 2 syntax of the C domain since the # documentation is built with -W (warnings treated as errors). c_warn_on_allowed_pre_v3 = False + +# sphinx.ext.extlinks config +extlinks = { + 'cve': ('https://www.cve.org/CVERecord?id=CVE-%s', 'CVE-%s'), +} diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 2a6d82fa057b00..7845e486c470d0 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -478,6 +478,10 @@ Opening network connections .. versionchanged:: 3.5 Added support for SSL/TLS in :class:`ProactorEventLoop`. + .. versionchanged:: next + Raises a ``DeprecationWarning`` if ``ssl.check_hostname`` is ``True`` + and ``server_hostname`` is not supplied. In Python 3.13 and + later a ``ValueError`` is raised instead. .. seealso:: diff --git a/Doc/library/http.cookies.rst b/Doc/library/http.cookies.rst index 17792b200599bd..07c1a68b9edb2c 100644 --- a/Doc/library/http.cookies.rst +++ b/Doc/library/http.cookies.rst @@ -270,9 +270,9 @@ The following example demonstrates how to use the :mod:`http.cookies` module. Set-Cookie: chips=ahoy Set-Cookie: vienna=finger >>> C = cookies.SimpleCookie() - >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') + >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";') >>> print(C) - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" + Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;" >>> C = cookies.SimpleCookie() >>> C["oreo"] = "doublestuff" >>> C["oreo"]["path"] = "/" diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index 235a4deecfc38e..2b40d8f59045d8 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -95,15 +95,17 @@ the :mod:`glob` module.) .. function:: commonprefix(list) - Return the longest path prefix (taken character-by-character) that is a - prefix of all paths in *list*. If *list* is empty, return the empty string + Return the longest string prefix (taken character-by-character) that is a + prefix of all strings in *list*. If *list* is empty, return the empty string (``''``). - .. note:: + .. warning:: This function may return invalid paths because it works a - character at a time. To obtain a valid path, see - :func:`commonpath`. + character at a time. + If you need a **common path prefix**, then the algorithm + implemented in this function is not secure. Use + :func:`commonpath` for finding a common path prefix. :: diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index af9bf798da3b49..3d3ef430bfae36 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1716,6 +1716,12 @@ to speed up repeated connections from the same clients. :class:`SSLContext` representing a certificate chain that matches the server name. + If the callback assigns a new context to :attr:`SSLSocket.context`, any + further ClientHello message on the same connection (for example after a + TLS 1.3 HelloRetryRequest) is dispatched to the new context's + *sni_callback*, if it has one; the original callback is not called again + for that connection. + Due to the early negotiation phase of the TLS connection, only limited methods and attributes are usable like :meth:`SSLSocket.selected_alpn_protocol` and :attr:`SSLSocket.context`. @@ -1739,6 +1745,11 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.7 + .. versionchanged:: next + After the callback assigns a new :attr:`SSLSocket.context`, later + ClientHello messages on the connection are dispatched to the new + context's *sni_callback*. + .. attribute:: SSLContext.set_servername_callback(server_name_callback) This is a legacy API retained for backwards compatibility. When possible, @@ -1856,7 +1867,11 @@ to speed up repeated connections from the same clients. outgoing BIO. The *server_side*, *server_hostname* and *session* parameters have the - same meaning as in :meth:`SSLContext.wrap_socket`. + same meaning as in :meth:`SSLContext.wrap_socket`, and are validated in + the same way: in particular a :exc:`DeprecationWarning` is raised when + :attr:`~SSLContext.check_hostname` is enabled but no *server_hostname* is + given, since there would be no name to match the peer's certificate + against. In Python 3.13 and later a ``ValueError`` is raised instead. .. versionchanged:: 3.6 *session* argument was added. @@ -1865,6 +1880,13 @@ to speed up repeated connections from the same clients. The method returns on instance of :attr:`SSLContext.sslobject_class` instead of hard-coded :class:`SSLObject`. + .. versionchanged:: next + The *server_side*, *server_hostname* and *session* parameters are now + validated as :meth:`SSLContext.wrap_socket` validates them. Previously + a context with :attr:`~SSLContext.check_hostname` enabled and no + *server_hostname* was accepted, and verified the certificate chain but + never the peer's identity. + .. attribute:: SSLContext.sslobject_class The return type of :meth:`SSLContext.wrap_bio`, defaults to diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index d03f13db5d8a32..996759f53ec5f4 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -950,14 +950,27 @@ reused in custom filters: paths (in case the name is absolute even after stripping slashes, e.g. ``C:/foo`` on Windows). This raises :class:`~tarfile.AbsolutePathError`. + - Normalize filenames (:attr:`TarInfo.name`) that contain ``..`` components + using :func:`os.path.normpath`. + Note that this removes internal ``..`` components, which may change the + meaning of the name if it traverses symbolic links. - :ref:`Refuse ` to extract files whose absolute path (after following symlinks) would end up outside the destination. This raises :class:`~tarfile.OutsideDestinationError`. + - :ref:`Refuse ` to extract links (hard or soft) + whose name refers to the destination directory itself. + This raises :class:`~tarfile.OutsideDestinationError`. + Note that such files are refused even on platforms that do not support + symbolic links. - Clear high mode bits (setuid, setgid, sticky) and group/other write bits (:attr:`~stat.S_IWGRP`|:attr:`~stat.S_IWOTH`). Return the modified ``TarInfo`` member. + .. versionchanged:: next + + Filenames containing ``..`` components are now normalized. + .. function:: data_filter(/, member, path) Implements the ``'data'`` filter. diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index ae9abe7d48ac85..192b383f79be6e 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -917,8 +917,14 @@ These methods are available on :class:`HTTPPasswordMgr` and *uri* can be either a single URI, or a sequence of URIs. *realm*, *user* and *passwd* must be strings. This causes ``(user, passwd)`` to be used as - authentication tokens when authentication for *realm* and a super-URI of any of - the given URIs is given. + authentication tokens when authentication for *realm* and a super-URI of any + of the given URIs is given. If a URI includes a scheme, its credentials only + match authentication URIs with the same scheme or no scheme. A URI without a + scheme matches authentication URIs with any scheme. + + .. versionchanged:: next + Authentication credentials for URIs with a scheme are now scoped by + that scheme. .. method:: HTTPPasswordMgr.find_user_password(realm, authuri) diff --git a/Doc/library/wsgiref.rst b/Doc/library/wsgiref.rst index e92a689de0b9ba..3b9b9174d1ea9e 100644 --- a/Doc/library/wsgiref.rst +++ b/Doc/library/wsgiref.rst @@ -9,6 +9,11 @@ -------------- +.. warning:: + + :mod:`wsgiref` is a reference implementation and is not recommended for + production. The module only implements basic security checks. + The Web Server Gateway Interface (WSGI) is a standard interface between web server software and web applications written in Python. Having a standard interface makes it easy to use an application that supports WSGI with a number diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index d4bda83a60634e..e1a3e7595adef8 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -415,6 +415,7 @@ def run(self): issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I) gh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I) +cve_re = re.compile('CVE-([0-9]{4}-[0-9]{4,})', re.I) whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$") @@ -444,6 +445,7 @@ def run(self): content = issue_re.sub(r':issue:`\1`', content) # Fallback handling for the GitHub issue content = gh_issue_re.sub(r':gh:`\1`', content) + content = cve_re.sub(r':cve:`\1`', content) content = whatsnew_re.sub(r'\1', content) # remove first 3 lines as they are the main heading lines = ['.. default-role:: obj', ''] + content.splitlines()[3:] diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index abb74637fd946c..ef6176e6f7e533 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -362,9 +362,9 @@ on using nuget. What follows is a summary that is sufficient for Python developers. The ``nuget.exe`` command line tool may be downloaded directly from -``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the -tool, the latest version of Python for 64-bit or 32-bit machines is installed -using:: +``https://dist.nuget.org/win-x86-commandline/latest/nuget.exe``, for example, +using curl or PowerShell. With the tool, the latest version of Python for +64-bit or 32-bit machines is installed using:: nuget.exe install python -ExcludeVersion -OutputDirectory . nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory . diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 18bdf386085e8e..532880775aa313 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -1697,3 +1697,127 @@ tarfile :func:`~tarfile.TarFile.errorlevel` is zero. (Contributed by Matt Prodani and Petr Viktorin in :gh:`112887` and CVE 2025-4435.) + + +Notable changes in 3.9.25.1 +=========================== + +This is an ActiveState security release of the 3.9 line, which is past +its upstream end of life. + +It carries the security fixes released upstream in Python 3.10.20, +3.10.21, 3.10.22 and 3.11.18 that apply to 3.9, on top of 3.9.25, plus +three fixes that upstream released only for newer lines (or has not +released yet): the :mod:`imaplib`, :mod:`poplib` and :mod:`http.client` +response-framing items below. The full list of changes is in the +changelog; the notable ones are below. + +ssl +--- + +* The :mod:`ssl` module now rejects TLS certificate chains that contain one + of a set of compromised eMudhra intermediate CA certificates. The affected + certificates are identified by their public key, so cross-signed copies that + reuse the same key are blocked as well. It is a defense-in-depth backstop + that applies even when an application supplies its own root CA bundle, and + regardless of whether the platform trust store has removed trust. + (Contributed by Christian Heimes in :gh:`160040` for CVE 2026-115557.) + +ensurepip +--------- + +* The bundled ``pip`` is upgraded from 23.0.1 to 26.0.1.post1, the ActiveState + security release of pip 26.0.1 (the last pip that supports Python 3.9). + pip loads its own vendored ``certifi`` CA bundle in addition to the system + trust store; the bundle in 26.0.1.post1 (certifi 2026.10.19) drops the + compromised eMudhra emSign root, and the release also patches + CVE-2026-3219, CVE-2026-6357, CVE-2026-8643 and CVE-2026-13346. +* The bundled ``setuptools`` is upgraded from 79.0.1 to 82.0.1.post1, the + ActiveState security release of setuptools 82.0.1 (the last setuptools that + supports Python 3.9), fixing CVE-2026-59890. setuptools removed + ``pkg_resources`` in 82.0.0, so environments seeded by :mod:`ensurepip` or + :mod:`venv` no longer include it; install an older setuptools if an + application still imports it. + +tarfile +------- + +* The ``data`` and ``tar`` extraction filters reject several more path and + link escapes: hard links to symbolic links (:gh:`157190`), symlinks created + through the hard-link extraction fallback (:gh:`151558`), members whose name + leaves the destination and comes back (:gh:`155999`), link targets that + differ from the value written to disk (:gh:`149486`), and links that replace + the destination directory itself (:gh:`160097`, CVE 2026-114583). +* :meth:`~tarfile.TarFile.extract` applies the filter to link fallbacks and + honors a ``None`` result from it (:gh:`151987`, :gh:`157265`). + +http, email, urllib and wsgiref +------------------------------- + +* Control characters are rejected in :mod:`http.cookies` morsels + (:gh:`143919`, :gh:`145599`), ``data:`` URL media types (:gh:`143925`), + :mod:`wsgiref` headers and status lines (:gh:`143916`, :gh:`144370`) and + HTTP tunnel headers (:gh:`146211`). +* :mod:`http.client` bounds memory use for large responses (:gh:`119451`) and + limits trailer lines and interim responses (:gh:`150743`); :mod:`http.server` + gets the same bound for CGI on Windows (:gh:`119452`). +* :class:`~urllib.request.HTTPPasswordMgr` credentials are scoped by URL + scheme (:gh:`155694`, CVE 2026-15806). +* :mod:`email` refuses to write unsafely folded headers (:gh:`144125`) and + folds comments correctly (:gh:`143935`, :gh:`87720`). +* :class:`http.cookiejar.DefaultCookiePolicy` checks the domain of cookies + with a negative ``Version`` (:gh:`160300`). +* :mod:`http.client` only accepts a ``Content-Length`` and chunk sizes that + match the HTTP grammar; signs, underscores and ``0x`` prefixes are no + longer accepted (:gh:`150751`, CVE 2026-103995). +* :mod:`imaplib` rejects NUL, CR and LF in command arguments (:gh:`143921`, + CVE 2025-15366) and :mod:`poplib` rejects control characters in commands + (:gh:`143923`, CVE 2025-15367), which could be used to inject extra + commands. + +XML +--- + +* The bundled libexpat is updated from 2.7.3 to 2.9.0. +* :mod:`xml.parsers.expat` seeds expat's hash with 16 bytes of entropy when + available (:gh:`149018`) and no longer crashes on deeply nested content + models (:gh:`145986`). +* Quadratic behavior is removed from :mod:`xml.dom.minidom` (:gh:`142145`) + and :mod:`xml.etree.ElementTree` path predicates (:gh:`152674`). + +Denial-of-service and memory-safety fixes +----------------------------------------- + +* Quadratic or exponential time is removed from :class:`html.parser.HTMLParser` + (:gh:`153030`), :mod:`configparser` (:gh:`146333`), :class:`csv.Sniffer` + (:gh:`109638`, :gh:`98820`), :func:`unicodedata.normalize` (:gh:`149079`) + and :mod:`plistlib` (:gh:`119342`). +* :mod:`zipfile` bounds decompression per read for bzip2 and LZMA members + (:gh:`156002`). +* Use-after-free and overflow fixes in :mod:`ssl` (:gh:`144833`, + :gh:`156293`), :mod:`bz2` and :mod:`lzma` (:gh:`148395`, :gh:`150599`) and + :class:`float` / :class:`complex` formatting (:gh:`158446`). + +Other +----- + +* ``SourcelessFileLoader`` opens ``.pyc`` files with :func:`io.open_code` + (:gh:`145506`, CVE 2026-2297). +* :mod:`webbrowser` rejects URLs starting with a dash (:gh:`143930`, + :gh:`148169`) and runs ``osascript`` by absolute path (:gh:`137586`). +* :mod:`mimetypes` no longer reads :data:`mimetypes.knownfiles` by default on + Windows (:gh:`159428`). +* :mod:`shutil.unpack_archive` no longer writes outside the destination for + some ZIP files on Windows (:gh:`146581`). +* :mod:`configparser` normalizes line endings in multi-line values + (:gh:`143927`). +* :mod:`stringprep` and the ``idna`` codec stop using Unicode attributes beyond + :rfc:`3454` (:gh:`155292`). +* :func:`ssl.SSLContext.wrap_bio` and :mod:`asyncio` TLS methods validate + their arguments like :meth:`~ssl.SSLContext.wrap_socket` (:gh:`156793`). +* ``ftplib.ftpcp`` no longer trusts the IPv4 address in a PASV reply + (:gh:`87451`, the CVE-2021-4189 fix). + +Not included: the expat amplification-tuning API from :gh:`90949`. The +protection itself (CVE-2025-59375) is built into libexpat 2.7.2 and later +and is on by default, so 3.9 gets it from the bundled libexpat 2.9.0. diff --git a/Include/patchlevel.h b/Include/patchlevel.h index ee45252aa01a94..7d80b1a1c18afc 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.9.25" +#define PY_VERSION "3.9.25.1" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Include/pyexpat.h b/Include/pyexpat.h index 9824d099c3df7d..5257bc269fa1a9 100644 --- a/Include/pyexpat.h +++ b/Include/pyexpat.h @@ -52,6 +52,9 @@ struct PyExpat_CAPI int (*SetHashSalt)(XML_Parser parser, unsigned long hash_salt); /* might be NULL for expat < 2.6.0 */ XML_Bool (*SetReparseDeferralEnabled)(XML_Parser parser, XML_Bool enabled); + /* might be NULL for expat < 2.8.0 */ + XML_Bool (*SetHashSalt16Bytes)( + XML_Parser parser, const uint8_t entropy[16]); /* always add new stuff to the end! */ }; diff --git a/Include/pyhash.h b/Include/pyhash.h index 4437b870332bde..d6a05621d62118 100644 --- a/Include/pyhash.h +++ b/Include/pyhash.h @@ -40,14 +40,14 @@ PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void*, Py_ssize_t); * pppppppp ssssssss ........ fnv -- two Py_hash_t * k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t * ........ ........ ssssssss djbx33a -- 16 bytes padding + one Py_hash_t - * ........ ........ eeeeeeee pyexpat XML hash salt + * eeeeeeee eeeeeeee eeeeeeee pyexpat XML hash salt * * memory layout on 32 bit systems * cccccccc cccccccc cccccccc uc * ppppssss ........ ........ fnv -- two Py_hash_t * k0k0k0k0 k1k1k1k1 ........ siphash -- two uint64_t (*) * ........ ........ ssss.... djbx33a -- 16 bytes padding + one Py_hash_t - * ........ ........ eeee.... pyexpat XML hash salt + * eeeeeeee eeeeeeee eeee.... pyexpat XML hash salt * * (*) The siphash member may not be available on 32 bit platforms without * an unsigned int64 data type. @@ -72,7 +72,9 @@ typedef union { Py_hash_t suffix; } djbx33a; struct { - unsigned char padding[16]; + /* 16 bytes for XML_SetHashSalt16Bytes */ + uint8_t hashsalt16[16]; + /* 4/8 bytes for legacy XML_SetHashSalt */ Py_hash_t hashsalt; } expat; } _Py_HashSecret_t; diff --git a/Lib/configparser.py b/Lib/configparser.py index 8dd5c13bcc0092..ffdc5d97022fd0 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -567,7 +567,10 @@ class RawConfigParser(MutableMapping): \] # ] """ _OPT_TMPL = r""" - (?P