Integrate 2.7.18.14 into 2.7#85
Conversation
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>
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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).
| # 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]') |
There was a problem hiding this comment.
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.
| except (ValueError, socket.error): | ||
| raise ValueError("Invalid IPv6 address") |
There was a problem hiding this comment.
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.
… 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>
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.7branch. Part 1 (#84) landed the pre-2.7.18.14 security/build work;2.7and 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.14range ontoorigin/2.7with--rebase-merges. The resulting tip tree is byte-for-byte identical to the tagged2.7.18.14branch, and the Windows-remediation merge topology is preserved.PY_VERSIONadvances to2.7.18.14.CVE / security cluster
Windows / UCRT remediation (preserved merge)
bc3b091test-fix branch into the remediation lineVerification
git diff origin/2.7.18.14 HEADis empty (identical tree)origin/2.7is an ancestor of this branch (clean rebase, no force-merge)Supersedes the closed #83 (which carried duplicate part-1 commits).