Skip to content

Integrate 2.7.18.14 into 2.7#85

Open
icanhasmath wants to merge 24 commits into
2.7from
2.7.18.14-part2
Open

Integrate 2.7.18.14 into 2.7#85
icanhasmath wants to merge 24 commits into
2.7from
2.7.18.14-part2

Conversation

@icanhasmath

Copy link
Copy Markdown

Part 2 of 2: full 2.7.18.14 CVE batch + release + Windows remediation

Second and final PR integrating the historic 2.7.18.14 work into the 2.7 branch. Part 1 (#84) landed the pre-2.7.18.14 security/build work; 2.7 and this branch's base now share identical tree content, so this PR adds only the genuinely new 2.7.18.14 commits — no part-1 duplicates.

Built by rebasing the 25093f561ae..2.7.18.14 range onto origin/2.7 with --rebase-merges. The resulting tip tree is byte-for-byte identical to the tagged 2.7.18.14 branch, and the Windows-remediation merge topology is preserved. PY_VERSION advances to 2.7.18.14.

CVE / security cluster

  • Address CVE-2025-8194 (tarfile) and CVE-2026-4786 (webbrowser)
  • Reject control characters in header/command APIs (injection cluster)
  • Reject header injection when generating email messages (CVE-2024-6923)
  • Harden zipfile against overlapping entries and bad ZIP64 locator
  • Reject misplaced square brackets in parsed URL hosts (CVE-2025-0938)
  • Fix quadratic complexity in minidom and os.path.expandvars
  • Fix quadratic complexity in HTMLParser at EOF (CVE-2025-6069)
  • Add strict validation option to base64.b64decode
  • Don't normalize AREGTYPE follow-up headers to DIRTYPE (CVE-2025-13462)
  • 2.7.18.14 Release (patchlevel + NEWS.d)

Windows / UCRT remediation (preserved merge)

  • Fix Windows test-suite hangs on invalid file descriptors
  • asyncore: recognize WSAECONNRESET/WSAESHUTDOWN as disconnects on Windows
  • ctypes: fix find_msvcrt on VS2015+ builds; skip win64 pointer-truncation test
  • ssl: accept WSAENOTCONN when probing whether a socket is connected
  • test_socket / test_ctypes / test_ssl / test_ftplib / test.support: forward-ported guards and skips
  • 2.7.18.14 Release: document Windows regression remediation
  • Merge of the bc3b091 test-fix branch into the remediation line

Verification

  • git diff origin/2.7.18.14 HEAD is empty (identical tree)
  • origin/2.7 is an ancestor of this branch (clean rebase, no force-merge)
  • Range contains exactly the 24 part-2 commits; first commit is the CVE-2025-8194/2026-4786 fix, not any part-1 work

Supersedes the closed #83 (which carried duplicate part-1 commits).

icanhasmath and others added 24 commits June 10, 2026 13:53
CVE-2025-8194: tarfile accepted negative member offsets (reachable via a
PAX extended header with a negative "size"), causing TarInfo._block to
return a negative block count that moved the archive offset backwards and
could hang (seekable files) or raise StreamError (streams). _block now
rejects negative counts with InvalidHeaderError.

CVE-2026-4786 / CVE-2026-4519: webbrowser.open passed attacker-controlled
URLs to the browser command line unvalidated, so a URL starting with "-"
could be treated as a command-line option (argument injection). Add
BaseBrowser._check_url and call it from GenericBrowser, BackgroundBrowser
and UnixBrowser; UnixBrowser validates the URL after %action expansion and
substitutes %action before %s so %action cannot smuggle a leading dash.

Adds tests in test_tarfile (negative _block count and a PAX negative-size
archive) and a new test_webbrowser covering URL rejection and the %action
bypass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backports a uniform "reject C0 control characters and DEL" defense across
several stdlib modules where unvalidated user input was emitted into
protocol headers/commands, enabling CR/LF (and NUL) injection:

- wsgiref.headers.Headers: validate name/value in __init__, __setitem__
  and add_header (CVE-2026-0865), raising ValueError.
- Cookie.Morsel: reject control chars in set()/__setitem__ key, value and
  coded value (CVE-2026-0672), raising CookieError. Validation lives at the
  value-storage chokepoints, so the CVE-2026-3644-style bypasses do not
  apply (2.7 has no Morsel.update/|=/__setstate__).
- imaplib.IMAP4._command: reject control chars in command arguments
  (CVE-2025-15366), raising ValueError.
- poplib.POP3._putline (and the SSL override): reject control chars in the
  command line (CVE-2025-15367), raising error_proto.
- httplib.HTTPConnection.set_tunnel: validate the CONNECT tunnel host via
  the existing _validate_host (CVE-2026-1502), raising InvalidURL.

Adds focused tests to test_cookie, test_wsgiref, test_imaplib, test_poplib
and test_httplib.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
email.generator.Generator wrote header values verbatim, so a value
containing a bare CR/LF (e.g. set via msg['To'] = 'a\r\nBcc: x') could
inject additional headers or body content.

Port the upstream verify_generated_headers behaviour into _write_headers:
after computing each header's serialized form, reject it (raise the new
email.errors.HeaderWriteError) if it contains a CR/LF that is not part of
valid folding, using NEWLINE_WITHOUT_FWSP = re.compile(
r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]'). Since 2.7's email has no policy
framework, the check is unconditional (matching upstream's default-on).

Adds email.errors.HeaderWriteError and a regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On a Windows release build, CRT calls made on a deliberately-invalid file
descriptor invoke the invalid-parameter handler and fast-fail (surfacing as
a "stopped working" dialog) instead of returning EBADF. Several tests
(test_fileio, test_os, test_signal) intentionally exercise bad fds and so
hung the suite. The existing _Py_BEGIN_SUPPRESS_IPH backport (e361063)
guarded the primary fd operations but missed three secondary fstat/lseek
calls:

- _io/fileio.c new_buffersize(): fstat()/lseek() reached by readall() before
  the already-guarded read() (test_fileio testErrnoOnClosedReadall).
- posixmodule.c posix_fdopen(): the directory-check fstat() reached by
  os.fdopen(bad_fd) (test_os TestInvalidFD.test_fdopen).
- signalmodule.c signal_set_wakeup_fd(): the validation fstat() reached by
  signal.set_wakeup_fd(bad_fd) (test_signal test_invalid_fd).

Wrap each in _Py_BEGIN_SUPPRESS_IPH/_Py_END_SUPPRESS_IPH so the call returns
an error and the expected exception is raised.

Also:
- regrtest: suppress Windows error-reporting / CRT-assert dialogs at suite
  startup (SetErrorMode + debug-CRT report mode), mirroring Python 3's test
  driver, so a faulting test crashes cleanly instead of blocking on a modal
  dialog. Runs in -j slaves too.
- test_ctypes: skip test_pass_pointers where c_long is narrower than a
  pointer (win64); it truncates the returned pointer and dereferences a bad
  address (an access violation, not an IPH case).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On a modern (UCRT / VS2015+) Windows build, <errno.h> defines the POSIX
errno values (ECONNRESET=108, ...), so Python's errno module exposes those
rather than the Winsock values. But the socket layer still reports Winsock
error codes (WSAECONNRESET=10054), so asyncore's _DISCONNECTED set -- built
from the C-runtime errno constants -- no longer matches a real connection
reset. recv() then re-raises instead of closing, the server threads in
test_ftplib die mid-loop, and they leak their handlers into the global
asyncore.socket_map ("socket_map was modified" -> test failure).

The existing Windows WSA handling here already mapped WSAENOTCONN,
WSAECONNABORTED and WSAEBADF, but omitted WSAECONNRESET (the actual error
seen) and WSAESHUTDOWN. Add both to the import, the POSIX fallback aliases,
and _DISCONNECTED. Harmless on older toolchains and on POSIX, where the WSA
names alias to the same POSIX values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On 64-bit Windows (LLP64) sys.maxint is still 2**31-1 because C long is
32-bit, while the address space and Py_ssize_t are 64-bit. seq_tests'
test_bigrepeat gated its 32-bit overflow check on sys.maxint, so on win64 it
ran the check and tried to build a ~34 GB sequence (2**32 elements) that
never raises MemoryError -- a multi-minute memory thrash ending in
"MemoryError not raised". This hit test_list, test_tuple and test_userlist
(all share seq_tests). Use sys.maxsize, matching upstream 2.7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This fork's test_socket.py calls _have_socket_can() (module level) and
references HAVE_SOCKET_ALG (in a class decorator) but the defining helpers
were lost in backport, so the module crashed at import on Windows with
"NameError: name '_have_socket_can' is not defined" (and would next hit
HAVE_SOCKET_ALG). Restore both helpers from upstream 2.7 and define
HAVE_SOCKET_ALG. On Windows AF_CAN/AF_ALG don't exist, so both return False
and the corresponding test classes skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CVE-2024-0450: zipfile did not detect "quoted overlap" archives where an
entry's compressed data overruns the start of the next entry, a high-ratio
zip bomb. _RealGetContents now records each member's _end_offset (the start
of the next local header, or the central directory for the last member) and
ZipFile.open raises BadZipfile if an entry's data would extend past it.

CVE-2025-8291: _EndRecData64 trusted that the ZIP64 end-of-central-directory
record sat immediately before its locator and ignored the locator's stored
relative offset. It now rejects archives whose locator offset points past
the expected record position ("Corrupt zip64 end of central directory
locator").

Adds _end_offset to ZipInfo (slots + __init__) and regression tests for
both issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion test

find_msvcrt() fabricated a non-existent CRT name (e.g. msvcr130.dll) on a
modern toolchain because _get_build_version() used the old "int(s[:-2]) - 6"
formula, which yields 13 for MSC v.19xx. CDLL() of that name fails with
WindowsError 126, breaking test_loading, test_errno and test_callbacks
(which load find_library("c")). Apply the upstream bpo-23606 fix: bump the
major version past the skipped v13, and have find_msvcrt() return None for
VS2015+ (the UCRT is not directly loadable). find_library("c") then returns
None and those tests skip cleanly.

Also skip test_prototypes.test_int_pointer_arg where c_long is narrower than
a pointer (win64): like test_pass_pointers it sets restype=c_long and
compares it against a full pointer address, which truncates on LLP64.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
urlparse.urlsplit only rejected mismatched IPv6 brackets, so a netloc like
"ex[ample].com" or "[example.com]" was accepted, parsing differently from
RFC 3986-compliant tools (a differential-parsing / SSRF vector).

Add _check_bracketed_netloc / _check_bracketed_host (ported from the
upstream fix) and call them from both urlsplit code paths. Brackets are now
allowed only when they enclose a valid IPv6/IPvFuture host. Since 2.7 lacks
the ipaddress module, IPv6 content is validated via socket.inet_pton (with a
conservative character fallback where inet_pton is unavailable).

Adds a regression test covering rejected and accepted bracketed hosts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SSLSocket.__init__ calls getpeername() to decide if the socket is already
connected, expecting errno.ENOTCONN for an unconnected socket. On a modern
(UCRT) Windows build errno.ENOTCONN is the POSIX value (107) while Winsock
reports WSAENOTCONN (10057), so the check failed and wrap_socket() re-raised
for the (very common) "wrap a not-yet-connected socket" path -- cascading to
~50 errors across test_ssl. Match both spellings via _NOT_CONNECTED_ERRORS,
same root cause as the asyncore WSAECONNRESET fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CVE-2025-12084: xml.dom.minidom._clear_id_cache called _in_document(), which
walks the parent chain to the document root on every node mutation, making
deeply nested appendChild()/insertBefore() O(n^2). Replace the walk with an
O(1) `node.ownerDocument` check (over-clearing a detached node's cache is
harmless; the cache is rebuilt lazily).

CVE-2025-6075: posixpath.expandvars rebuilt the whole path string on each
substitution and ntpath.expandvars concatenated to a result string char by
char, both quadratic in the input size. posixpath now accumulates output
segments; ntpath now expands via a single regex-substitution pass (ported
from upstream), preserving the existing matching semantics.

Adds bulk/regression tests for both expandvars implementations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test_socket.py imports `from test import test_support` but the (partially
forward-ported) body refers to the support module under BOTH names
(test_support.* and support.*, ~24 each). The bare `support.*` references —
including @support.requires_linux_version decorators evaluated at import —
raised "NameError: name 'support' is not defined", crashing the module on
import once the earlier _have_socket_can gap was fixed. Bind support =
test_support (same module object) so both names resolve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When close() flushed input ending in an unterminated construct, goahead()
advanced only to the next '<' and re-parsed, so an input with many
incomplete constructs (e.g. repeated "<!--") scanned the remaining buffer
once per construct -- O(n^2).

Backport the upstream fix: at EOF an unterminated construct is closed per
HTML5 (comments/declarations/CDATA/PI are emitted via their handlers, and
incomplete tags are ignored) and the rest of the buffer is consumed in one
step (k = n), making it linear. Adds endtagopen and updates the affected
EOF expectations in test_htmlparser, plus new test_eof_in_* cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test_loading.test_load_library asserted libc_name (find_library("c")) is not
None, but on a VS2015+/UCRT build find_msvcrt() correctly returns None (the
UCRT is not loadable as a single msvcrXXX.dll), so the assert failed. The
test's real purpose is loading kernel32, which is independent of libc_name;
drop the vestigial assertion (the libc-dependent cases already skip when
libc_name is None).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
base64.b64decode silently discarded non-alphabet characters and, with an
alternative alphabet, still accepted the standard '+'/'/' characters
(CVE-2025-12781); it also ignored any data after the padding
(CVE-2026-3446).

Add a validate=False parameter (mirroring Python 3). When validate=True the
input is checked against the *requested* alphabet -- so '+'/'/' are rejected
when altchars is given, and embedded or post-padding junk is rejected rather
than silently dropped. This goes beyond upstream, which only deprecates the
lenient behaviour. The default (validate=False) is unchanged.

Adds a regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two classes of Windows/2.7 test-side failures after the ssl.py WSAENOTCONN
library fix:

- test_getpeercert_enotconn / test_do_handshake_enotconn asserted the raised
  errno equals errno.ENOTCONN, but on a UCRT build the socket reports the
  Winsock value WSAENOTCONN (10057) while errno.ENOTCONN is the C-runtime
  value (126). Compare against a set that includes both (_ENOTCONN).

- test_pha_no_pha_client/server, test_pha_not_tls13 and (ThreadedTests)
  test_bpo37428_pha_cert_none exercise TLS 1.3 post-handshake authentication,
  whose Python API (ssl.TLSVersion etc.) is not present in this 2.7 ssl
  backport (test_pha_not_tls13 raised AttributeError on ssl.TLSVersion).
  Skip them unless hasattr(ssl, 'TLSVersion').

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tarfile normalized an AREGTYPE ('\x00') header whose name ends in a slash to
DIRTYPE.  This was also applied to follow-up headers (a GNU long name/link
or a pax header), letting a crafted archive be interpreted differently from
other tools.

Split the header parsing into _frombuf(dircheck=...) / _fromtarfile and
perform the AREGTYPE->DIRTYPE normalization only for primary headers; the
follow-up reads in _proc_gnulong and _proc_pax pass dircheck=False. The
public frombuf()/fromtarfile() signatures are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test_socket.py (and other partially forward-ported tests) decorate
Linux/CAN-specific cases with @support.requires_linux_version(...), which is a
Python 3 test.support helper absent from this fork. Evaluated at class-body
import time, it raised "AttributeError: 'module' object has no attribute
'requires_linux_version'", crashing test_socket on import (after the earlier
support-alias and _have_socket_can gaps were closed).

Backport requires_linux_version (+ the _requires_unix_version helper) from
upstream test.support. On non-Linux it is a pass-through; the CAN/Linux tests
it guards already skip on Windows via HAVE_SOCKET_CAN. functools/platform/
unittest are already imported here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump PY_VERSION to 2.7.18.14 and add release notes for the security fixes
addressed in this release: CVE-2025-8194, CVE-2026-4519, CVE-2026-4786,
CVE-2026-0865, CVE-2026-0672, CVE-2025-15366, CVE-2025-15367, CVE-2026-1502,
CVE-2024-6923, CVE-2024-0450, CVE-2025-8291, CVE-2025-0938, CVE-2024-11168,
CVE-2025-6069, CVE-2025-6075, CVE-2025-12084, CVE-2025-13462, CVE-2025-12781
and CVE-2026-3446.  Documents "not affected" determinations for
CVE-2025-13836, CVE-2025-15282, CVE-2025-11468, CVE-2025-1795, CVE-2026-3644
and CVE-2024-5642.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on win32

Two test-side residuals, surfaced once the asyncore WSAECONNRESET fix stopped
the server threads dying mid-run:

- test_with_statement sent 'noop', but DummyFTPHandler had no cmd_noop, so it
  replied "550 command not understood". Add cmd_noop (200 ok).
- test_source_address / _passive_connection assert the bound source port
  equals a find_unused_port() value; on Windows that port can be taken between
  selection and bind, so getsockname() returns a different port (off by 1-2).
  Skip on win32 (the EADDRINUSE branch doesn't cover the assertEqual race).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_can

Two test-suite fixes surfaced by running `python2 -m test` against the
2.7.18.14 build:

* test_posixpath: test_expandvars_nonascii_word dereferenced
  test_support.FS_NONASCII (None under an ASCII/C-locale filesystem
  encoding) before its skipTest check, crashing with
  AttributeError: 'NoneType' object has no attribute 'encode'.
  Guard it with @skipUnless(test_support.FS_NONASCII, ...) to match
  its sibling test_expandvars_many and upstream CPython. Regression
  from the CVE-2025-6075 expandvars backport.

* test_socket: HAVE_SOCKET_CAN = _have_socket_can() called a helper
  that was missing from the module, raising NameError at import and
  crashing the whole test. Restore the upstream _have_socket_can()
  definition; it resolves to False where PF_CAN/CAN_RAW are absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finalize the 2.7.18.14 release notes with the Windows (VS2022/UCRT, win64)
regression-remediation work delivered on this line: IPH suppression for
invalid-fd CRT calls (fileio/posix/signal), asyncore WSAECONNRESET/WSAESHUTDOWN
and ssl WSAENOTCONN handling, ctypes find_msvcrt on VS2015+ (bpo-23606), and
the win64/UCRT test-suite fixes. patchlevel.h already reads 2.7.18.14 (FINAL).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reconcile the remote 2.7.18.14 branch (which carried an independent fix,
bc3b091: skip-guard test_expandvars_nonascii_word on FS_NONASCII, and a
_have_socket_can helper) with the rc1-rc5 Windows regression remediation.

test_socket.py conflict resolved in favour of the more complete remediation
version (keeps both _have_socket_can and _have_socket_alg + the support alias);
the test_posixpath.py FS_NONASCII skip-guard from bc3b091 is taken as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@icanhasmath icanhasmath changed the title Integrate 2.7.18.14 into 2.7 (part 2/2: full 2.7.18.14 CVE batch + release + Windows remediation) Integrate 2.7.18.14 into 2.7 Jun 10, 2026
@icanhasmath icanhasmath requested a review from Copilot June 10, 2026 18:56

Copilot AI 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.

Pull request overview

This PR integrates the historical 2.7.18.14 changeset into the 2.7 branch, including the full CVE/security batch, the 2.7.18.14 release metadata/version bump, and preserved Windows/UCRT remediation work.

Changes:

  • Adds multiple security hardenings across stdlib modules (tarfile, webbrowser, wsgiref/email/imaplib/poplib/httplib, zipfile, urlparse, HTMLParser, base64) with corresponding tests.
  • Applies Windows/UCRT reliability fixes (suppress CRT invalid-parameter handler aborts; adjust socket/ssl/asyncore error handling; stabilize test runner against modal crash dialogs).
  • Updates release artifacts for 2.7.18.14 (NEWS entry, PY_VERSION).

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Modules/signalmodule.c Suppress Windows CRT invalid-parameter handler around fstat() in set_wakeup_fd().
Modules/posixmodule.c Suppress CRT invalid-parameter handler around fstat() in os.fdopen() dir-check path.
Modules/_io/fileio.c Suppress CRT invalid-parameter handler around fstat()/lseek() during buffer sizing.
Misc/NEWS.d/2.7.18.14.rst Adds 2.7.18.14 release notes covering security and Windows remediation items.
Lib/zipfile.py Hardens ZIP64 locator handling and rejects overlapping entries; tracks member end offsets.
Lib/xml/dom/minidom.py Removes quadratic behavior by clearing id cache based on ownerDocument rather than walking parents.
Lib/wsgiref/headers.py Rejects control characters in header APIs to prevent response splitting.
Lib/webbrowser.py Validates URLs to prevent argument injection (leading-dash URLs), including %action substitution path.
Lib/urlparse.py Validates bracket usage in hosts/netloc to only allow valid IPv6/IPvFuture bracketed hosts.
Lib/test/test_zipfile.py Adds tests for overlapping-entry rejection and ZIP64 locator offset rejection.
Lib/test/test_wsgiref.py Adds tests ensuring control characters in headers are rejected.
Lib/test/test_webbrowser.py Adds tests for URL leading-dash validation and %action bypass prevention.
Lib/test/test_urlparse.py Adds tests for invalid bracket placement/contents in URL hosts.
Lib/test/test_tarfile.py Adds tests for negative pax size, negative _block(), and AREGTYPE dircheck follow-up handling.
Lib/test/test_ssl.py Accepts Winsock ENOTCONN variants; skips TLS 1.3 PHA tests if API unavailable.
Lib/test/test_socket.py Adds AF_CAN / AF_ALG capability probes; binds support alias to test_support.
Lib/test/test_posixpath.py Adds expandvars “many substitutions” test for linear-time behavior.
Lib/test/test_poplib.py Adds tests for control-character rejection in POP3 command lines.
Lib/test/test_ntpath.py Adds expandvars stress tests for linear-time behavior.
Lib/test/test_imaplib.py Adds tests for control-character rejection in IMAP command arguments.
Lib/test/test_httplib.py Adds test ensuring set_tunnel() rejects control characters in host.
Lib/test/test_htmlparser.py Updates EOF behavior expectations and adds EOF-closure tests for quadratic fix.
Lib/test/test_ftplib.py Skips flaky exact-port assertions on Windows; adds NOOP handler in test server.
Lib/test/test_cookie.py Adds tests for control-character rejection; removes prior control-char value fixture.
Lib/test/test_base64.py Adds tests for strict validate=True base64 decoding behavior.
Lib/test/support/init.py Restores requires_linux_version decorator (via generic unix version helper).
Lib/test/seq_tests.py Uses sys.maxsize (Py_ssize_t) rather than sys.maxint for 64-bit Windows gating.
Lib/test/regrtest.py Suppresses Windows crash/assert dialogs at test-run start to prevent hangs.
Lib/tarfile.py Fixes negative block counts; avoids AREGTYPE follow-up normalization to DIRTYPE (dircheck parameter).
Lib/ssl.py Accepts Winsock ENOTCONN variants when probing socket connection state.
Lib/posixpath.py Rewrites expandvars() to build output linearly (avoid quadratic behavior).
Lib/poplib.py Rejects control characters in POP3 command lines.
Lib/ntpath.py Rewrites expandvars() using a single regex pass to avoid quadratic behavior.
Lib/imaplib.py Rejects control characters in IMAP command arguments.
Lib/httplib.py Validates tunnel host to reject control-character injection.
Lib/HTMLParser.py Changes EOF handling to close unterminated constructs per HTML5 and avoid quadratic rescans.
Lib/email/test/test_email.py Adds regression test for header-injection rejection during message generation.
Lib/email/generator.py Rejects injected newlines when generating headers; introduces HeaderWriteError.
Lib/email/errors.py Adds HeaderWriteError exception type.
Lib/ctypes/util.py Updates find_msvcrt() behavior for VS2015+/UCRT and adjusts build-version parsing.
Lib/ctypes/test/test_prototypes.py Skips pointer-truncation-sensitive test on win64 (where c_long isn’t pointer-sized).
Lib/ctypes/test/test_pointers.py Skips pointer-truncation-sensitive test on win64 (where c_long isn’t pointer-sized).
Lib/ctypes/test/test_loading.py Clarifies expected find_library("c") behavior under UCRT; keeps kernel32 loading checks.
Lib/Cookie.py Rejects control characters in cookie keys/values/attributes; updates docs/examples accordingly.
Lib/base64.py Adds validate= option to b64decode() for strict alphabet/padding validation.
Lib/asyncore.py Treats WSAECONNRESET/WSAESHUTDOWN as disconnects; fixes Winsock-vs-errno mismatch handling.
Include/patchlevel.h Bumps PY_VERSION to 2.7.18.14.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Lib/wsgiref/headers.py
Comment on lines +19 to +23
def _check_string(value):
"""Reject header names/values containing control characters."""
if isinstance(value, str) and _control_chars_re.search(value):
raise ValueError("Control characters not allowed in headers")
return value

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed real — verified on the Py2.7 build: a str value with \r\n is rejected but the same unicode value passes through, since isinstance(value, str) is bytes-only in Py2. Fixed by checking basestring instead. Addressed in follow-up #86 (with a unicode regression test in test_wsgiref).

Comment thread Lib/email/generator.py
Comment on lines +18 to +21
# Matches a CR/LF that is NOT part of a valid header folding (i.e. not
# immediately followed by folding whitespace). Used to detect injected
# newlines in generated headers (CVE-2024-6923).
NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Confirmed real — NEWLINE_WITHOUT_FWSP.search(value) returns no match for value ending in a bare CR/LF/CRLF (e.g. Subject: evil\n), because the [^ \t] classes require a following character. The generator then appends its own newline, prematurely terminating the header block. Fixed by switching to negative lookaheads (which also fire at end-of-string, while still permitting valid CRLF folding). Addressed in follow-up #86 with trailing-newline regression tests; full test_email still 280/280.

Comment thread Lib/urlparse.py
Comment on lines +219 to +220
except (ValueError, socket.error):
raise ValueError("Invalid IPv6 address")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Does not reproduce on this build — socket.inet_pton(AF_INET6, u"...") uses the "s" arg parser, which encodes unicode rather than raising TypeError: valid ASCII unicode is accepted, an invalid host raises socket.error, and non-ASCII unicode raises UnicodeEncodeError (a ValueError subclass). All of these are already covered by the existing except (ValueError, socket.error). No change needed; leaving as-is.

icanhasmath added a commit that referenced this pull request Jun 10, 2026
… newline

Two defense-in-depth gaps in the 2.7.18.14 header-injection fixes, surfaced
in review of PR #85 and folded into this release:

- wsgiref.headers._check_string only checked `str`, so a `unicode` header
  name/value carrying control characters bypassed the guard (CVE-2026-0865).
  Check `basestring` instead.

- email.generator.NEWLINE_WITHOUT_FWSP used consuming character classes
  ([^ \t]) that require a following character, so a value ending in a bare
  CR/LF/CRLF was not rejected -- the generator then appends its own newline,
  prematurely terminating the header block (CVE-2024-6923). Use negative
  lookaheads, which also fire at end-of-string while still permitting valid
  CRLF folding.

Regression tests extended in test_wsgiref (unicode names/values) and
test_email (trailing-newline values); NEWS entries for CVE-2026-0865 and
CVE-2024-6923 updated. A third review comment about urlparse leaking
TypeError from socket.inet_pton on unicode hosts does not reproduce on 2.7
(the "s" arg parser encodes unicode -> UnicodeEncodeError/socket.error, both
already handled).

Co-Authored-By: Claude Opus 4.8 (1M context) <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