forked from pool/python39
Compare commits
263 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
e900ddc04a
|
|||
|
380660d7b6
|
|||
|
793408c576
|
|||
|
a7506e8af6
|
|||
|
9cf0841df6
|
|||
|
6d41ecb4ad
|
|||
|
69e885b9cf
|
|||
| 9eec107af7 | |||
| 379872e378 | |||
| c830e00341 | |||
| 09f9ecd767 | |||
| c7dccba328 | |||
| 493db1096a | |||
| a74f2b808f | |||
| 1309e04380 | |||
| 9f343d4b19 | |||
| fb0f2c0f89 | |||
| fe8dd13261 | |||
| c2915d540e | |||
| db68008d03 | |||
| 64818e1d6b | |||
| df6df60726 | |||
| 2a7083b52f | |||
| b3445ff772 | |||
| 16a3758f99 | |||
| 819fb77284 | |||
| 975044e74b | |||
| 303cf28c8d | |||
| 6dcdf81612 | |||
| af4e895cc2 | |||
| 557343380e | |||
| 81e576898b | |||
| f857ffb685 | |||
| b657f1bd02 | |||
| 0e5b96eafc | |||
| 935cc14ece | |||
| 77334d03b1 | |||
| 3b3841013a | |||
| 090db10a8a | |||
| 28d2065b0f | |||
| 8b5e0d922c | |||
| 79cfd15358 | |||
| 087c362626 | |||
| c05bd945fa | |||
| 88f27fa1de | |||
| a3040dacb7 | |||
| 63de619ed6 | |||
| a456d9d1b0 | |||
| ff3037e669 | |||
| 7dad477866 | |||
| 3fc2e6a2ef | |||
| a9055a2611 | |||
| f39c6ce1fe | |||
| 1955425d20 | |||
| 52ba2746e2 | |||
| 9196daa838 | |||
| ee4c161ee9 | |||
| 51d667b29b | |||
| e7e6aae574 | |||
| e4bde5df1c | |||
| 477d837ffc | |||
| 0abadf881e | |||
| 5c5b1d5bd8 | |||
| 402bcdd59b | |||
| 5e7bedbe7b | |||
| a6bb102623 | |||
| 9ed46c99a2 | |||
| af01200932 | |||
| b08f4f5b35 | |||
| 9dfd78f56c | |||
| b66ea2b702 | |||
| f364a35c85 | |||
| db43d93a80 | |||
| 2ee23ed438 | |||
| f0704e96b5 | |||
| 731de38310 | |||
| e6aa51477e | |||
| 103e541cc6 | |||
| 22ffaaf624 | |||
| 289cc66e3c | |||
| 7ff141432c | |||
| 15c8751a4b | |||
| 7c8ca681d6 | |||
| 6d21418eaf | |||
| 2c60467072 | |||
| 068535b602 | |||
| c154c39fde | |||
| 311f19ba89 | |||
| 9b86048150 | |||
| b8f8306bca | |||
| 89466274a0 | |||
| 96f7ae7576 | |||
| fb0cb0d77e | |||
| dbd04e1e44 | |||
| c13a3979ae | |||
| 0999da949b | |||
| b5917212a3 | |||
| 9d7c3614b4 | |||
| 4182a08672 | |||
| 745f5ba19c | |||
| 22c0faa015 | |||
| 27cb2961b5 | |||
| 69c4eef74b | |||
| 0ed644a292 | |||
| 97bb975b72 | |||
| 6c43cd2475 | |||
| 5fc7c9de92 | |||
| ac33b94579 | |||
|
|
5caf918e2d | ||
| 83790a812b | |||
| afb0081ba8 | |||
| 6438e76544 | |||
| 1079252656 | |||
| 7ce77a1280 | |||
| cbc1e5d930 | |||
| 4864dd15f6 | |||
| d3d22d08c2 | |||
| fa669904c0 | |||
| 5247938501 | |||
| c65f198c5f | |||
| 2163aded52 | |||
| 1adf96a982 | |||
| c4677b0c0c | |||
| ca3d1579b7 | |||
| 99c7e0b52b | |||
| cac9860ceb | |||
| 80936f6706 | |||
| 59150a7e9a | |||
| 2c04be55bd | |||
| a0ade6e31d | |||
| 80ef87d611 | |||
| a697b381bc | |||
| ea87139f16 | |||
| d28bf8ebe9 | |||
| d6d31d7ca3 | |||
| 6c0c30d16d | |||
| 0f6aeb04bb | |||
| a25e716d37 | |||
| 6fa3cda544 | |||
| a851d71273 | |||
| 19674afc6d | |||
| a2b82842e5 | |||
| 0535f25347 | |||
| f343483635 | |||
| d57ee42f22 | |||
| 8ac9461637 | |||
| 6f0c4c85a1 | |||
| 35d1711e18 | |||
| 56c63c9ae2 | |||
| 04678e52ad | |||
| 9b7188a56a | |||
| 2253eadce9 | |||
| c65f6c6577 | |||
| 87c616ceaf | |||
| c0ef92b69d | |||
| 7b72538bfb | |||
| 0054c87fd3 | |||
| 77b5437185 | |||
| 660a10d613 | |||
| d6932903cc | |||
| f7ad0c8e9b | |||
| 3c6c7f7fee | |||
| 77fd8b492b | |||
| 1cea88a1fa | |||
| 6f0c05a479 | |||
| df2471a1fa | |||
| 14c194c885 | |||
| b52e0022b1 | |||
| 41195dffc1 | |||
| ed4a6bb277 | |||
| 95e5f943bd | |||
| a87cc20a6a | |||
| 415beff858 | |||
| 02c947161e | |||
| 90e6506490 | |||
| b3f4fa91cb | |||
| 79e0f644e0 | |||
| 97158a3a2c | |||
| cddb7279e5 | |||
| 0150e36f11 | |||
| 6a5249892f | |||
| 861dbc7cda | |||
|
|
9023685d33 | ||
| f0f3bcc72a | |||
| dc8ada3cd0 | |||
| 4b1c2f2f9c | |||
| c524d26818 | |||
| bcad073109 | |||
| 1fb9f9d47e | |||
| 25e135318f | |||
| 2fa8f8d6ae | |||
| 81c6bde23d | |||
| 4ae49af4ba | |||
| d412267ee4 | |||
| 1e1de10cdd | |||
| 143e377b2e | |||
| 0f302d71c9 | |||
| 85067059b6 | |||
| ac13143082 | |||
| 4bbc769d8d | |||
| 40e9d58763 | |||
| 0cf7e4ca96 | |||
| 2a91d2876b | |||
| 3c35d8ccf5 | |||
|
|
8fdf4ffd00 | ||
| ce250c01c7 | |||
| 9559d22979 | |||
| d3bad64b2f | |||
| f168450c97 | |||
| ca899a3e2a | |||
| 771e6fa592 | |||
| f0a02de745 | |||
| 910c55d10c | |||
| e410e3892b | |||
| 12d62b8ab0 | |||
| b49889be05 | |||
| 499bf81eab | |||
| 51fd21dcf6 | |||
| 976b91d922 | |||
| ddccfc5ed2 | |||
| 4a7f7a3418 | |||
| 8de6436aa2 | |||
| 5b2c8e3c6b | |||
| 83e48f46d5 | |||
| 29b655d5a3 | |||
| 5b0e8f466e | |||
| e9c0765f6b | |||
| b21b787dc9 | |||
| d75e173524 | |||
| 29826da096 | |||
| 1461311fdc | |||
| 93a45cc321 | |||
|
|
fe2245b6d4 | ||
| 10f9749ce3 | |||
| bdf33c8bd2 | |||
|
|
b8b18b6c66 | ||
| e445e8dd23 | |||
| a8d3631e3c | |||
| 8a74134292 | |||
| c4998b18b3 | |||
| 29e93e2ad8 | |||
| 5784628c23 | |||
| 5bdd82140c | |||
| 2e06ccfc30 | |||
| 71ac2aa56c | |||
|
|
63a4856637 | ||
| 7e181496c3 | |||
| bfca21eba6 | |||
| 322af6478b | |||
| b83b6258d2 | |||
| 070e08b2a4 | |||
| fe6eb0ea67 | |||
|
|
012ae3418d | ||
|
|
9809d09b33 | ||
|
|
3049a47ee1 | ||
|
|
5ac62c3616 | ||
|
|
5c53ab03d9 | ||
| bec09a8a16 | |||
| fc34fc829e | |||
| 7debc9c156 | |||
| 9e5d738f44 | |||
| 5ab6b5f996 | |||
| 6d6343ca6b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.osc
|
||||
python39-*-build/
|
||||
|
||||
@@ -10,9 +10,11 @@ Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for
|
||||
Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst | 1 +
|
||||
2 files changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/Doc/tools/extensions/pyspecific.py
|
||||
+++ b/Doc/tools/extensions/pyspecific.py
|
||||
@@ -26,7 +26,7 @@ try:
|
||||
Index: Python-3.9.22/Doc/tools/extensions/pyspecific.py
|
||||
===================================================================
|
||||
--- Python-3.9.22.orig/Doc/tools/extensions/pyspecific.py 2025-04-11 09:49:58.417019238 +0200
|
||||
+++ Python-3.9.22/Doc/tools/extensions/pyspecific.py 2025-04-11 09:50:56.818993764 +0200
|
||||
@@ -27,7 +27,7 @@
|
||||
from sphinx.errors import NoUri
|
||||
except ImportError:
|
||||
from sphinx.environment import NoUri
|
||||
@@ -21,7 +23,7 @@ Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for
|
||||
from sphinx.util import status_iterator, logging
|
||||
from sphinx.util.nodes import split_explicit_title
|
||||
from sphinx.writers.text import TextWriter, TextTranslator
|
||||
@@ -110,7 +110,7 @@ class ImplementationDetail(Directive):
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
def run(self):
|
||||
pnode = nodes.compound(classes=['impl-detail'])
|
||||
@@ -30,7 +32,7 @@ Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for
|
||||
content = self.content
|
||||
add_text = nodes.strong(label, label)
|
||||
if self.arguments:
|
||||
@@ -179,7 +179,7 @@ class AuditEvent(Directive):
|
||||
@@ -180,7 +180,7 @@
|
||||
else:
|
||||
args = []
|
||||
|
||||
@@ -39,16 +41,18 @@ Subject: [PATCH 1/2] fix(doc-tools): use sphinx.locale._ as gettext() for
|
||||
text = label.format(name="``{}``".format(name),
|
||||
args=", ".join("``{}``".format(a) for a in args if a))
|
||||
|
||||
@@ -358,7 +358,7 @@ class DeprecatedRemoved(Directive):
|
||||
@@ -380,7 +380,7 @@
|
||||
else:
|
||||
label = self._removed_label
|
||||
|
||||
- label = translators['sphinx'].gettext(label)
|
||||
+ label = sphinx_gettext(label)
|
||||
text = label.format(deprecated=self.arguments[0], removed=self.arguments[1])
|
||||
text = label.format(deprecated=version[0], removed=version[1])
|
||||
if len(self.arguments) == 3:
|
||||
inodes, messages = self.state.inline_text(self.arguments[2],
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst
|
||||
Index: Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ Python-3.9.22/Misc/NEWS.d/next/Documentation/2022-10-19-07-15-52.gh-issue-98366.UskMXF.rst 2025-04-11 09:50:08.952333342 +0200
|
||||
@@ -0,0 +1 @@
|
||||
+Use sphinx.locale._ as the gettext function in pyspecific.py.
|
||||
|
||||
@@ -1,461 +0,0 @@
|
||||
---
|
||||
Doc/library/email.utils.rst | 19 -
|
||||
Lib/email/utils.py | 151 +++++++-
|
||||
Lib/test/test_email/test_email.py | 187 +++++++++-
|
||||
Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8
|
||||
4 files changed, 344 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/Doc/library/email.utils.rst
|
||||
+++ b/Doc/library/email.utils.rst
|
||||
@@ -60,13 +60,18 @@ of the new API.
|
||||
begins with angle brackets, they are stripped off.
|
||||
|
||||
|
||||
-.. function:: parseaddr(address)
|
||||
+.. function:: parseaddr(address, *, strict=True)
|
||||
|
||||
Parse address -- which should be the value of some address-containing field such
|
||||
as :mailheader:`To` or :mailheader:`Cc` -- into its constituent *realname* and
|
||||
*email address* parts. Returns a tuple of that information, unless the parse
|
||||
fails, in which case a 2-tuple of ``('', '')`` is returned.
|
||||
|
||||
+ If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||
+
|
||||
+ .. versionchanged:: 3.13
|
||||
+ Add *strict* optional parameter and reject malformed inputs by default.
|
||||
+
|
||||
|
||||
.. function:: formataddr(pair, charset='utf-8')
|
||||
|
||||
@@ -84,12 +89,15 @@ of the new API.
|
||||
Added the *charset* option.
|
||||
|
||||
|
||||
-.. function:: getaddresses(fieldvalues)
|
||||
+.. function:: getaddresses(fieldvalues, *, strict=True)
|
||||
|
||||
This method returns a list of 2-tuples of the form returned by ``parseaddr()``.
|
||||
*fieldvalues* is a sequence of header field values as might be returned by
|
||||
- :meth:`Message.get_all <email.message.Message.get_all>`. Here's a simple
|
||||
- example that gets all the recipients of a message::
|
||||
+ :meth:`Message.get_all <email.message.Message.get_all>`.
|
||||
+
|
||||
+ If *strict* is true, use a strict parser which rejects malformed inputs.
|
||||
+
|
||||
+ Here's a simple example that gets all the recipients of a message::
|
||||
|
||||
from email.utils import getaddresses
|
||||
|
||||
@@ -99,6 +107,9 @@ of the new API.
|
||||
resent_ccs = msg.get_all('resent-cc', [])
|
||||
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
|
||||
|
||||
+ .. versionchanged:: 3.13
|
||||
+ Add *strict* optional parameter and reject malformed inputs by default.
|
||||
+
|
||||
|
||||
.. function:: parsedate(date)
|
||||
|
||||
--- a/Lib/email/utils.py
|
||||
+++ b/Lib/email/utils.py
|
||||
@@ -48,6 +48,7 @@ TICK = "'"
|
||||
specialsre = re.compile(r'[][\\()<>@,:;".]')
|
||||
escapesre = re.compile(r'[\\"]')
|
||||
|
||||
+
|
||||
def _has_surrogates(s):
|
||||
"""Return True if s contains surrogate-escaped binary data."""
|
||||
# This check is based on the fact that unless there are surrogates, utf8
|
||||
@@ -106,12 +107,127 @@ def formataddr(pair, charset='utf-8'):
|
||||
return address
|
||||
|
||||
|
||||
+def _iter_escaped_chars(addr):
|
||||
+ pos = 0
|
||||
+ escape = False
|
||||
+ for pos, ch in enumerate(addr):
|
||||
+ if escape:
|
||||
+ yield (pos, '\\' + ch)
|
||||
+ escape = False
|
||||
+ elif ch == '\\':
|
||||
+ escape = True
|
||||
+ else:
|
||||
+ yield (pos, ch)
|
||||
+ if escape:
|
||||
+ yield (pos, '\\')
|
||||
+
|
||||
+
|
||||
+def _strip_quoted_realnames(addr):
|
||||
+ """Strip real names between quotes."""
|
||||
+ if '"' not in addr:
|
||||
+ # Fast path
|
||||
+ return addr
|
||||
+
|
||||
+ start = 0
|
||||
+ open_pos = None
|
||||
+ result = []
|
||||
+ for pos, ch in _iter_escaped_chars(addr):
|
||||
+ if ch == '"':
|
||||
+ if open_pos is None:
|
||||
+ open_pos = pos
|
||||
+ else:
|
||||
+ if start != open_pos:
|
||||
+ result.append(addr[start:open_pos])
|
||||
+ start = pos + 1
|
||||
+ open_pos = None
|
||||
|
||||
-def getaddresses(fieldvalues):
|
||||
- """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
|
||||
- all = COMMASPACE.join(str(v) for v in fieldvalues)
|
||||
- a = _AddressList(all)
|
||||
- return a.addresslist
|
||||
+ if start < len(addr):
|
||||
+ result.append(addr[start:])
|
||||
+
|
||||
+ return ''.join(result)
|
||||
+
|
||||
+
|
||||
+supports_strict_parsing = True
|
||||
+
|
||||
+def getaddresses(fieldvalues, *, strict=True):
|
||||
+ """Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.
|
||||
+
|
||||
+ When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
|
||||
+ its place.
|
||||
+
|
||||
+ If strict is true, use a strict parser which rejects malformed inputs.
|
||||
+ """
|
||||
+
|
||||
+ # If strict is true, if the resulting list of parsed addresses is greater
|
||||
+ # than the number of fieldvalues in the input list, a parsing error has
|
||||
+ # occurred and consequently a list containing a single empty 2-tuple [('',
|
||||
+ # '')] is returned in its place. This is done to avoid invalid output.
|
||||
+ #
|
||||
+ # Malformed input: getaddresses(['alice@example.com <bob@example.com>'])
|
||||
+ # Invalid output: [('', 'alice@example.com'), ('', 'bob@example.com')]
|
||||
+ # Safe output: [('', '')]
|
||||
+
|
||||
+ if not strict:
|
||||
+ all = COMMASPACE.join(str(v) for v in fieldvalues)
|
||||
+ a = _AddressList(all)
|
||||
+ return a.addresslist
|
||||
+
|
||||
+ fieldvalues = [str(v) for v in fieldvalues]
|
||||
+ fieldvalues = _pre_parse_validation(fieldvalues)
|
||||
+ addr = COMMASPACE.join(fieldvalues)
|
||||
+ a = _AddressList(addr)
|
||||
+ result = _post_parse_validation(a.addresslist)
|
||||
+
|
||||
+ # Treat output as invalid if the number of addresses is not equal to the
|
||||
+ # expected number of addresses.
|
||||
+ n = 0
|
||||
+ for v in fieldvalues:
|
||||
+ # When a comma is used in the Real Name part it is not a deliminator.
|
||||
+ # So strip those out before counting the commas.
|
||||
+ v = _strip_quoted_realnames(v)
|
||||
+ # Expected number of addresses: 1 + number of commas
|
||||
+ n += 1 + v.count(',')
|
||||
+ if len(result) != n:
|
||||
+ return [('', '')]
|
||||
+
|
||||
+ return result
|
||||
+
|
||||
+
|
||||
+def _check_parenthesis(addr):
|
||||
+ # Ignore parenthesis in quoted real names.
|
||||
+ addr = _strip_quoted_realnames(addr)
|
||||
+
|
||||
+ opens = 0
|
||||
+ for pos, ch in _iter_escaped_chars(addr):
|
||||
+ if ch == '(':
|
||||
+ opens += 1
|
||||
+ elif ch == ')':
|
||||
+ opens -= 1
|
||||
+ if opens < 0:
|
||||
+ return False
|
||||
+ return (opens == 0)
|
||||
+
|
||||
+
|
||||
+def _pre_parse_validation(email_header_fields):
|
||||
+ accepted_values = []
|
||||
+ for v in email_header_fields:
|
||||
+ if not _check_parenthesis(v):
|
||||
+ v = "('', '')"
|
||||
+ accepted_values.append(v)
|
||||
+
|
||||
+ return accepted_values
|
||||
+
|
||||
+
|
||||
+def _post_parse_validation(parsed_email_header_tuples):
|
||||
+ accepted_values = []
|
||||
+ # The parser would have parsed a correctly formatted domain-literal
|
||||
+ # The existence of an [ after parsing indicates a parsing failure
|
||||
+ for v in parsed_email_header_tuples:
|
||||
+ if '[' in v[1]:
|
||||
+ v = ('', '')
|
||||
+ accepted_values.append(v)
|
||||
+
|
||||
+ return accepted_values
|
||||
|
||||
|
||||
def _format_timetuple_and_zone(timetuple, zone):
|
||||
@@ -202,16 +318,33 @@ def parsedate_to_datetime(data):
|
||||
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
|
||||
|
||||
|
||||
-def parseaddr(addr):
|
||||
+def parseaddr(addr, *, strict=True):
|
||||
"""
|
||||
Parse addr into its constituent realname and email address parts.
|
||||
|
||||
Return a tuple of realname and email address, unless the parse fails, in
|
||||
which case return a 2-tuple of ('', '').
|
||||
+
|
||||
+ If strict is True, use a strict parser which rejects malformed inputs.
|
||||
"""
|
||||
- addrs = _AddressList(addr).addresslist
|
||||
- if not addrs:
|
||||
- return '', ''
|
||||
+ if not strict:
|
||||
+ addrs = _AddressList(addr).addresslist
|
||||
+ if not addrs:
|
||||
+ return ('', '')
|
||||
+ return addrs[0]
|
||||
+
|
||||
+ if isinstance(addr, list):
|
||||
+ addr = addr[0]
|
||||
+
|
||||
+ if not isinstance(addr, str):
|
||||
+ return ('', '')
|
||||
+
|
||||
+ addr = _pre_parse_validation([addr])[0]
|
||||
+ addrs = _post_parse_validation(_AddressList(addr).addresslist)
|
||||
+
|
||||
+ if not addrs or len(addrs) > 1:
|
||||
+ return ('', '')
|
||||
+
|
||||
return addrs[0]
|
||||
|
||||
|
||||
--- a/Lib/test/test_email/test_email.py
|
||||
+++ b/Lib/test/test_email/test_email.py
|
||||
@@ -16,6 +16,7 @@ from unittest.mock import patch
|
||||
|
||||
import email
|
||||
import email.policy
|
||||
+import email.utils
|
||||
|
||||
from email.charset import Charset
|
||||
from email.header import Header, decode_header, make_header
|
||||
@@ -3263,15 +3264,137 @@ Foo
|
||||
[('Al Person', 'aperson@dom.ain'),
|
||||
('Bud Person', 'bperson@dom.ain')])
|
||||
|
||||
+ def test_parsing_errors(self):
|
||||
+ """Test for parsing errors from CVE-2023-27043 and CVE-2019-16056"""
|
||||
+ alice = 'alice@example.org'
|
||||
+ bob = 'bob@example.com'
|
||||
+ empty = ('', '')
|
||||
+
|
||||
+ # Test utils.getaddresses() and utils.parseaddr() on malformed email
|
||||
+ # addresses: default behavior (strict=True) rejects malformed address,
|
||||
+ # and strict=False which tolerates malformed address.
|
||||
+ for invalid_separator, expected_non_strict in (
|
||||
+ ('(', [(f'<{bob}>', alice)]),
|
||||
+ (')', [('', alice), empty, ('', bob)]),
|
||||
+ ('<', [('', alice), empty, ('', bob), empty]),
|
||||
+ ('>', [('', alice), empty, ('', bob)]),
|
||||
+ ('[', [('', f'{alice}[<{bob}>]')]),
|
||||
+ (']', [('', alice), empty, ('', bob)]),
|
||||
+ ('@', [empty, empty, ('', bob)]),
|
||||
+ (';', [('', alice), empty, ('', bob)]),
|
||||
+ (':', [('', alice), ('', bob)]),
|
||||
+ ('.', [('', alice + '.'), ('', bob)]),
|
||||
+ ('"', [('', alice), ('', f'<{bob}>')]),
|
||||
+ ):
|
||||
+ address = f'{alice}{invalid_separator}<{bob}>'
|
||||
+ with self.subTest(address=address):
|
||||
+ self.assertEqual(utils.getaddresses([address]),
|
||||
+ [empty])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||
+ expected_non_strict)
|
||||
+
|
||||
+ self.assertEqual(utils.parseaddr([address]),
|
||||
+ empty)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Comma (',') is treated differently depending on strict parameter.
|
||||
+ # Comma without quotes.
|
||||
+ address = f'{alice},<{bob}>'
|
||||
+ self.assertEqual(utils.getaddresses([address]),
|
||||
+ [('', alice), ('', bob)])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||
+ [('', alice), ('', bob)])
|
||||
+ self.assertEqual(utils.parseaddr([address]),
|
||||
+ empty)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Real name between quotes containing comma.
|
||||
+ address = '"Alice, alice@example.org" <bob@example.com>'
|
||||
+ expected_strict = ('Alice, alice@example.org', 'bob@example.com')
|
||||
+ self.assertEqual(utils.getaddresses([address]), [expected_strict])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict])
|
||||
+ self.assertEqual(utils.parseaddr([address]), expected_strict)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Valid parenthesis in comments.
|
||||
+ address = 'alice@example.org (Alice)'
|
||||
+ expected_strict = ('Alice', 'alice@example.org')
|
||||
+ self.assertEqual(utils.getaddresses([address]), [expected_strict])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False), [expected_strict])
|
||||
+ self.assertEqual(utils.parseaddr([address]), expected_strict)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Invalid parenthesis in comments.
|
||||
+ address = 'alice@example.org )Alice('
|
||||
+ self.assertEqual(utils.getaddresses([address]), [empty])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||
+ [('', 'alice@example.org'), ('', ''), ('', 'Alice')])
|
||||
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Two addresses with quotes separated by comma.
|
||||
+ address = '"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>'
|
||||
+ self.assertEqual(utils.getaddresses([address]),
|
||||
+ [('Jane Doe', 'jane@example.net'),
|
||||
+ ('John Doe', 'john@example.net')])
|
||||
+ self.assertEqual(utils.getaddresses([address], strict=False),
|
||||
+ [('Jane Doe', 'jane@example.net'),
|
||||
+ ('John Doe', 'john@example.net')])
|
||||
+ self.assertEqual(utils.parseaddr([address]), empty)
|
||||
+ self.assertEqual(utils.parseaddr([address], strict=False),
|
||||
+ ('', address))
|
||||
+
|
||||
+ # Test email.utils.supports_strict_parsing attribute
|
||||
+ self.assertEqual(email.utils.supports_strict_parsing, True)
|
||||
+
|
||||
def test_getaddresses_nasty(self):
|
||||
- eq = self.assertEqual
|
||||
- eq(utils.getaddresses(['foo: ;']), [('', '')])
|
||||
- eq(utils.getaddresses(
|
||||
- ['[]*-- =~$']),
|
||||
- [('', ''), ('', ''), ('', '*--')])
|
||||
- eq(utils.getaddresses(
|
||||
- ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
|
||||
- [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
|
||||
+ for addresses, expected in (
|
||||
+ (['"Sürname, Firstname" <to@example.com>'],
|
||||
+ [('Sürname, Firstname', 'to@example.com')]),
|
||||
+
|
||||
+ (['foo: ;'],
|
||||
+ [('', '')]),
|
||||
+
|
||||
+ (['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>'],
|
||||
+ [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]),
|
||||
+
|
||||
+ ([r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>'],
|
||||
+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')]),
|
||||
+
|
||||
+ (['(Empty list)(start)Undisclosed recipients :(nobody(I know))'],
|
||||
+ [('', '')]),
|
||||
+
|
||||
+ (['Mary <@machine.tld:mary@example.net>, , jdoe@test . example'],
|
||||
+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')]),
|
||||
+
|
||||
+ (['John Doe <jdoe@machine(comment). example>'],
|
||||
+ [('John Doe (comment)', 'jdoe@machine.example')]),
|
||||
+
|
||||
+ (['"Mary Smith: Personal Account" <smith@home.example>'],
|
||||
+ [('Mary Smith: Personal Account', 'smith@home.example')]),
|
||||
+
|
||||
+ (['Undisclosed recipients:;'],
|
||||
+ [('', '')]),
|
||||
+
|
||||
+ ([r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>'],
|
||||
+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')]),
|
||||
+ ):
|
||||
+ with self.subTest(addresses=addresses):
|
||||
+ self.assertEqual(utils.getaddresses(addresses),
|
||||
+ expected)
|
||||
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
|
||||
+ expected)
|
||||
+
|
||||
+ addresses = ['[]*-- =~$']
|
||||
+ self.assertEqual(utils.getaddresses(addresses),
|
||||
+ [('', '')])
|
||||
+ self.assertEqual(utils.getaddresses(addresses, strict=False),
|
||||
+ [('', ''), ('', ''), ('', '*--')])
|
||||
|
||||
def test_getaddresses_embedded_comment(self):
|
||||
"""Test proper handling of a nested comment"""
|
||||
@@ -3460,6 +3583,54 @@ multipart/report
|
||||
m = cls(*constructor, policy=email.policy.default)
|
||||
self.assertIs(m.policy, email.policy.default)
|
||||
|
||||
+ def test_iter_escaped_chars(self):
|
||||
+ self.assertEqual(list(utils._iter_escaped_chars(r'a\\b\"c\\"d')),
|
||||
+ [(0, 'a'),
|
||||
+ (2, '\\\\'),
|
||||
+ (3, 'b'),
|
||||
+ (5, '\\"'),
|
||||
+ (6, 'c'),
|
||||
+ (8, '\\\\'),
|
||||
+ (9, '"'),
|
||||
+ (10, 'd')])
|
||||
+ self.assertEqual(list(utils._iter_escaped_chars('a\\')),
|
||||
+ [(0, 'a'), (1, '\\')])
|
||||
+
|
||||
+ def test_strip_quoted_realnames(self):
|
||||
+ def check(addr, expected):
|
||||
+ self.assertEqual(utils._strip_quoted_realnames(addr), expected)
|
||||
+
|
||||
+ check('"Jane Doe" <jane@example.net>, "John Doe" <john@example.net>',
|
||||
+ ' <jane@example.net>, <john@example.net>')
|
||||
+ check(r'"Jane \"Doe\"." <jane@example.net>',
|
||||
+ ' <jane@example.net>')
|
||||
+
|
||||
+ # special cases
|
||||
+ check(r'before"name"after', 'beforeafter')
|
||||
+ check(r'before"name"', 'before')
|
||||
+ check(r'b"name"', 'b') # single char
|
||||
+ check(r'"name"after', 'after')
|
||||
+ check(r'"name"a', 'a') # single char
|
||||
+ check(r'"name"', '')
|
||||
+
|
||||
+ # no change
|
||||
+ for addr in (
|
||||
+ 'Jane Doe <jane@example.net>, John Doe <john@example.net>',
|
||||
+ 'lone " quote',
|
||||
+ ):
|
||||
+ self.assertEqual(utils._strip_quoted_realnames(addr), addr)
|
||||
+
|
||||
+
|
||||
+ def test_check_parenthesis(self):
|
||||
+ addr = 'alice@example.net'
|
||||
+ self.assertTrue(utils._check_parenthesis(f'{addr} (Alice)'))
|
||||
+ self.assertFalse(utils._check_parenthesis(f'{addr} )Alice('))
|
||||
+ self.assertFalse(utils._check_parenthesis(f'{addr} (Alice))'))
|
||||
+ self.assertFalse(utils._check_parenthesis(f'{addr} ((Alice)'))
|
||||
+
|
||||
+ # Ignore real name between quotes
|
||||
+ self.assertTrue(utils._check_parenthesis(f'")Alice((" {addr}'))
|
||||
+
|
||||
|
||||
# Test the iterator/generators
|
||||
class TestIterators(TestEmailBase):
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst
|
||||
@@ -0,0 +1,8 @@
|
||||
+:func:`email.utils.getaddresses` and :func:`email.utils.parseaddr` now
|
||||
+return ``('', '')`` 2-tuples in more situations where invalid email
|
||||
+addresses are encountered instead of potentially inaccurate values. Add
|
||||
+optional *strict* parameter to these two functions: use ``strict=False`` to
|
||||
+get the old behavior, accept malformed inputs.
|
||||
+``getattr(email.utils, 'supports_strict_parsing', False)`` can be use to check
|
||||
+if the *strict* paramater is available. Patch by Thomas Dwyer and Victor
|
||||
+Stinner to improve the CVE-2023-27043 fix.
|
||||
@@ -4,9 +4,11 @@
|
||||
Lib/test/test_xml_etree.py | 7 +++++++
|
||||
3 files changed, 14 insertions(+)
|
||||
|
||||
--- a/Lib/test/test_pyexpat.py
|
||||
+++ b/Lib/test/test_pyexpat.py
|
||||
@@ -766,6 +766,10 @@ class ReparseDeferralTest(unittest.TestC
|
||||
Index: Python-3.9.25/Lib/test/test_pyexpat.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_pyexpat.py 2025-12-11 22:43:38.646411669 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_pyexpat.py 2025-12-11 22:43:57.288891858 +0100
|
||||
@@ -802,6 +802,10 @@
|
||||
self.assertEqual(started, ['doc'])
|
||||
|
||||
def test_reparse_deferral_disabled(self):
|
||||
@@ -17,9 +19,11 @@
|
||||
started = []
|
||||
|
||||
def start_element(name, _):
|
||||
--- a/Lib/test/test_sax.py
|
||||
+++ b/Lib/test/test_sax.py
|
||||
@@ -1236,6 +1236,9 @@ class ExpatReaderTest(XmlTestBase):
|
||||
Index: Python-3.9.25/Lib/test/test_sax.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_sax.py 2025-12-11 22:43:38.675498657 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_sax.py 2025-12-11 22:43:57.289349463 +0100
|
||||
@@ -1236,6 +1236,9 @@
|
||||
|
||||
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
||||
|
||||
@@ -29,9 +33,11 @@
|
||||
def test_flush_reparse_deferral_disabled(self):
|
||||
result = BytesIO()
|
||||
xmlgen = XMLGenerator(result)
|
||||
--- a/Lib/test/test_xml_etree.py
|
||||
+++ b/Lib/test/test_xml_etree.py
|
||||
@@ -1416,9 +1416,13 @@ class XMLPullParserTest(unittest.TestCas
|
||||
Index: Python-3.9.25/Lib/test/test_xml_etree.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_xml_etree.py 2025-12-11 22:43:38.988627336 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_xml_etree.py 2025-12-11 22:43:57.289604596 +0100
|
||||
@@ -1416,9 +1416,13 @@
|
||||
self.assert_event_tags(parser, [('end', 'root')])
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
@@ -45,7 +51,7 @@
|
||||
def test_simple_xml_chunk_5(self):
|
||||
self.test_simple_xml(chunk_size=5, flush=True)
|
||||
|
||||
@@ -1643,6 +1647,9 @@ class XMLPullParserTest(unittest.TestCas
|
||||
@@ -1643,6 +1647,9 @@
|
||||
|
||||
self.assert_event_tags(parser, [('end', 'doc')])
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
From 732c7d512e7cdf656a3f02a38c329b14a14a8573 Mon Sep 17 00:00:00 2001
|
||||
From: Seth Michael Larson <seth@python.org>
|
||||
Date: Fri, 19 Apr 2024 11:21:40 -0700
|
||||
Subject: [PATCH] [3.9] gh-114572: Fix locking in cert_store_stats and
|
||||
get_ca_certs
|
||||
|
||||
---
|
||||
Misc/NEWS.d/next/Security/2024-04-19-11-21-13.gh-issue-114572.t1QMQD.rst | 4
|
||||
Modules/_ssl.c | 91 +++++++++-
|
||||
2 files changed, 92 insertions(+), 3 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2024-04-19-11-21-13.gh-issue-114572.t1QMQD.rst
|
||||
|
||||
Index: Python-3.9.19/Misc/NEWS.d/next/Security/2024-04-19-11-21-13.gh-issue-114572.t1QMQD.rst
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ Python-3.9.19/Misc/NEWS.d/next/Security/2024-04-19-11-21-13.gh-issue-114572.t1QMQD.rst
|
||||
@@ -0,0 +1,4 @@
|
||||
+:meth:`ssl.SSLContext.cert_store_stats` and
|
||||
+:meth:`ssl.SSLContext.get_ca_certs` now correctly lock access to the
|
||||
+certificate store, when the :class:`ssl.SSLContext` is shared across
|
||||
+multiple threads.
|
||||
Index: Python-3.9.19/Modules/_ssl.c
|
||||
===================================================================
|
||||
--- Python-3.9.19.orig/Modules/_ssl.c
|
||||
+++ Python-3.9.19/Modules/_ssl.c
|
||||
@@ -166,6 +166,10 @@ extern const SSL_METHOD *TLSv1_2_method(
|
||||
# define PY_OPENSSL_1_1_API 1
|
||||
#endif
|
||||
|
||||
+#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
+# define OPENSSL_VERSION_3_3 1
|
||||
+#endif
|
||||
+
|
||||
/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f
|
||||
* This includes the SSL_set_SSL_CTX() function.
|
||||
*/
|
||||
@@ -210,6 +214,16 @@ extern const SSL_METHOD *TLSv1_2_method(
|
||||
#define HAVE_OPENSSL_CRYPTO_LOCK
|
||||
#endif
|
||||
|
||||
+/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
|
||||
+#ifdef OPENSSL_VERSION_1_1
|
||||
+#define HAVE_OPENSSL_X509_STORE_LOCK
|
||||
+#endif
|
||||
+
|
||||
+/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
|
||||
+#ifdef OPENSSL_VERSION_3_3
|
||||
+#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
|
||||
+#endif
|
||||
+
|
||||
#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
|
||||
#define OPENSSL_NO_SSL2
|
||||
#endif
|
||||
@@ -4675,6 +4689,54 @@ set_sni_callback(PySSLContext *self, PyO
|
||||
#endif
|
||||
}
|
||||
|
||||
+/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
|
||||
+ * Only available with the X509_STORE_lock() API */
|
||||
+#if defined(HAVE_OPENSSL_X509_STORE_LOCK) && !defined(OPENSSL_VERSION_3_3)
|
||||
+#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
|
||||
+
|
||||
+static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
|
||||
+{
|
||||
+ int ok;
|
||||
+ X509_OBJECT *ret = X509_OBJECT_new();
|
||||
+ if (ret == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ switch (X509_OBJECT_get_type(obj)) {
|
||||
+ case X509_LU_X509:
|
||||
+ ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
|
||||
+ break;
|
||||
+ case X509_LU_CRL:
|
||||
+ /* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
|
||||
+ ok = X509_OBJECT_set1_X509_CRL(
|
||||
+ ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
|
||||
+ break;
|
||||
+ default:
|
||||
+ /* We cannot duplicate unrecognized types in a polyfill, but it is
|
||||
+ * safe to leave an empty object. The caller will ignore it. */
|
||||
+ ok = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ if (!ok) {
|
||||
+ X509_OBJECT_free(ret);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static STACK_OF(X509_OBJECT) *
|
||||
+X509_STORE_get1_objects(X509_STORE *store)
|
||||
+{
|
||||
+ STACK_OF(X509_OBJECT) *ret;
|
||||
+ if (!X509_STORE_lock(store)) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
|
||||
+ x509_object_dup, X509_OBJECT_free);
|
||||
+ X509_STORE_unlock(store);
|
||||
+ return ret;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
|
||||
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
|
||||
\n\
|
||||
@@ -4704,7 +4766,15 @@ _ssl__SSLContext_cert_store_stats_impl(P
|
||||
int x509 = 0, crl = 0, ca = 0, i;
|
||||
|
||||
store = SSL_CTX_get_cert_store(self->ctx);
|
||||
+#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
|
||||
+ objs = X509_STORE_get1_objects(store);
|
||||
+ if (objs == NULL) {
|
||||
+ PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+#else
|
||||
objs = X509_STORE_get0_objects(store);
|
||||
+#endif
|
||||
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
|
||||
obj = sk_X509_OBJECT_value(objs, i);
|
||||
switch (X509_OBJECT_get_type(obj)) {
|
||||
@@ -4718,12 +4788,13 @@ _ssl__SSLContext_cert_store_stats_impl(P
|
||||
crl++;
|
||||
break;
|
||||
default:
|
||||
- /* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
|
||||
- * As far as I can tell they are internal states and never
|
||||
- * stored in a cert store */
|
||||
+ /* Ignore unrecognized types. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
+#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
|
||||
+ sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
|
||||
+#endif
|
||||
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
|
||||
"x509_ca", ca);
|
||||
}
|
||||
@@ -4755,7 +4826,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSL
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(self->ctx);
|
||||
+#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
|
||||
+ objs = X509_STORE_get1_objects(store);
|
||||
+ if (objs == NULL) {
|
||||
+ PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+#else
|
||||
objs = X509_STORE_get0_objects(store);
|
||||
+#endif
|
||||
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
|
||||
X509_OBJECT *obj;
|
||||
X509 *cert;
|
||||
@@ -4783,9 +4862,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSL
|
||||
}
|
||||
Py_CLEAR(ci);
|
||||
}
|
||||
+#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
|
||||
+ sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
|
||||
+#endif
|
||||
return rlist;
|
||||
|
||||
error:
|
||||
+#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
|
||||
+ sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
|
||||
+#endif
|
||||
Py_XDECREF(ci);
|
||||
Py_XDECREF(rlist);
|
||||
return NULL;
|
||||
@@ -1,396 +0,0 @@
|
||||
From 5e58376d424fb951966277e5d46cf0b11d860ef3 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <encukou@gmail.com>
|
||||
Date: Wed, 24 Apr 2024 14:29:30 +0200
|
||||
Subject: [PATCH 1/3] gh-113171: gh-65056: Fix "private" (non-global) IP
|
||||
address ranges (GH-113179) (GH-113186) (GH-118177)
|
||||
|
||||
* GH-113171: Fix "private" (non-global) IP address ranges (GH-113179)
|
||||
|
||||
The _private_networks variables, used by various is_private
|
||||
implementations, were missing some ranges and at the same time had
|
||||
overly strict ranges (where there are more specific ranges considered
|
||||
globally reachable by the IANA registries).
|
||||
|
||||
This patch updates the ranges with what was missing or otherwise
|
||||
incorrect.
|
||||
|
||||
100.64.0.0/10 is left alone, for now, as it's been made special in [1].
|
||||
|
||||
The _address_exclude_many() call returns 8 networks for IPv4, 121
|
||||
networks for IPv6.
|
||||
|
||||
[1] https://github.com/python/cpython/issues/61602
|
||||
|
||||
* GH-65056: Improve the IP address' is_global/is_private documentation (GH-113186)
|
||||
|
||||
It wasn't clear what the semantics of is_global/is_private are and, when
|
||||
one gets to the bottom of it, it's not quite so simple (hence the
|
||||
exceptions listed).
|
||||
|
||||
(cherry picked from commit 2a4cbf17af19a01d942f9579342f77c39fbd23c4)
|
||||
(cherry picked from commit 40d75c2b7f5c67e254d0a025e0f2e2c7ada7f69f)
|
||||
|
||||
---------
|
||||
|
||||
(cherry picked from commit f86b17ac511e68192ba71f27e752321a3252cee3)
|
||||
|
||||
Co-authored-by: Jakub Stasiak <jakub@stasiak.at>
|
||||
---
|
||||
Doc/library/ipaddress.rst | 43 +++-
|
||||
Doc/tools/susp-ignored.csv | 8
|
||||
Doc/whatsnew/3.9.rst | 9
|
||||
Lib/ipaddress.py | 107 +++++++---
|
||||
Lib/test/test_ipaddress.py | 52 ++++
|
||||
Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst | 9
|
||||
6 files changed, 201 insertions(+), 27 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst
|
||||
|
||||
--- a/Doc/library/ipaddress.rst
|
||||
+++ b/Doc/library/ipaddress.rst
|
||||
@@ -188,18 +188,53 @@ write code that handles both IP versions
|
||||
|
||||
.. attribute:: is_private
|
||||
|
||||
- ``True`` if the address is allocated for private networks. See
|
||||
+ ``True`` if the address is defined as not globally reachable by
|
||||
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
- (for IPv6).
|
||||
+ (for IPv6) with the following exceptions:
|
||||
+
|
||||
+ * ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``)
|
||||
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
+
|
||||
+ address.is_private == address.ipv4_mapped.is_private
|
||||
+
|
||||
+ ``is_private`` has value opposite to :attr:`is_global`, except for the shared address space
|
||||
+ (``100.64.0.0/10`` range) where they are both ``False``.
|
||||
+
|
||||
+ .. versionchanged:: 3.9.20
|
||||
+
|
||||
+ Fixed some false positives and false negatives.
|
||||
+
|
||||
+ * ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and
|
||||
+ ``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private).
|
||||
+ * ``64:ff9b:1::/48`` is considered private.
|
||||
+ * ``2002::/16`` is considered private.
|
||||
+ * There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``,
|
||||
+ ``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``.
|
||||
+ The exceptions are not considered private.
|
||||
|
||||
.. attribute:: is_global
|
||||
|
||||
- ``True`` if the address is allocated for public networks. See
|
||||
+ ``True`` if the address is defined as globally reachable by
|
||||
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
- (for IPv6).
|
||||
+ (for IPv6) with the following exception:
|
||||
+
|
||||
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
+
|
||||
+ address.is_global == address.ipv4_mapped.is_global
|
||||
+
|
||||
+ ``is_global`` has value opposite to :attr:`is_private`, except for the shared address space
|
||||
+ (``100.64.0.0/10`` range) where they are both ``False``.
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
+ .. versionchanged:: 3.9.20
|
||||
+
|
||||
+ Fixed some false positives and false negatives, see :attr:`is_private` for details.
|
||||
+
|
||||
.. attribute:: is_unspecified
|
||||
|
||||
``True`` if the address is unspecified. See :RFC:`5735` (for IPv4)
|
||||
--- a/Doc/tools/susp-ignored.csv
|
||||
+++ b/Doc/tools/susp-ignored.csv
|
||||
@@ -169,6 +169,14 @@ library/ipaddress,,:db00,2001:db00::0/24
|
||||
library/ipaddress,,::,2001:db00::0/24
|
||||
library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
|
||||
library/ipaddress,,::,2001:db00::0/ffff:ff00::
|
||||
+library/ipaddress,,:ff9b,64:ff9b:1::/48
|
||||
+library/ipaddress,,::,64:ff9b:1::/48
|
||||
+library/ipaddress,,::,2001::
|
||||
+library/ipaddress,,::,2001:1::
|
||||
+library/ipaddress,,::,2001:3::
|
||||
+library/ipaddress,,::,2001:4:112::
|
||||
+library/ipaddress,,::,2001:20::
|
||||
+library/ipaddress,,::,2001:30::
|
||||
library/itertools,,:step,elements from seq[start:stop:step]
|
||||
library/itertools,,:stop,elements from seq[start:stop:step]
|
||||
library/itertools,,::,kernel = tuple(kernel)[::-1]
|
||||
--- a/Doc/whatsnew/3.9.rst
|
||||
+++ b/Doc/whatsnew/3.9.rst
|
||||
@@ -1616,3 +1616,12 @@ tarfile
|
||||
:exc:`DeprecationWarning`.
|
||||
In Python 3.14, the default will switch to ``'data'``.
|
||||
(Contributed by Petr Viktorin in :pep:`706`.)
|
||||
+
|
||||
+Notable changes in 3.9.20
|
||||
+=========================
|
||||
+
|
||||
+ipaddress
|
||||
+---------
|
||||
+
|
||||
+* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
|
||||
+ ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.
|
||||
--- a/Lib/ipaddress.py
|
||||
+++ b/Lib/ipaddress.py
|
||||
@@ -1322,18 +1322,41 @@ class IPv4Address(_BaseV4, _BaseAddress)
|
||||
@property
|
||||
@functools.lru_cache()
|
||||
def is_private(self):
|
||||
- """Test if this address is allocated for private networks.
|
||||
-
|
||||
- Returns:
|
||||
- A boolean, True if the address is reserved per
|
||||
- iana-ipv4-special-registry.
|
||||
-
|
||||
- """
|
||||
- return any(self in net for net in self._constants._private_networks)
|
||||
+ """``True`` if the address is defined as not globally reachable by
|
||||
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
+ (for IPv6) with the following exceptions:
|
||||
+
|
||||
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
|
||||
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
+
|
||||
+ address.is_private == address.ipv4_mapped.is_private
|
||||
+
|
||||
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
|
||||
+ IPv4 range where they are both ``False``.
|
||||
+ """
|
||||
+ return (
|
||||
+ any(self in net for net in self._constants._private_networks)
|
||||
+ and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||
+ )
|
||||
|
||||
@property
|
||||
@functools.lru_cache()
|
||||
def is_global(self):
|
||||
+ """``True`` if the address is defined as globally reachable by
|
||||
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
+ (for IPv6) with the following exception:
|
||||
+
|
||||
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
+
|
||||
+ address.is_global == address.ipv4_mapped.is_global
|
||||
+
|
||||
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
|
||||
+ IPv4 range where they are both ``False``.
|
||||
+ """
|
||||
return self not in self._constants._public_network and not self.is_private
|
||||
|
||||
@property
|
||||
@@ -1537,13 +1560,15 @@ class _IPv4Constants:
|
||||
|
||||
_public_network = IPv4Network('100.64.0.0/10')
|
||||
|
||||
+ # Not globally reachable address blocks listed on
|
||||
+ # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||
_private_networks = [
|
||||
IPv4Network('0.0.0.0/8'),
|
||||
IPv4Network('10.0.0.0/8'),
|
||||
IPv4Network('127.0.0.0/8'),
|
||||
IPv4Network('169.254.0.0/16'),
|
||||
IPv4Network('172.16.0.0/12'),
|
||||
- IPv4Network('192.0.0.0/29'),
|
||||
+ IPv4Network('192.0.0.0/24'),
|
||||
IPv4Network('192.0.0.170/31'),
|
||||
IPv4Network('192.0.2.0/24'),
|
||||
IPv4Network('192.168.0.0/16'),
|
||||
@@ -1554,6 +1579,11 @@ class _IPv4Constants:
|
||||
IPv4Network('255.255.255.255/32'),
|
||||
]
|
||||
|
||||
+ _private_networks_exceptions = [
|
||||
+ IPv4Network('192.0.0.9/32'),
|
||||
+ IPv4Network('192.0.0.10/32'),
|
||||
+ ]
|
||||
+
|
||||
_reserved_network = IPv4Network('240.0.0.0/4')
|
||||
|
||||
_unspecified_address = IPv4Address('0.0.0.0')
|
||||
@@ -1995,23 +2025,42 @@ class IPv6Address(_BaseV6, _BaseAddress)
|
||||
@property
|
||||
@functools.lru_cache()
|
||||
def is_private(self):
|
||||
- """Test if this address is allocated for private networks.
|
||||
-
|
||||
- Returns:
|
||||
- A boolean, True if the address is reserved per
|
||||
- iana-ipv6-special-registry.
|
||||
-
|
||||
- """
|
||||
- return any(self in net for net in self._constants._private_networks)
|
||||
+ """``True`` if the address is defined as not globally reachable by
|
||||
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
+ (for IPv6) with the following exceptions:
|
||||
+
|
||||
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
|
||||
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
+
|
||||
+ address.is_private == address.ipv4_mapped.is_private
|
||||
+
|
||||
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
|
||||
+ IPv4 range where they are both ``False``.
|
||||
+ """
|
||||
+ ipv4_mapped = self.ipv4_mapped
|
||||
+ if ipv4_mapped is not None:
|
||||
+ return ipv4_mapped.is_private
|
||||
+ return (
|
||||
+ any(self in net for net in self._constants._private_networks)
|
||||
+ and all(self not in net for net in self._constants._private_networks_exceptions)
|
||||
+ )
|
||||
|
||||
@property
|
||||
def is_global(self):
|
||||
- """Test if this address is allocated for public networks.
|
||||
+ """``True`` if the address is defined as globally reachable by
|
||||
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
|
||||
+ (for IPv6) with the following exception:
|
||||
+
|
||||
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
|
||||
+ semantics of the underlying IPv4 addresses and the following condition holds
|
||||
+ (see :attr:`IPv6Address.ipv4_mapped`)::
|
||||
|
||||
- Returns:
|
||||
- A boolean, true if the address is not reserved per
|
||||
- iana-ipv6-special-registry.
|
||||
+ address.is_global == address.ipv4_mapped.is_global
|
||||
|
||||
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
|
||||
+ IPv4 range where they are both ``False``.
|
||||
"""
|
||||
return not self.is_private
|
||||
|
||||
@@ -2252,19 +2301,31 @@ class _IPv6Constants:
|
||||
|
||||
_multicast_network = IPv6Network('ff00::/8')
|
||||
|
||||
+ # Not globally reachable address blocks listed on
|
||||
+ # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
||||
_private_networks = [
|
||||
IPv6Network('::1/128'),
|
||||
IPv6Network('::/128'),
|
||||
IPv6Network('::ffff:0:0/96'),
|
||||
+ IPv6Network('64:ff9b:1::/48'),
|
||||
IPv6Network('100::/64'),
|
||||
IPv6Network('2001::/23'),
|
||||
- IPv6Network('2001:2::/48'),
|
||||
IPv6Network('2001:db8::/32'),
|
||||
- IPv6Network('2001:10::/28'),
|
||||
+ # IANA says N/A, let's consider it not globally reachable to be safe
|
||||
+ IPv6Network('2002::/16'),
|
||||
IPv6Network('fc00::/7'),
|
||||
IPv6Network('fe80::/10'),
|
||||
]
|
||||
|
||||
+ _private_networks_exceptions = [
|
||||
+ IPv6Network('2001:1::1/128'),
|
||||
+ IPv6Network('2001:1::2/128'),
|
||||
+ IPv6Network('2001:3::/32'),
|
||||
+ IPv6Network('2001:4:112::/48'),
|
||||
+ IPv6Network('2001:20::/28'),
|
||||
+ IPv6Network('2001:30::/28'),
|
||||
+ ]
|
||||
+
|
||||
_reserved_networks = [
|
||||
IPv6Network('::/8'), IPv6Network('100::/8'),
|
||||
IPv6Network('200::/7'), IPv6Network('400::/6'),
|
||||
--- a/Lib/test/test_ipaddress.py
|
||||
+++ b/Lib/test/test_ipaddress.py
|
||||
@@ -2263,6 +2263,10 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||
self.assertEqual(True, ipaddress.ip_address(
|
||||
'172.31.255.255').is_private)
|
||||
self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private)
|
||||
+ self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
|
||||
|
||||
self.assertEqual(True,
|
||||
ipaddress.ip_address('169.254.100.200').is_link_local)
|
||||
@@ -2278,6 +2282,40 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||
self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback)
|
||||
self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified)
|
||||
|
||||
+ def testPrivateNetworks(self):
|
||||
+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private)
|
||||
+ self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private)
|
||||
+
|
||||
+ self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private)
|
||||
+ self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private)
|
||||
+
|
||||
+ self.assertEqual(False, ipaddress.ip_network("::/0").is_private)
|
||||
+ self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private)
|
||||
+
|
||||
+ self.assertEqual(True, ipaddress.ip_network("::1/128").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("::/128").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("100::/64").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private)
|
||||
+ self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private)
|
||||
+ self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private)
|
||||
+
|
||||
def testReservedIpv6(self):
|
||||
|
||||
self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast)
|
||||
@@ -2351,6 +2389,20 @@ class IpaddrUnitTest(unittest.TestCase):
|
||||
self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified)
|
||||
self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified)
|
||||
|
||||
+ self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2001::').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
|
||||
+ self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
|
||||
+ self.assertFalse(ipaddress.ip_address('2002::').is_global)
|
||||
+
|
||||
# some generic IETF reserved addresses
|
||||
self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
|
||||
self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved)
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2024-03-14-01-38-44.gh-issue-113171.VFnObz.rst
|
||||
@@ -0,0 +1,9 @@
|
||||
+Fixed various false positives and false negatives in
|
||||
+
|
||||
+* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details)
|
||||
+* :attr:`ipaddress.IPv4Address.is_global`
|
||||
+* :attr:`ipaddress.IPv6Address.is_private`
|
||||
+* :attr:`ipaddress.IPv6Address.is_global`
|
||||
+
|
||||
+Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network`
|
||||
+attributes.
|
||||
@@ -44,9 +44,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
15 files changed, 77 insertions(+), 873 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
|
||||
|
||||
--- a/Doc/using/unix.rst
|
||||
+++ b/Doc/using/unix.rst
|
||||
@@ -113,6 +113,7 @@ For example, on most Linux systems, the
|
||||
Index: Python-3.9.25/Doc/using/unix.rst
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Doc/using/unix.rst 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Doc/using/unix.rst 2025-12-19 23:25:05.628832227 +0100
|
||||
@@ -113,6 +113,7 @@
|
||||
| | embedding the interpreter. |
|
||||
+-----------------------------------------------+------------------------------------------+
|
||||
|
||||
@@ -54,9 +56,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
Miscellaneous
|
||||
=============
|
||||
--- a/Lib/ssl.py
|
||||
+++ b/Lib/ssl.py
|
||||
@@ -910,15 +910,12 @@ class SSLObject:
|
||||
Index: Python-3.9.25/Lib/ssl.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/ssl.py 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Lib/ssl.py 2025-12-19 23:25:05.629281589 +0100
|
||||
@@ -912,15 +912,12 @@
|
||||
"""Return the currently selected NPN protocol as a string, or ``None``
|
||||
if a next protocol was not negotiated or if NPN is not supported by one
|
||||
of the peers."""
|
||||
@@ -73,7 +77,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
def cipher(self):
|
||||
"""Return the currently selected cipher as a 3-tuple ``(name,
|
||||
@@ -1160,10 +1157,7 @@ class SSLSocket(socket):
|
||||
@@ -1162,10 +1159,7 @@
|
||||
@_sslcopydoc
|
||||
def selected_npn_protocol(self):
|
||||
self._checkClosed()
|
||||
@@ -85,9 +89,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
@_sslcopydoc
|
||||
def selected_alpn_protocol(self):
|
||||
--- a/Lib/test/test_ssl.py
|
||||
+++ b/Lib/test/test_ssl.py
|
||||
@@ -39,7 +39,6 @@ Py_DEBUG_WIN32 = Py_DEBUG and sys.platfo
|
||||
Index: Python-3.9.25/Lib/test/test_ssl.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_ssl.py 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_ssl.py 2025-12-19 23:25:05.630031412 +0100
|
||||
@@ -39,7 +39,6 @@
|
||||
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
|
||||
HOST = socket_helper.HOST
|
||||
IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL')
|
||||
@@ -95,7 +101,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
IS_OPENSSL_1_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1)
|
||||
IS_OPENSSL_3_0_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0)
|
||||
PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS')
|
||||
@@ -269,18 +268,6 @@ def handle_error(prefix):
|
||||
@@ -269,18 +268,6 @@
|
||||
if support.verbose:
|
||||
sys.stdout.write(prefix + exc_format)
|
||||
|
||||
@@ -114,7 +120,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def _have_secp_curves():
|
||||
if not ssl.HAS_ECDH:
|
||||
return False
|
||||
@@ -371,17 +358,15 @@ class BasicSocketTests(unittest.TestCase
|
||||
@@ -371,17 +358,15 @@
|
||||
ssl.OP_SINGLE_DH_USE
|
||||
if ssl.HAS_ECDH:
|
||||
ssl.OP_SINGLE_ECDH_USE
|
||||
@@ -135,7 +141,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23)
|
||||
|
||||
def test_private_init(self):
|
||||
@@ -1169,7 +1154,6 @@ class ContextTests(unittest.TestCase):
|
||||
@@ -1169,7 +1154,6 @@
|
||||
self.assertNotIn("RC4", name)
|
||||
self.assertNotIn("3DES", name)
|
||||
|
||||
@@ -143,7 +149,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_get_ciphers(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.set_ciphers('AESGCM')
|
||||
@@ -1201,15 +1185,11 @@ class ContextTests(unittest.TestCase):
|
||||
@@ -1201,15 +1185,11 @@
|
||||
self.assertEqual(default, ctx.options)
|
||||
ctx.options |= ssl.OP_NO_TLSv1
|
||||
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
|
||||
@@ -164,7 +170,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
def test_verify_mode_protocol(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
@@ -1328,8 +1308,6 @@ class ContextTests(unittest.TestCase):
|
||||
@@ -1328,8 +1308,6 @@
|
||||
with self.assertRaises(ValueError):
|
||||
ctx.maximum_version = ssl.TLSVersion.TLSv1
|
||||
|
||||
@@ -173,7 +179,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_verify_flags(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
# default value
|
||||
@@ -1807,7 +1785,6 @@ class ContextTests(unittest.TestCase):
|
||||
@@ -1807,7 +1785,6 @@
|
||||
obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO())
|
||||
self.assertIsInstance(obj, MySSLObject)
|
||||
|
||||
@@ -181,7 +187,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_num_tickest(self):
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
self.assertEqual(ctx.num_tickets, 2)
|
||||
@@ -2972,8 +2949,6 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -2972,8 +2949,6 @@
|
||||
after = ssl.cert_time_to_seconds(cert['notAfter'])
|
||||
self.assertLess(before, after)
|
||||
|
||||
@@ -190,7 +196,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_crl_check(self):
|
||||
if support.verbose:
|
||||
sys.stdout.write("\n")
|
||||
@@ -3877,12 +3852,7 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -3877,12 +3852,7 @@
|
||||
self.assertIs(s.version(), None)
|
||||
self.assertIs(s._sslobj, None)
|
||||
s.connect((HOST, server.port))
|
||||
@@ -204,7 +210,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
self.assertIs(s._sslobj, None)
|
||||
self.assertIs(s.version(), None)
|
||||
|
||||
@@ -3984,8 +3954,6 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -3984,8 +3954,6 @@
|
||||
# explicitly using the 'ECCdraft' cipher alias. Otherwise,
|
||||
# our default cipher list should prefer ECDH-based ciphers
|
||||
# automatically.
|
||||
@@ -213,7 +219,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
with ThreadedEchoServer(context=context) as server:
|
||||
with context.wrap_socket(socket.socket()) as s:
|
||||
s.connect((HOST, server.port))
|
||||
@@ -4117,15 +4085,11 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -4117,15 +4085,11 @@
|
||||
server_context.set_ciphers("ECDHE:!eNULL:!aNULL")
|
||||
server_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
|
||||
try:
|
||||
@@ -233,7 +239,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
def test_selected_alpn_protocol(self):
|
||||
# selected_alpn_protocol() is None unless ALPN is used.
|
||||
@@ -4135,7 +4099,6 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -4135,7 +4099,6 @@
|
||||
sni_name=hostname)
|
||||
self.assertIs(stats['client_alpn_protocol'], None)
|
||||
|
||||
@@ -241,7 +247,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_selected_alpn_protocol_if_server_uses_alpn(self):
|
||||
# selected_alpn_protocol() is None unless ALPN is used by the client.
|
||||
client_context, server_context, hostname = testing_context()
|
||||
@@ -4145,7 +4108,6 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -4145,7 +4108,6 @@
|
||||
sni_name=hostname)
|
||||
self.assertIs(stats['client_alpn_protocol'], None)
|
||||
|
||||
@@ -249,7 +255,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
def test_alpn_protocols(self):
|
||||
server_protocols = ['foo', 'bar', 'milkshake']
|
||||
protocol_tests = [
|
||||
@@ -4168,22 +4130,17 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -4168,22 +4130,17 @@
|
||||
except ssl.SSLError as e:
|
||||
stats = e
|
||||
|
||||
@@ -283,7 +289,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
def test_selected_npn_protocol(self):
|
||||
# selected_npn_protocol() is None unless NPN is used
|
||||
@@ -4193,31 +4150,8 @@ class ThreadedTests(unittest.TestCase):
|
||||
@@ -4193,31 +4150,8 @@
|
||||
sni_name=hostname)
|
||||
self.assertIs(stats['client_npn_protocol'], None)
|
||||
|
||||
@@ -314,9 +320,9 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
- self.assertEqual(server_result, expected, msg % (server_result, "server"))
|
||||
+ assert not ssl.HAS_NPN
|
||||
|
||||
def sni_contexts(self):
|
||||
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
@@ -4387,8 +4321,7 @@ class ThreadedTests(unittest.TestCase):
|
||||
def test_empty_npn_protocols(self):
|
||||
"""npn_protocols cannot be empty, see CVE-2024-5642 & gh-121227"""
|
||||
@@ -4393,8 +4327,7 @@
|
||||
self.assertGreater(session.time, 0)
|
||||
self.assertGreater(session.timeout, 0)
|
||||
self.assertTrue(session.has_ticket)
|
||||
@@ -326,13 +332,17 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
self.assertFalse(stats['session_reused'])
|
||||
sess_stat = server_context.session_stats()
|
||||
self.assertEqual(sess_stat['accept'], 1)
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
|
||||
Index: Python-3.9.25/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ Python-3.9.25/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst 2025-12-19 23:25:05.630820206 +0100
|
||||
@@ -0,0 +1 @@
|
||||
+Implement :pep:`644`. Python now requires OpenSSL 1.1.1 or newer.
|
||||
--- a/Modules/Setup
|
||||
+++ b/Modules/Setup
|
||||
@@ -210,11 +210,23 @@ _symtable symtablemodule.c
|
||||
Index: Python-3.9.25/Modules/Setup
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/Setup 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/Setup 2025-12-19 23:25:05.631164735 +0100
|
||||
@@ -210,11 +210,23 @@
|
||||
#_socket socketmodule.c
|
||||
|
||||
# Socket module helper for SSL support; you must comment out the other
|
||||
@@ -361,8 +371,10 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
# The crypt module is now disabled by default because it breaks builds
|
||||
# on many systems (where -lcrypt is needed), e.g. Linux (I believe).
|
||||
--- a/Modules/_hashopenssl.c
|
||||
+++ b/Modules/_hashopenssl.c
|
||||
Index: Python-3.9.25/Modules/_hashopenssl.c
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/_hashopenssl.c 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/_hashopenssl.c 2025-12-19 23:25:05.631562345 +0100
|
||||
@@ -43,51 +43,12 @@
|
||||
# error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
|
||||
#endif
|
||||
@@ -416,7 +428,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
#define PY_EVP_MD EVP_MD
|
||||
@@ -1311,8 +1272,7 @@ pbkdf2_hmac_impl(PyObject *module, const
|
||||
@@ -1311,8 +1272,7 @@
|
||||
return key_obj;
|
||||
}
|
||||
|
||||
@@ -426,7 +438,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* XXX: Parameters salt, n, r and p should be required keyword-only parameters.
|
||||
They are optional in the Argument Clinic declaration only due to a
|
||||
@@ -1433,7 +1393,7 @@ _hashlib_scrypt_impl(PyObject *module, P
|
||||
@@ -1433,7 +1393,7 @@
|
||||
}
|
||||
return key_obj;
|
||||
}
|
||||
@@ -435,7 +447,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* Fast HMAC for hmac.digest()
|
||||
*/
|
||||
@@ -1920,12 +1880,6 @@ hashlib_md_meth_names(PyObject *module)
|
||||
@@ -1920,12 +1880,6 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -448,7 +460,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/*[clinic input]
|
||||
_hashlib.get_fips_mode -> int
|
||||
|
||||
@@ -1963,7 +1917,6 @@ _hashlib_get_fips_mode_impl(PyObject *mo
|
||||
@@ -1963,7 +1917,6 @@
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
@@ -456,7 +468,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
|
||||
static int
|
||||
@@ -2144,17 +2097,6 @@ hashlib_free(void *m)
|
||||
@@ -2144,17 +2097,6 @@
|
||||
|
||||
/* Py_mod_exec functions */
|
||||
static int
|
||||
@@ -474,7 +486,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
hashlib_init_hashtable(PyObject *module)
|
||||
{
|
||||
_hashlibstate *state = get_hashlib_state(module);
|
||||
@@ -2227,10 +2169,7 @@ hashlib_init_hmactype(PyObject *module)
|
||||
@@ -2227,10 +2169,7 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -485,7 +497,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
{Py_mod_exec, hashlib_init_hashtable},
|
||||
{Py_mod_exec, hashlib_init_evptype},
|
||||
{Py_mod_exec, hashlib_init_evpxoftype},
|
||||
@@ -2238,7 +2177,6 @@ static PyModuleDef_Slot hashlib_slots[]
|
||||
@@ -2238,7 +2177,6 @@
|
||||
{Py_mod_exec, hashlib_md_meth_names},
|
||||
{0, NULL}
|
||||
};
|
||||
@@ -493,7 +505,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
static struct PyModuleDef _hashlibmodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
@@ -2246,7 +2184,7 @@ static struct PyModuleDef _hashlibmodule
|
||||
@@ -2246,7 +2184,7 @@
|
||||
.m_doc = "OpenSSL interface for hashlib module",
|
||||
.m_size = sizeof(_hashlibstate),
|
||||
.m_methods = EVP_functions,
|
||||
@@ -502,7 +514,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
.m_traverse = hashlib_traverse,
|
||||
.m_clear = hashlib_clear,
|
||||
.m_free = hashlib_free
|
||||
@@ -2255,41 +2193,5 @@ static struct PyModuleDef _hashlibmodule
|
||||
@@ -2255,41 +2193,5 @@
|
||||
PyMODINIT_FUNC
|
||||
PyInit__hashlib(void)
|
||||
{
|
||||
@@ -545,8 +557,10 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
- return m;
|
||||
+ return PyModuleDef_Init(&_hashlibmodule);
|
||||
}
|
||||
--- a/Modules/_ssl.c
|
||||
+++ b/Modules/_ssl.c
|
||||
Index: Python-3.9.25/Modules/_ssl.c
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/_ssl.c 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/_ssl.c 2025-12-19 23:25:05.632257411 +0100
|
||||
@@ -29,9 +29,9 @@
|
||||
#define _PySSL_FIX_ERRNO
|
||||
|
||||
@@ -559,7 +573,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
#define PySSL_BEGIN_ALLOW_THREADS { \
|
||||
PyThreadState *_save = NULL; \
|
||||
PySSL_BEGIN_ALLOW_THREADS_S(_save);
|
||||
@@ -62,16 +62,6 @@ static PySocketModule_APIObject PySocket
|
||||
@@ -62,16 +62,6 @@
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/dh.h"
|
||||
|
||||
@@ -576,7 +590,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
#ifndef OPENSSL_THREADS
|
||||
# error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
|
||||
#endif
|
||||
@@ -142,15 +132,7 @@ static void _PySSLFixErrno(void) {
|
||||
@@ -142,15 +132,7 @@
|
||||
#include "_ssl_data.h"
|
||||
#endif
|
||||
|
||||
@@ -593,7 +607,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
#ifndef OPENSSL_NO_TLS1_METHOD
|
||||
extern const SSL_METHOD *TLSv1_method(void);
|
||||
#endif
|
||||
@@ -161,59 +143,10 @@ extern const SSL_METHOD *TLSv1_1_method(
|
||||
@@ -161,59 +143,10 @@
|
||||
extern const SSL_METHOD *TLSv1_2_method(void);
|
||||
#endif
|
||||
|
||||
@@ -653,7 +667,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
|
||||
#ifdef OPENSSL_VERSION_1_1
|
||||
#define HAVE_OPENSSL_X509_STORE_LOCK
|
||||
@@ -224,80 +157,8 @@ extern const SSL_METHOD *TLSv1_2_method(
|
||||
@@ -224,80 +157,8 @@
|
||||
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
|
||||
#endif
|
||||
|
||||
@@ -735,7 +749,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* Default cipher suites */
|
||||
#ifndef PY_SSL_DEFAULT_CIPHERS
|
||||
@@ -409,24 +270,10 @@ enum py_proto_version {
|
||||
@@ -409,24 +270,10 @@
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -760,7 +774,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* In case of 'tls-unique' it will be 12 bytes for TLS, 36 bytes for
|
||||
* older SSL, but let's be safe */
|
||||
@@ -436,17 +283,9 @@ static unsigned int _ssl_locks_count = 0
|
||||
@@ -436,17 +283,9 @@
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
SSL_CTX *ctx;
|
||||
@@ -778,7 +792,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
int check_hostname;
|
||||
/* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct.
|
||||
* We have to maintain our own copy. OpenSSL's hostflags default to 0.
|
||||
@@ -457,10 +296,8 @@ typedef struct {
|
||||
@@ -457,10 +296,8 @@
|
||||
int post_handshake_auth;
|
||||
#endif
|
||||
PyObject *msg_cb;
|
||||
@@ -789,7 +803,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
} PySSLContext;
|
||||
|
||||
typedef struct {
|
||||
@@ -667,23 +504,18 @@ fill_and_set_sslerror(PySSLSocket *sslso
|
||||
@@ -667,23 +504,18 @@
|
||||
}
|
||||
|
||||
switch (verify_code) {
|
||||
@@ -813,7 +827,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
default:
|
||||
verify_str = X509_verify_cert_error_string(verify_code);
|
||||
if (verify_str != NULL) {
|
||||
@@ -2014,7 +1846,6 @@ cipher_to_tuple(const SSL_CIPHER *cipher
|
||||
@@ -2014,7 +1846,6 @@
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -821,7 +835,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static PyObject *
|
||||
cipher_to_dict(const SSL_CIPHER *cipher)
|
||||
{
|
||||
@@ -2023,10 +1854,8 @@ cipher_to_dict(const SSL_CIPHER *cipher)
|
||||
@@ -2023,10 +1854,8 @@
|
||||
unsigned long cipher_id;
|
||||
int alg_bits, strength_bits, len;
|
||||
char buf[512] = {0};
|
||||
@@ -832,7 +846,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* can be NULL */
|
||||
cipher_name = SSL_CIPHER_get_name(cipher);
|
||||
@@ -2039,7 +1868,6 @@ cipher_to_dict(const SSL_CIPHER *cipher)
|
||||
@@ -2039,7 +1868,6 @@
|
||||
buf[len-1] = '\0';
|
||||
strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
|
||||
|
||||
@@ -840,7 +854,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
aead = SSL_CIPHER_is_aead(cipher);
|
||||
nid = SSL_CIPHER_get_cipher_nid(cipher);
|
||||
skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||
@@ -2049,13 +1877,10 @@ cipher_to_dict(const SSL_CIPHER *cipher)
|
||||
@@ -2049,13 +1877,10 @@
|
||||
kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||
nid = SSL_CIPHER_get_auth_nid(cipher);
|
||||
auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||
@@ -854,7 +868,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
"}",
|
||||
"id", cipher_id,
|
||||
"name", cipher_name,
|
||||
@@ -2063,16 +1888,13 @@ cipher_to_dict(const SSL_CIPHER *cipher)
|
||||
@@ -2063,16 +1888,13 @@
|
||||
"description", buf,
|
||||
"strength_bits", strength_bits,
|
||||
"alg_bits", alg_bits
|
||||
@@ -871,7 +885,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/*[clinic input]
|
||||
_ssl._SSLSocket.shared_ciphers
|
||||
@@ -2143,28 +1965,6 @@ _ssl__SSLSocket_version_impl(PySSLSocket
|
||||
@@ -2143,28 +1965,6 @@
|
||||
return PyUnicode_FromString(version);
|
||||
}
|
||||
|
||||
@@ -900,7 +914,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/*[clinic input]
|
||||
_ssl._SSLSocket.selected_alpn_protocol
|
||||
[clinic start generated code]*/
|
||||
@@ -2182,7 +1982,6 @@ _ssl__SSLSocket_selected_alpn_protocol_i
|
||||
@@ -2182,7 +1982,6 @@
|
||||
Py_RETURN_NONE;
|
||||
return PyUnicode_FromStringAndSize((char *)out, outlen);
|
||||
}
|
||||
@@ -908,7 +922,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/*[clinic input]
|
||||
_ssl._SSLSocket.compression
|
||||
@@ -2219,11 +2018,6 @@ static int PySSL_set_context(PySSLSocket
|
||||
@@ -2219,11 +2018,6 @@
|
||||
void *closure) {
|
||||
|
||||
if (PyObject_TypeCheck(value, &PySSLContext_Type)) {
|
||||
@@ -920,7 +934,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
Py_INCREF(value);
|
||||
Py_SETREF(self->ctx, (PySSLContext *)value);
|
||||
SSL_set_SSL_CTX(self->ssl, self->ctx->ctx);
|
||||
@@ -2232,7 +2026,6 @@ static int PySSL_set_context(PySSLSocket
|
||||
@@ -2232,7 +2026,6 @@
|
||||
self->ssl,
|
||||
self->ctx->msg_cb ? _PySSL_msg_callback : NULL
|
||||
);
|
||||
@@ -928,7 +942,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext");
|
||||
return -1;
|
||||
@@ -2857,8 +2650,6 @@ _ssl__SSLSocket_verify_client_post_hands
|
||||
@@ -2857,8 +2650,6 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -937,7 +951,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static SSL_SESSION*
|
||||
_ssl_session_dup(SSL_SESSION *session) {
|
||||
SSL_SESSION *newsession = NULL;
|
||||
@@ -2899,7 +2690,6 @@ _ssl_session_dup(SSL_SESSION *session) {
|
||||
@@ -2899,7 +2690,6 @@
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -945,7 +959,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
static PyObject *
|
||||
PySSL_get_session(PySSLSocket *self, void *closure) {
|
||||
@@ -2908,7 +2698,6 @@ PySSL_get_session(PySSLSocket *self, voi
|
||||
@@ -2908,7 +2698,6 @@
|
||||
PySSLSession *pysess;
|
||||
SSL_SESSION *session;
|
||||
|
||||
@@ -953,7 +967,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/* duplicate session as workaround for session bug in OpenSSL 1.1.0,
|
||||
* https://github.com/openssl/openssl/issues/1550 */
|
||||
session = SSL_get0_session(self->ssl); /* borrowed reference */
|
||||
@@ -2918,12 +2707,10 @@ PySSL_get_session(PySSLSocket *self, voi
|
||||
@@ -2918,12 +2707,10 @@
|
||||
if ((session = _ssl_session_dup(session)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -966,7 +980,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
pysess = PyObject_GC_New(PySSLSession, &PySSLSession_Type);
|
||||
if (pysess == NULL) {
|
||||
SSL_SESSION_free(session);
|
||||
@@ -2942,9 +2729,7 @@ static int PySSL_set_session(PySSLSocket
|
||||
@@ -2942,9 +2729,7 @@
|
||||
void *closure)
|
||||
{
|
||||
PySSLSession *pysess;
|
||||
@@ -976,7 +990,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
int result;
|
||||
|
||||
if (!PySSLSession_Check(value)) {
|
||||
@@ -2968,7 +2753,6 @@ static int PySSL_set_session(PySSLSocket
|
||||
@@ -2968,7 +2753,6 @@
|
||||
"Cannot set session after handshake.");
|
||||
return -1;
|
||||
}
|
||||
@@ -984,7 +998,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/* duplicate session */
|
||||
if ((session = _ssl_session_dup(pysess->session)) == NULL) {
|
||||
return -1;
|
||||
@@ -2976,9 +2760,6 @@ static int PySSL_set_session(PySSLSocket
|
||||
@@ -2976,9 +2760,6 @@
|
||||
result = SSL_set_session(self->ssl, session);
|
||||
/* free duplicate, SSL_set_session() bumps ref count */
|
||||
SSL_SESSION_free(session);
|
||||
@@ -994,7 +1008,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
if (result == 0) {
|
||||
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||
return -1;
|
||||
@@ -3029,7 +2810,6 @@ static PyMethodDef PySSLMethods[] = {
|
||||
@@ -3029,7 +2810,6 @@
|
||||
_SSL__SSLSOCKET_CIPHER_METHODDEF
|
||||
_SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF
|
||||
_SSL__SSLSOCKET_VERSION_METHODDEF
|
||||
@@ -1002,7 +1016,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF
|
||||
_SSL__SSLSOCKET_COMPRESSION_METHODDEF
|
||||
_SSL__SSLSOCKET_SHUTDOWN_METHODDEF
|
||||
@@ -3123,9 +2903,6 @@ _ssl__SSLContext_impl(PyTypeObject *type
|
||||
@@ -3123,9 +2903,6 @@
|
||||
SSL_CTX *ctx = NULL;
|
||||
X509_VERIFY_PARAM *params;
|
||||
int result;
|
||||
@@ -1012,7 +1026,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
switch(proto_version) {
|
||||
@@ -3190,19 +2967,10 @@ _ssl__SSLContext_impl(PyTypeObject *type
|
||||
@@ -3190,19 +2967,10 @@
|
||||
self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
|
||||
self->protocol = proto_version;
|
||||
self->msg_cb = NULL;
|
||||
@@ -1032,7 +1046,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/* Don't check host name by default */
|
||||
if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
|
||||
self->check_hostname = 1;
|
||||
@@ -3264,37 +3032,9 @@ _ssl__SSLContext_impl(PyTypeObject *type
|
||||
@@ -3264,37 +3032,9 @@
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1072,7 +1086,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
#define SID_CTX "Python"
|
||||
SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX,
|
||||
@@ -3302,11 +3042,9 @@ _ssl__SSLContext_impl(PyTypeObject *type
|
||||
@@ -3302,11 +3042,9 @@
|
||||
#undef SID_CTX
|
||||
|
||||
params = SSL_CTX_get0_param(self->ctx);
|
||||
@@ -1084,7 +1098,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
X509_VERIFY_PARAM_set_hostflags(params, self->hostflags);
|
||||
|
||||
#ifdef TLS1_3_VERSION
|
||||
@@ -3320,9 +3058,7 @@ _ssl__SSLContext_impl(PyTypeObject *type
|
||||
@@ -3320,9 +3058,7 @@
|
||||
static int
|
||||
context_traverse(PySSLContext *self, visitproc visit, void *arg)
|
||||
{
|
||||
@@ -1094,7 +1108,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
Py_VISIT(self->msg_cb);
|
||||
return 0;
|
||||
}
|
||||
@@ -3330,11 +3066,8 @@ context_traverse(PySSLContext *self, vis
|
||||
@@ -3330,11 +3066,8 @@
|
||||
static int
|
||||
context_clear(PySSLContext *self)
|
||||
{
|
||||
@@ -1106,7 +1120,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
Py_CLEAR(self->keylog_filename);
|
||||
if (self->keylog_bio != NULL) {
|
||||
PySSL_BEGIN_ALLOW_THREADS
|
||||
@@ -3342,7 +3075,6 @@ context_clear(PySSLContext *self)
|
||||
@@ -3342,7 +3075,6 @@
|
||||
PySSL_END_ALLOW_THREADS
|
||||
self->keylog_bio = NULL;
|
||||
}
|
||||
@@ -1114,7 +1128,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3353,12 +3085,7 @@ context_dealloc(PySSLContext *self)
|
||||
@@ -3353,12 +3085,7 @@
|
||||
PyObject_GC_UnTrack(self);
|
||||
context_clear(self);
|
||||
SSL_CTX_free(self->ctx);
|
||||
@@ -1127,7 +1141,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
@@ -3385,7 +3112,6 @@ _ssl__SSLContext_set_ciphers_impl(PySSLC
|
||||
@@ -3385,7 +3112,6 @@
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -1135,7 +1149,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/*[clinic input]
|
||||
_ssl._SSLContext.get_ciphers
|
||||
[clinic start generated code]*/
|
||||
@@ -3428,10 +3154,8 @@ _ssl__SSLContext_get_ciphers_impl(PySSLC
|
||||
@@ -3428,10 +3154,8 @@
|
||||
return result;
|
||||
|
||||
}
|
||||
@@ -1146,7 +1160,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static int
|
||||
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *server_protocols, unsigned int server_protocols_len,
|
||||
@@ -3455,77 +3179,7 @@ do_protocol_selection(int alpn, unsigned
|
||||
@@ -3455,77 +3179,7 @@
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
@@ -1224,7 +1238,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static int
|
||||
_selectALPN_cb(SSL *s,
|
||||
const unsigned char **out, unsigned char *outlen,
|
||||
@@ -3537,7 +3191,6 @@ _selectALPN_cb(SSL *s,
|
||||
@@ -3537,7 +3191,6 @@
|
||||
ctx->alpn_protocols, ctx->alpn_protocols_len,
|
||||
client_protocols, client_protocols_len);
|
||||
}
|
||||
@@ -1232,7 +1246,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/*[clinic input]
|
||||
_ssl._SSLContext._set_alpn_protocols
|
||||
@@ -3550,7 +3203,6 @@ _ssl__SSLContext__set_alpn_protocols_imp
|
||||
@@ -3550,7 +3203,6 @@
|
||||
Py_buffer *protos)
|
||||
/*[clinic end generated code: output=87599a7f76651a9b input=9bba964595d519be]*/
|
||||
{
|
||||
@@ -1240,7 +1254,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
if ((size_t)protos->len > UINT_MAX) {
|
||||
PyErr_Format(PyExc_OverflowError,
|
||||
"protocols longer than %u bytes", UINT_MAX);
|
||||
@@ -3569,11 +3221,6 @@ _ssl__SSLContext__set_alpn_protocols_imp
|
||||
@@ -3569,11 +3221,6 @@
|
||||
SSL_CTX_set_alpn_select_cb(self->ctx, _selectALPN_cb, self);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
@@ -1252,7 +1266,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
@@ -3649,9 +3296,6 @@ set_verify_flags(PySSLContext *self, PyO
|
||||
@@ -3649,9 +3296,6 @@
|
||||
}
|
||||
|
||||
/* Getter and setter for protocol version */
|
||||
@@ -1262,7 +1276,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static int
|
||||
set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what)
|
||||
{
|
||||
@@ -3746,9 +3390,8 @@ set_maximum_version(PySSLContext *self,
|
||||
@@ -3746,9 +3390,8 @@
|
||||
{
|
||||
return set_min_max_proto_version(self, arg, 1);
|
||||
}
|
||||
@@ -1273,7 +1287,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static PyObject *
|
||||
get_num_tickets(PySSLContext *self, void *c)
|
||||
{
|
||||
@@ -3779,7 +3422,7 @@ set_num_tickets(PySSLContext *self, PyOb
|
||||
@@ -3779,7 +3422,7 @@
|
||||
|
||||
PyDoc_STRVAR(PySSLContext_num_tickets_doc,
|
||||
"Control the number of TLSv1.3 session tickets");
|
||||
@@ -1282,7 +1296,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
static PyObject *
|
||||
get_options(PySSLContext *self, void *c)
|
||||
@@ -3797,13 +3440,7 @@ set_options(PySSLContext *self, PyObject
|
||||
@@ -3797,13 +3440,7 @@
|
||||
clear = opts & ~new_opts;
|
||||
set = ~opts & new_opts;
|
||||
if (clear) {
|
||||
@@ -1296,7 +1310,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
}
|
||||
if (set)
|
||||
SSL_CTX_set_options(self->ctx, set);
|
||||
@@ -4500,7 +4137,6 @@ _ssl__SSLContext_set_default_verify_path
|
||||
@@ -4500,7 +4137,6 @@
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -1304,7 +1318,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/*[clinic input]
|
||||
_ssl._SSLContext.set_ecdh_curve
|
||||
name: object
|
||||
@@ -4535,9 +4171,7 @@ _ssl__SSLContext_set_ecdh_curve(PySSLCon
|
||||
@@ -4535,9 +4171,7 @@
|
||||
EC_KEY_free(key);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@@ -1314,7 +1328,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static int
|
||||
_servername_callback(SSL *s, int *al, void *args)
|
||||
{
|
||||
@@ -4641,7 +4275,6 @@ error:
|
||||
@@ -4641,7 +4275,6 @@
|
||||
PyGILState_Release(gstate);
|
||||
return ret;
|
||||
}
|
||||
@@ -1322,7 +1336,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
static PyObject *
|
||||
get_sni_callback(PySSLContext *self, void *c)
|
||||
@@ -4662,7 +4295,6 @@ set_sni_callback(PySSLContext *self, PyO
|
||||
@@ -4662,7 +4295,6 @@
|
||||
"sni_callback cannot be set on TLS_CLIENT context");
|
||||
return -1;
|
||||
}
|
||||
@@ -1330,7 +1344,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
Py_CLEAR(self->set_sni_cb);
|
||||
if (arg == Py_None) {
|
||||
SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL);
|
||||
@@ -4680,13 +4312,6 @@ set_sni_callback(PySSLContext *self, PyO
|
||||
@@ -4680,13 +4312,6 @@
|
||||
SSL_CTX_set_tlsext_servername_arg(self->ctx, self);
|
||||
}
|
||||
return 0;
|
||||
@@ -1344,7 +1358,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
}
|
||||
|
||||
/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
|
||||
@@ -4882,21 +4507,17 @@ static PyGetSetDef context_getsetlist[]
|
||||
@@ -4882,21 +4507,17 @@
|
||||
(setter) set_check_hostname, NULL},
|
||||
{"_host_flags", (getter) get_host_flags,
|
||||
(setter) set_host_flags, NULL},
|
||||
@@ -1367,7 +1381,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
{"num_tickets", (getter) get_num_tickets,
|
||||
(setter) set_num_tickets, PySSLContext_num_tickets_doc},
|
||||
#endif
|
||||
@@ -4923,7 +4544,6 @@ static struct PyMethodDef context_method
|
||||
@@ -4923,7 +4544,6 @@
|
||||
_SSL__SSLCONTEXT__WRAP_BIO_METHODDEF
|
||||
_SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF
|
||||
_SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF
|
||||
@@ -1375,7 +1389,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
_SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF
|
||||
_SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF
|
||||
_SSL__SSLCONTEXT_LOAD_VERIFY_LOCATIONS_METHODDEF
|
||||
@@ -5441,11 +5061,7 @@ PySSL_RAND(int len, int pseudo)
|
||||
@@ -5441,11 +5061,7 @@
|
||||
if (bytes == NULL)
|
||||
return NULL;
|
||||
if (pseudo) {
|
||||
@@ -1387,7 +1401,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
if (ok == 0 || ok == 1)
|
||||
return Py_BuildValue("NO", bytes, ok == 1 ? Py_True : Py_False);
|
||||
}
|
||||
@@ -6000,92 +5616,6 @@ static PyMethodDef PySSL_methods[] = {
|
||||
@@ -6000,92 +5616,6 @@
|
||||
};
|
||||
|
||||
|
||||
@@ -1480,7 +1494,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"Implementation module for SSL socket operations. See the socket module\n\
|
||||
for documentation.");
|
||||
@@ -6152,14 +5682,6 @@ PyInit__ssl(void)
|
||||
@@ -6152,14 +5682,6 @@
|
||||
return NULL;
|
||||
PySocketModule = *socket_api;
|
||||
|
||||
@@ -1495,7 +1509,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
#ifdef HAVE_OPENSSL_CRYPTO_LOCK
|
||||
/* note that this will start threading if not already started */
|
||||
if (!_setup_ssl_threads()) {
|
||||
@@ -6266,10 +5788,8 @@ PyInit__ssl(void)
|
||||
@@ -6266,10 +5788,8 @@
|
||||
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
|
||||
PyModule_AddIntConstant(m, "VERIFY_X509_STRICT",
|
||||
X509_V_FLAG_X509_STRICT);
|
||||
@@ -1506,7 +1520,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
/* Alert Descriptions from ssl.h */
|
||||
/* note RESERVED constants no longer intended for use have been removed */
|
||||
@@ -6426,31 +5946,11 @@ PyInit__ssl(void)
|
||||
@@ -6426,31 +5946,11 @@
|
||||
PyModule_AddObject((m), (key), bool_obj); \
|
||||
} while (0)
|
||||
|
||||
@@ -1538,9 +1552,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
#if defined(SSL2_VERSION) && !defined(OPENSSL_NO_SSL2)
|
||||
addbool(m, "HAS_SSLv2", 1);
|
||||
--- a/Modules/_ssl/debughelpers.c
|
||||
+++ b/Modules/_ssl/debughelpers.c
|
||||
@@ -114,8 +114,6 @@ _PySSLContext_set_msg_callback(PySSLCont
|
||||
Index: Python-3.9.25/Modules/_ssl/debughelpers.c
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/_ssl/debughelpers.c 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/_ssl/debughelpers.c 2025-12-19 23:25:05.633150689 +0100
|
||||
@@ -114,8 +114,6 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1549,15 +1565,17 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
static void
|
||||
_PySSL_keylog_callback(const SSL *ssl, const char *line)
|
||||
{
|
||||
@@ -219,5 +217,3 @@ _PySSLContext_set_keylog_filename(PySSLC
|
||||
@@ -219,5 +217,3 @@
|
||||
SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback);
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
-#endif
|
||||
--- a/Modules/clinic/_hashopenssl.c.h
|
||||
+++ b/Modules/clinic/_hashopenssl.c.h
|
||||
@@ -965,7 +965,7 @@ exit:
|
||||
Index: Python-3.9.25/Modules/clinic/_hashopenssl.c.h
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/clinic/_hashopenssl.c.h 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/clinic/_hashopenssl.c.h 2025-12-19 23:25:05.633452755 +0100
|
||||
@@ -965,7 +965,7 @@
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -1566,7 +1584,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
PyDoc_STRVAR(_hashlib_scrypt__doc__,
|
||||
"scrypt($module, /, password, *, salt=None, n=None, r=None, p=None,\n"
|
||||
@@ -1093,7 +1093,7 @@ exit:
|
||||
@@ -1093,7 +1093,7 @@
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -1575,7 +1593,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
PyDoc_STRVAR(_hashlib_hmac_singleshot__doc__,
|
||||
"hmac_digest($module, /, key, msg, digest)\n"
|
||||
@@ -1324,8 +1324,6 @@ _hashlib_HMAC_hexdigest(HMACobject *self
|
||||
@@ -1324,8 +1324,6 @@
|
||||
return _hashlib_HMAC_hexdigest_impl(self);
|
||||
}
|
||||
|
||||
@@ -1584,7 +1602,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_hashlib_get_fips_mode__doc__,
|
||||
"get_fips_mode($module, /)\n"
|
||||
"--\n"
|
||||
@@ -1361,8 +1359,6 @@ exit:
|
||||
@@ -1361,8 +1359,6 @@
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -1593,7 +1611,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_hashlib_compare_digest__doc__,
|
||||
"compare_digest($module, a, b, /)\n"
|
||||
"--\n"
|
||||
@@ -1439,7 +1435,4 @@ exit:
|
||||
@@ -1439,7 +1435,4 @@
|
||||
#define _HASHLIB_SCRYPT_METHODDEF
|
||||
#endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */
|
||||
|
||||
@@ -1601,9 +1619,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
- #define _HASHLIB_GET_FIPS_MODE_METHODDEF
|
||||
-#endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */
|
||||
/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/
|
||||
--- a/Modules/clinic/_ssl.c.h
|
||||
+++ b/Modules/clinic/_ssl.c.h
|
||||
@@ -139,29 +139,6 @@ _ssl__SSLSocket_version(PySSLSocket *sel
|
||||
Index: Python-3.9.25/Modules/clinic/_ssl.c.h
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Modules/clinic/_ssl.c.h 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Modules/clinic/_ssl.c.h 2025-12-19 23:25:05.633884238 +0100
|
||||
@@ -139,29 +139,6 @@
|
||||
return _ssl__SSLSocket_version_impl(self);
|
||||
}
|
||||
|
||||
@@ -1633,7 +1653,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLSocket_selected_alpn_protocol__doc__,
|
||||
"selected_alpn_protocol($self, /)\n"
|
||||
"--\n"
|
||||
@@ -179,8 +156,6 @@ _ssl__SSLSocket_selected_alpn_protocol(P
|
||||
@@ -179,8 +156,6 @@
|
||||
return _ssl__SSLSocket_selected_alpn_protocol_impl(self);
|
||||
}
|
||||
|
||||
@@ -1642,7 +1662,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLSocket_compression__doc__,
|
||||
"compression($self, /)\n"
|
||||
"--\n"
|
||||
@@ -457,8 +432,6 @@ exit:
|
||||
@@ -457,8 +432,6 @@
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -1651,7 +1671,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__,
|
||||
"get_ciphers($self, /)\n"
|
||||
"--\n"
|
||||
@@ -476,44 +449,6 @@ _ssl__SSLContext_get_ciphers(PySSLContex
|
||||
@@ -476,44 +449,6 @@
|
||||
return _ssl__SSLContext_get_ciphers_impl(self);
|
||||
}
|
||||
|
||||
@@ -1696,7 +1716,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLContext__set_alpn_protocols__doc__,
|
||||
"_set_alpn_protocols($self, protos, /)\n"
|
||||
"--\n"
|
||||
@@ -844,8 +779,6 @@ _ssl__SSLContext_set_default_verify_path
|
||||
@@ -844,8 +779,6 @@
|
||||
return _ssl__SSLContext_set_default_verify_paths_impl(self);
|
||||
}
|
||||
|
||||
@@ -1705,7 +1725,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_curve__doc__,
|
||||
"set_ecdh_curve($self, name, /)\n"
|
||||
"--\n"
|
||||
@@ -854,8 +787,6 @@ PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_c
|
||||
@@ -854,8 +787,6 @@
|
||||
#define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF \
|
||||
{"set_ecdh_curve", (PyCFunction)_ssl__SSLContext_set_ecdh_curve, METH_O, _ssl__SSLContext_set_ecdh_curve__doc__},
|
||||
|
||||
@@ -1714,7 +1734,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__,
|
||||
"cert_store_stats($self, /)\n"
|
||||
"--\n"
|
||||
@@ -1455,22 +1386,6 @@ exit:
|
||||
@@ -1455,22 +1386,6 @@
|
||||
|
||||
#endif /* defined(_MSC_VER) */
|
||||
|
||||
@@ -1737,9 +1757,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
#ifndef _SSL_RAND_EGD_METHODDEF
|
||||
#define _SSL_RAND_EGD_METHODDEF
|
||||
#endif /* !defined(_SSL_RAND_EGD_METHODDEF) */
|
||||
--- a/Tools/ssl/multissltests.py
|
||||
+++ b/Tools/ssl/multissltests.py
|
||||
@@ -43,8 +43,6 @@ import tarfile
|
||||
Index: Python-3.9.25/Tools/ssl/multissltests.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Tools/ssl/multissltests.py 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/Tools/ssl/multissltests.py 2025-12-19 23:25:05.634262920 +0100
|
||||
@@ -44,8 +44,6 @@
|
||||
log = logging.getLogger("multissl")
|
||||
|
||||
OPENSSL_OLD_VERSIONS = [
|
||||
@@ -1748,7 +1770,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
]
|
||||
|
||||
OPENSSL_RECENT_VERSIONS = [
|
||||
@@ -53,11 +51,9 @@ OPENSSL_RECENT_VERSIONS = [
|
||||
@@ -54,11 +52,9 @@
|
||||
]
|
||||
|
||||
LIBRESSL_OLD_VERSIONS = [
|
||||
@@ -1760,9 +1782,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
]
|
||||
|
||||
# store files in ../multissl
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -88,6 +88,13 @@ fi
|
||||
Index: Python-3.9.25/configure
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/configure 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/configure 2025-12-19 23:25:05.636084606 +0100
|
||||
@@ -88,6 +88,13 @@
|
||||
# splitting by setting IFS to empty value.)
|
||||
IFS=" "" $as_nl"
|
||||
|
||||
@@ -1776,7 +1800,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
# Find who we are. Look in the path if we contain no directory separator.
|
||||
as_myself=
|
||||
case $0 in #((
|
||||
@@ -17997,7 +18004,6 @@ as_fn_error ()
|
||||
@@ -17997,7 +18004,6 @@
|
||||
as_fn_exit $as_status
|
||||
} # as_fn_error
|
||||
|
||||
@@ -1784,14 +1808,16 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
# as_fn_set_status STATUS
|
||||
# -----------------------
|
||||
# Set $? to STATUS, without forking.
|
||||
@@ -19043,4 +19049,3 @@ if test "$Py_OPT" = 'false' -a "$Py_DEBU
|
||||
@@ -19043,4 +19049,3 @@
|
||||
echo "" >&6
|
||||
echo "" >&6
|
||||
fi
|
||||
-
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -5756,42 +5756,6 @@ ac_includes_default="$save_includes_defa
|
||||
Index: Python-3.9.25/configure.ac
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/configure.ac 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/configure.ac 2025-12-19 23:25:05.637165408 +0100
|
||||
@@ -5756,42 +5756,6 @@
|
||||
# Check for usable OpenSSL
|
||||
AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no])
|
||||
|
||||
@@ -1834,8 +1860,10 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
# ssl module default cipher suite string
|
||||
AH_TEMPLATE(PY_SSL_DEFAULT_CIPHERS,
|
||||
[Default cipher suites list for ssl module.
|
||||
--- a/pyconfig.h.in
|
||||
+++ b/pyconfig.h.in
|
||||
Index: Python-3.9.25/pyconfig.h.in
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/pyconfig.h.in 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/pyconfig.h.in 2025-12-19 23:25:05.638051492 +0100
|
||||
@@ -1351,9 +1351,6 @@
|
||||
/* Define to 1 if you have the `writev' function. */
|
||||
#undef HAVE_WRITEV
|
||||
@@ -1846,9 +1874,11 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
/* Define if the zlib library has inflateCopy */
|
||||
#undef HAVE_ZLIB_COPY
|
||||
|
||||
--- a/setup.py
|
||||
+++ b/setup.py
|
||||
@@ -539,10 +539,7 @@ class PyBuildExt(build_ext):
|
||||
Index: Python-3.9.25/setup.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/setup.py 2025-12-19 23:25:00.457106547 +0100
|
||||
+++ Python-3.9.25/setup.py 2025-12-19 23:25:05.638647942 +0100
|
||||
@@ -539,10 +539,7 @@
|
||||
for l in (self.missing, self.failed, self.failed_on_import)):
|
||||
print()
|
||||
print("Could not build the ssl module!")
|
||||
@@ -1860,7 +1890,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
print()
|
||||
|
||||
if os.environ.get("PYTHONSTRICTEXTENSIONBUILD") and (self.failed or self.failed_on_import):
|
||||
@@ -2346,13 +2343,13 @@ class PyBuildExt(build_ext):
|
||||
@@ -2346,13 +2343,13 @@
|
||||
self.missing.extend(['_ssl', '_hashlib'])
|
||||
return None, None
|
||||
|
||||
@@ -1880,7 +1910,7 @@ Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer
|
||||
|
||||
if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"):
|
||||
self.add(Extension(
|
||||
@@ -2367,8 +2364,6 @@ class PyBuildExt(build_ext):
|
||||
@@ -2367,8 +2364,6 @@
|
||||
'_ssl_data_111.h',
|
||||
'_ssl_data_300.h',
|
||||
]))
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
From 15eec9d5076b780463c3dc73afcef688651c5295 Mon Sep 17 00:00:00 2001
|
||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||
Date: Sat, 17 Aug 2024 16:30:52 +0300
|
||||
Subject: [PATCH] gh-123067: Fix quadratic complexity in parsing "-quoted
|
||||
cookie values with backslashes (GH-123075)
|
||||
|
||||
This fixes CVE-2024-7592.
|
||||
(cherry picked from commit 44e458357fca05ca0ae2658d62c8c595b048b5ef)
|
||||
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
---
|
||||
Lib/http/cookies.py | 34 ++------
|
||||
Lib/test/test_http_cookies.py | 38 ++++++++++
|
||||
Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst | 1
|
||||
3 files changed, 47 insertions(+), 26 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst
|
||||
|
||||
--- a/Lib/http/cookies.py
|
||||
+++ b/Lib/http/cookies.py
|
||||
@@ -184,8 +184,13 @@ def _quote(str):
|
||||
return '"' + str.translate(_Translator) + '"'
|
||||
|
||||
|
||||
-_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
|
||||
-_QuotePatt = re.compile(r"[\\].")
|
||||
+_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub
|
||||
+
|
||||
+def _unquote_replace(m):
|
||||
+ if m[1]:
|
||||
+ return chr(int(m[1], 8))
|
||||
+ else:
|
||||
+ return m[2]
|
||||
|
||||
def _unquote(str):
|
||||
# If there aren't any doublequotes,
|
||||
@@ -205,30 +210,7 @@ def _unquote(str):
|
||||
# \012 --> \n
|
||||
# \" --> "
|
||||
#
|
||||
- i = 0
|
||||
- n = len(str)
|
||||
- res = []
|
||||
- while 0 <= i < n:
|
||||
- o_match = _OctalPatt.search(str, i)
|
||||
- q_match = _QuotePatt.search(str, i)
|
||||
- if not o_match and not q_match: # Neither matched
|
||||
- res.append(str[i:])
|
||||
- break
|
||||
- # else:
|
||||
- j = k = -1
|
||||
- if o_match:
|
||||
- j = o_match.start(0)
|
||||
- if q_match:
|
||||
- k = q_match.start(0)
|
||||
- if q_match and (not o_match or k < j): # QuotePatt matched
|
||||
- res.append(str[i:k])
|
||||
- res.append(str[k+1])
|
||||
- i = k + 2
|
||||
- else: # OctalPatt matched
|
||||
- res.append(str[i:j])
|
||||
- res.append(chr(int(str[j+1:j+4], 8)))
|
||||
- i = j + 4
|
||||
- return _nulljoin(res)
|
||||
+ return _unquote_sub(_unquote_replace, str)
|
||||
|
||||
# The _getdate() routine is used to set the expiration time in the cookie's HTTP
|
||||
# header. By default, _getdate() returns the current time in the appropriate
|
||||
--- a/Lib/test/test_http_cookies.py
|
||||
+++ b/Lib/test/test_http_cookies.py
|
||||
@@ -5,6 +5,7 @@ from test.support import run_unittest, r
|
||||
import unittest
|
||||
from http import cookies
|
||||
import pickle
|
||||
+from test import support
|
||||
|
||||
|
||||
class CookieTests(unittest.TestCase):
|
||||
@@ -58,6 +59,43 @@ class CookieTests(unittest.TestCase):
|
||||
for k, v in sorted(case['dict'].items()):
|
||||
self.assertEqual(C[k].value, v)
|
||||
|
||||
+ def test_unquote(self):
|
||||
+ cases = [
|
||||
+ (r'a="b=\""', 'b="'),
|
||||
+ (r'a="b=\\"', 'b=\\'),
|
||||
+ (r'a="b=\="', 'b=='),
|
||||
+ (r'a="b=\n"', 'b=n'),
|
||||
+ (r'a="b=\042"', 'b="'),
|
||||
+ (r'a="b=\134"', 'b=\\'),
|
||||
+ (r'a="b=\377"', 'b=\xff'),
|
||||
+ (r'a="b=\400"', 'b=400'),
|
||||
+ (r'a="b=\42"', 'b=42'),
|
||||
+ (r'a="b=\\042"', 'b=\\042'),
|
||||
+ (r'a="b=\\134"', 'b=\\134'),
|
||||
+ (r'a="b=\\\""', 'b=\\"'),
|
||||
+ (r'a="b=\\\042"', 'b=\\"'),
|
||||
+ (r'a="b=\134\""', 'b=\\"'),
|
||||
+ (r'a="b=\134\042"', 'b=\\"'),
|
||||
+ ]
|
||||
+ for encoded, decoded in cases:
|
||||
+ with self.subTest(encoded):
|
||||
+ C = cookies.SimpleCookie()
|
||||
+ C.load(encoded)
|
||||
+ self.assertEqual(C['a'].value, decoded)
|
||||
+
|
||||
+ @support.requires_resource('cpu')
|
||||
+ def test_unquote_large(self):
|
||||
+ n = 10**6
|
||||
+ for encoded in r'\\', r'\134':
|
||||
+ with self.subTest(encoded):
|
||||
+ data = 'a="b=' + encoded*n + ';"'
|
||||
+ C = cookies.SimpleCookie()
|
||||
+ C.load(data)
|
||||
+ value = C['a'].value
|
||||
+ self.assertEqual(value[:3], 'b=\\')
|
||||
+ self.assertEqual(value[-2:], '\\;')
|
||||
+ self.assertEqual(len(value), n + 3)
|
||||
+
|
||||
def test_load(self):
|
||||
C = cookies.SimpleCookie()
|
||||
C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme')
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst
|
||||
@@ -0,0 +1 @@
|
||||
+Fix quadratic complexity in parsing ``"``-quoted cookie values with backslashes by :mod:`http.cookies`.
|
||||
@@ -1,339 +0,0 @@
|
||||
From f9ddc53ea850fb02d640a9b3263756d43fb6d868 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Viktorin <encukou@gmail.com>
|
||||
Date: Wed, 31 Jul 2024 00:19:48 +0200
|
||||
Subject: [PATCH] [3.9] gh-121650: Encode newlines in headers, and verify
|
||||
headers are sound (GH-122233)
|
||||
|
||||
GH-GH- Encode header parts that contain newlines
|
||||
|
||||
Per RFC 2047:
|
||||
|
||||
> [...] these encoding schemes allow the
|
||||
> encoding of arbitrary octet values, mail readers that implement this
|
||||
> decoding should also ensure that display of the decoded data on the
|
||||
> recipient's terminal will not cause unwanted side-effects
|
||||
|
||||
It seems that the "quoted-word" scheme is a valid way to include
|
||||
a newline character in a header value, just like we already allow
|
||||
undecodable bytes or control characters.
|
||||
They do need to be properly quoted when serialized to text, though.
|
||||
|
||||
GH-GH- Verify that email headers are well-formed
|
||||
|
||||
This should fail for custom fold() implementations that aren't careful
|
||||
about newlines.
|
||||
|
||||
(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384)
|
||||
|
||||
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||
Co-authored-by: Bas Bloemsaat <bas@bloemsaat.org>
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
---
|
||||
Doc/library/email.errors.rst | 6
|
||||
Doc/library/email.policy.rst | 18 ++
|
||||
Doc/whatsnew/3.9.rst | 12 +
|
||||
Lib/email/_header_value_parser.py | 12 +
|
||||
Lib/email/_policybase.py | 8 +
|
||||
Lib/email/errors.py | 4
|
||||
Lib/email/generator.py | 13 +-
|
||||
Lib/test/test_email/test_generator.py | 62 ++++++++++
|
||||
Lib/test/test_email/test_policy.py | 26 ++++
|
||||
Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5
|
||||
10 files changed, 162 insertions(+), 4 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst
|
||||
|
||||
--- a/Doc/library/email.errors.rst
|
||||
+++ b/Doc/library/email.errors.rst
|
||||
@@ -59,6 +59,12 @@ The following exception classes are defi
|
||||
:class:`~email.mime.image.MIMEImage`).
|
||||
|
||||
|
||||
+.. exception:: HeaderWriteError()
|
||||
+
|
||||
+ Raised when an error occurs when the :mod:`~email.generator` outputs
|
||||
+ headers.
|
||||
+
|
||||
+
|
||||
Here is the list of the defects that the :class:`~email.parser.FeedParser`
|
||||
can find while parsing messages. Note that the defects are added to the message
|
||||
where the problem was found, so for example, if a message nested inside a
|
||||
--- a/Doc/library/email.policy.rst
|
||||
+++ b/Doc/library/email.policy.rst
|
||||
@@ -229,6 +229,24 @@ added matters. To illustrate::
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
+
|
||||
+ .. attribute:: verify_generated_headers
|
||||
+
|
||||
+ If ``True`` (the default), the generator will raise
|
||||
+ :exc:`~email.errors.HeaderWriteError` instead of writing a header
|
||||
+ that is improperly folded or delimited, such that it would
|
||||
+ be parsed as multiple headers or joined with adjacent data.
|
||||
+ Such headers can be generated by custom header classes or bugs
|
||||
+ in the ``email`` module.
|
||||
+
|
||||
+ As it's a security feature, this defaults to ``True`` even in the
|
||||
+ :class:`~email.policy.Compat32` policy.
|
||||
+ For backwards compatible, but unsafe, behavior, it must be set to
|
||||
+ ``False`` explicitly.
|
||||
+
|
||||
+ .. versionadded:: 3.11.10
|
||||
+
|
||||
+
|
||||
The following :class:`Policy` method is intended to be called by code using
|
||||
the email library to create policy instances with custom settings:
|
||||
|
||||
--- a/Doc/whatsnew/3.9.rst
|
||||
+++ b/Doc/whatsnew/3.9.rst
|
||||
@@ -1625,3 +1625,15 @@ ipaddress
|
||||
|
||||
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
|
||||
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.
|
||||
+
|
||||
+email
|
||||
+-----
|
||||
+
|
||||
+* Headers with embedded newlines are now quoted on output.
|
||||
+
|
||||
+ The :mod:`~email.generator` will now refuse to serialize (write) headers
|
||||
+ that are improperly folded or delimited, such that they would be parsed as
|
||||
+ multiple headers or joined with adjacent data.
|
||||
+ If you need to turn this safety feature off,
|
||||
+ set :attr:`~email.policy.Policy.verify_generated_headers`.
|
||||
+ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.)
|
||||
--- a/Lib/email/_header_value_parser.py
|
||||
+++ b/Lib/email/_header_value_parser.py
|
||||
@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP
|
||||
ASPECIALS = TSPECIALS | set("*'%")
|
||||
ATTRIBUTE_ENDS = ASPECIALS | WSP
|
||||
EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%')
|
||||
+NLSET = {'\n', '\r'}
|
||||
+SPECIALSNL = SPECIALS | NLSET
|
||||
|
||||
def quote_string(value):
|
||||
return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"'
|
||||
@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po
|
||||
wrap_as_ew_blocked -= 1
|
||||
continue
|
||||
tstr = str(part)
|
||||
- if part.token_type == 'ptext' and set(tstr) & SPECIALS:
|
||||
- # Encode if tstr contains special characters.
|
||||
- want_encoding = True
|
||||
+ if not want_encoding:
|
||||
+ if part.token_type == 'ptext':
|
||||
+ # Encode if tstr contains special characters.
|
||||
+ want_encoding = not SPECIALSNL.isdisjoint(tstr)
|
||||
+ else:
|
||||
+ # Encode if tstr contains newlines.
|
||||
+ want_encoding = not NLSET.isdisjoint(tstr)
|
||||
try:
|
||||
tstr.encode(encoding)
|
||||
charset = encoding
|
||||
--- a/Lib/email/_policybase.py
|
||||
+++ b/Lib/email/_policybase.py
|
||||
@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc.
|
||||
message_factory -- the class to use to create new message objects.
|
||||
If the value is None, the default is Message.
|
||||
|
||||
+ verify_generated_headers
|
||||
+ -- if true, the generator verifies that each header
|
||||
+ they are properly folded, so that a parser won't
|
||||
+ treat it as multiple headers, start-of-body, or
|
||||
+ part of another header.
|
||||
+ This is a check against custom Header & fold()
|
||||
+ implementations.
|
||||
"""
|
||||
|
||||
raise_on_defect = False
|
||||
@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc.
|
||||
max_line_length = 78
|
||||
mangle_from_ = False
|
||||
message_factory = None
|
||||
+ verify_generated_headers = True
|
||||
|
||||
def handle_defect(self, obj, defect):
|
||||
"""Based on policy, either raise defect or call register_defect.
|
||||
--- a/Lib/email/errors.py
|
||||
+++ b/Lib/email/errors.py
|
||||
@@ -29,6 +29,10 @@ class CharsetError(MessageError):
|
||||
"""An illegal charset was given."""
|
||||
|
||||
|
||||
+class HeaderWriteError(MessageError):
|
||||
+ """Error while writing headers."""
|
||||
+
|
||||
+
|
||||
# These are parsing defects which the parser was able to work around.
|
||||
class MessageDefect(ValueError):
|
||||
"""Base class for a message defect."""
|
||||
--- a/Lib/email/generator.py
|
||||
+++ b/Lib/email/generator.py
|
||||
@@ -14,12 +14,14 @@ import random
|
||||
from copy import deepcopy
|
||||
from io import StringIO, BytesIO
|
||||
from email.utils import _has_surrogates
|
||||
+from email.errors import HeaderWriteError
|
||||
|
||||
UNDERSCORE = '_'
|
||||
NL = '\n' # XXX: no longer used by the code below.
|
||||
|
||||
NLCRE = re.compile(r'\r\n|\r|\n')
|
||||
fcre = re.compile(r'^From ', re.MULTILINE)
|
||||
+NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
|
||||
|
||||
|
||||
|
||||
@@ -223,7 +225,16 @@ class Generator:
|
||||
|
||||
def _write_headers(self, msg):
|
||||
for h, v in msg.raw_items():
|
||||
- self.write(self.policy.fold(h, v))
|
||||
+ folded = self.policy.fold(h, v)
|
||||
+ if self.policy.verify_generated_headers:
|
||||
+ linesep = self.policy.linesep
|
||||
+ if not folded.endswith(self.policy.linesep):
|
||||
+ raise HeaderWriteError(
|
||||
+ f'folded header does not end with {linesep!r}: {folded!r}')
|
||||
+ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)):
|
||||
+ raise HeaderWriteError(
|
||||
+ f'folded header contains newline: {folded!r}')
|
||||
+ self.write(folded)
|
||||
# A blank line always separates headers from body
|
||||
self.write(self._NL)
|
||||
|
||||
--- a/Lib/test/test_email/test_generator.py
|
||||
+++ b/Lib/test/test_email/test_generator.py
|
||||
@@ -6,6 +6,7 @@ from email.message import EmailMessage
|
||||
from email.generator import Generator, BytesGenerator
|
||||
from email.headerregistry import Address
|
||||
from email import policy
|
||||
+import email.errors
|
||||
from test.test_email import TestEmailBase, parameterize
|
||||
|
||||
|
||||
@@ -216,6 +217,44 @@ class TestGeneratorBase:
|
||||
g.flatten(msg)
|
||||
self.assertEqual(s.getvalue(), self.typ(expected))
|
||||
|
||||
+ def test_keep_encoded_newlines(self):
|
||||
+ msg = self.msgmaker(self.typ(textwrap.dedent("""\
|
||||
+ To: nobody
|
||||
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||
+
|
||||
+ None
|
||||
+ """)))
|
||||
+ expected = textwrap.dedent("""\
|
||||
+ To: nobody
|
||||
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||
+
|
||||
+ None
|
||||
+ """)
|
||||
+ s = self.ioclass()
|
||||
+ g = self.genclass(s, policy=self.policy.clone(max_line_length=80))
|
||||
+ g.flatten(msg)
|
||||
+ self.assertEqual(s.getvalue(), self.typ(expected))
|
||||
+
|
||||
+ def test_keep_long_encoded_newlines(self):
|
||||
+ msg = self.msgmaker(self.typ(textwrap.dedent("""\
|
||||
+ To: nobody
|
||||
+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com
|
||||
+
|
||||
+ None
|
||||
+ """)))
|
||||
+ expected = textwrap.dedent("""\
|
||||
+ To: nobody
|
||||
+ Subject: Bad subject
|
||||
+ =?utf-8?q?=0A?=Bcc:
|
||||
+ injection@example.com
|
||||
+
|
||||
+ None
|
||||
+ """)
|
||||
+ s = self.ioclass()
|
||||
+ g = self.genclass(s, policy=self.policy.clone(max_line_length=30))
|
||||
+ g.flatten(msg)
|
||||
+ self.assertEqual(s.getvalue(), self.typ(expected))
|
||||
+
|
||||
|
||||
class TestGenerator(TestGeneratorBase, TestEmailBase):
|
||||
|
||||
@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T
|
||||
ioclass = io.StringIO
|
||||
typ = str
|
||||
|
||||
+ def test_verify_generated_headers(self):
|
||||
+ """gh-121650: by default the generator prevents header injection"""
|
||||
+ class LiteralHeader(str):
|
||||
+ name = 'Header'
|
||||
+ def fold(self, **kwargs):
|
||||
+ return self
|
||||
+
|
||||
+ for text in (
|
||||
+ 'Value\r\nBad Injection\r\n',
|
||||
+ 'NoNewLine'
|
||||
+ ):
|
||||
+ with self.subTest(text=text):
|
||||
+ message = message_from_string(
|
||||
+ "Header: Value\r\n\r\nBody",
|
||||
+ policy=self.policy,
|
||||
+ )
|
||||
+
|
||||
+ del message['Header']
|
||||
+ message['Header'] = LiteralHeader(text)
|
||||
+
|
||||
+ with self.assertRaises(email.errors.HeaderWriteError):
|
||||
+ message.as_string()
|
||||
+
|
||||
|
||||
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):
|
||||
|
||||
--- a/Lib/test/test_email/test_policy.py
|
||||
+++ b/Lib/test/test_email/test_policy.py
|
||||
@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase):
|
||||
'raise_on_defect': False,
|
||||
'mangle_from_': True,
|
||||
'message_factory': None,
|
||||
+ 'verify_generated_headers': True,
|
||||
}
|
||||
# These default values are the ones set on email.policy.default.
|
||||
# If any of these defaults change, the docs must be updated.
|
||||
@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase):
|
||||
with self.assertRaises(email.errors.HeaderParseError):
|
||||
policy.fold("Subject", subject)
|
||||
|
||||
+ def test_verify_generated_headers(self):
|
||||
+ """Turning protection off allows header injection"""
|
||||
+ policy = email.policy.default.clone(verify_generated_headers=False)
|
||||
+ for text in (
|
||||
+ 'Header: Value\r\nBad: Injection\r\n',
|
||||
+ 'Header: NoNewLine'
|
||||
+ ):
|
||||
+ with self.subTest(text=text):
|
||||
+ message = email.message_from_string(
|
||||
+ "Header: Value\r\n\r\nBody",
|
||||
+ policy=policy,
|
||||
+ )
|
||||
+ class LiteralHeader(str):
|
||||
+ name = 'Header'
|
||||
+ def fold(self, **kwargs):
|
||||
+ return self
|
||||
+
|
||||
+ del message['Header']
|
||||
+ message['Header'] = LiteralHeader(text)
|
||||
+
|
||||
+ self.assertEqual(
|
||||
+ message.as_string(),
|
||||
+ f"{text}\nBody",
|
||||
+ )
|
||||
+
|
||||
# XXX: Need subclassing tests.
|
||||
# For adding subclassed objects, make sure the usual rules apply (subclass
|
||||
# wins), but that the order still works (right overrides left).
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst
|
||||
@@ -0,0 +1,5 @@
|
||||
+:mod:`email` headers with embedded newlines are now quoted on output. The
|
||||
+:mod:`~email.generator` will now refuse to serialize (write) headers that
|
||||
+are unsafely folded or delimited; see
|
||||
+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas
|
||||
+Bloemsaat and Petr Viktorin in :gh:`121650`.)
|
||||
@@ -1,136 +0,0 @@
|
||||
From dcb320a0c85713c5dfe89a83d6eb295ad1511be8 Mon Sep 17 00:00:00 2001
|
||||
From: "Jason R. Coombs" <jaraco@jaraco.com>
|
||||
Date: Tue, 27 Aug 2024 17:10:30 -0400
|
||||
Subject: [PATCH] [3.8] [3.9] [3.11] gh-123270: Replaced SanitizedNames with a
|
||||
more surgical fix. (GH-123354)
|
||||
|
||||
Applies changes from zipp 3.20.1 and jaraco/zippGH-124
|
||||
(cherry picked from commit 2231286d78d328c2f575e0b05b16fe447d1656d6)
|
||||
(cherry picked from commit 17b77bb41409259bad1cd6c74761c18b6ab1e860)
|
||||
(cherry picked from commit 66d3383)
|
||||
|
||||
Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
|
||||
---
|
||||
Lib/test/test_zipfile.py | 75 ++++++++++
|
||||
Lib/zipfile.py | 9 -
|
||||
Misc/NEWS.d/next/Library/2024-08-26-13-45-20.gh-issue-123270.gXHvNJ.rst | 3
|
||||
3 files changed, 85 insertions(+), 2 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2024-08-26-13-45-20.gh-issue-123270.gXHvNJ.rst
|
||||
|
||||
--- a/Lib/test/test_zipfile.py
|
||||
+++ b/Lib/test/test_zipfile.py
|
||||
@@ -3054,6 +3054,81 @@ class TestPath(unittest.TestCase):
|
||||
data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
|
||||
zipfile.CompleteDirs._implied_dirs(data)
|
||||
|
||||
+ def test_malformed_paths(self):
|
||||
+ """
|
||||
+ Path should handle malformed paths gracefully.
|
||||
+
|
||||
+ Paths with leading slashes are not visible.
|
||||
+
|
||||
+ Paths with dots are treated like regular files.
|
||||
+ """
|
||||
+ data = io.BytesIO()
|
||||
+ zf = zipfile.ZipFile(data, "w")
|
||||
+ zf.writestr("../parent.txt", b"content")
|
||||
+ zf.filename = ''
|
||||
+ root = zipfile.Path(zf)
|
||||
+ assert list(map(str, root.iterdir())) == ['../']
|
||||
+ assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content'
|
||||
+
|
||||
+ def test_unsupported_names(self):
|
||||
+ """
|
||||
+ Path segments with special characters are readable.
|
||||
+
|
||||
+ On some platforms or file systems, characters like
|
||||
+ ``:`` and ``?`` are not allowed, but they are valid
|
||||
+ in the zip file.
|
||||
+ """
|
||||
+ data = io.BytesIO()
|
||||
+ zf = zipfile.ZipFile(data, "w")
|
||||
+ zf.writestr("path?", b"content")
|
||||
+ zf.writestr("V: NMS.flac", b"fLaC...")
|
||||
+ zf.filename = ''
|
||||
+ root = zipfile.Path(zf)
|
||||
+ contents = root.iterdir()
|
||||
+ assert next(contents).name == 'path?'
|
||||
+ assert next(contents).name == 'V: NMS.flac'
|
||||
+ assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..."
|
||||
+
|
||||
+ def test_backslash_not_separator(self):
|
||||
+ """
|
||||
+ In a zip file, backslashes are not separators.
|
||||
+ """
|
||||
+ data = io.BytesIO()
|
||||
+ zf = zipfile.ZipFile(data, "w")
|
||||
+ zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content")
|
||||
+ zf.filename = ''
|
||||
+ root = zipfile.Path(zf)
|
||||
+ (first,) = root.iterdir()
|
||||
+ assert not first.is_dir()
|
||||
+ assert first.name == 'foo\\bar'
|
||||
+
|
||||
+
|
||||
+class DirtyZipInfo(zipfile.ZipInfo):
|
||||
+ """
|
||||
+ Bypass name sanitization.
|
||||
+ """
|
||||
+
|
||||
+ def __init__(self, filename, *args, **kwargs):
|
||||
+ super().__init__(filename, *args, **kwargs)
|
||||
+ self.filename = filename
|
||||
+
|
||||
+ @classmethod
|
||||
+ def for_name(cls, name, archive):
|
||||
+ """
|
||||
+ Construct the same way that ZipFile.writestr does.
|
||||
+
|
||||
+ TODO: extract this functionality and re-use
|
||||
+ """
|
||||
+ self = cls(filename=name, date_time=time.localtime(time.time())[:6])
|
||||
+ self.compress_type = archive.compression
|
||||
+ self.compress_level = archive.compresslevel
|
||||
+ if self.filename.endswith('/'): # pragma: no cover
|
||||
+ self.external_attr = 0o40775 << 16 # drwxrwxr-x
|
||||
+ self.external_attr |= 0x10 # MS-DOS directory flag
|
||||
+ else:
|
||||
+ self.external_attr = 0o600 << 16 # ?rw-------
|
||||
+ return self
|
||||
+
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
--- a/Lib/zipfile.py
|
||||
+++ b/Lib/zipfile.py
|
||||
@@ -2146,7 +2146,7 @@ def _parents(path):
|
||||
def _ancestry(path):
|
||||
"""
|
||||
Given a path with elements separated by
|
||||
- posixpath.sep, generate all elements of that path
|
||||
+ posixpath.sep, generate all elements of that path.
|
||||
|
||||
>>> list(_ancestry('b/d'))
|
||||
['b/d', 'b']
|
||||
@@ -2158,9 +2158,14 @@ def _ancestry(path):
|
||||
['b']
|
||||
>>> list(_ancestry(''))
|
||||
[]
|
||||
+
|
||||
+ Multiple separators are treated like a single.
|
||||
+
|
||||
+ >>> list(_ancestry('//b//d///f//'))
|
||||
+ ['//b//d///f', '//b//d', '//b']
|
||||
"""
|
||||
path = path.rstrip(posixpath.sep)
|
||||
- while path and path != posixpath.sep:
|
||||
+ while path.rstrip(posixpath.sep):
|
||||
yield path
|
||||
path, tail = posixpath.split(path)
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Library/2024-08-26-13-45-20.gh-issue-123270.gXHvNJ.rst
|
||||
@@ -0,0 +1,3 @@
|
||||
+Applied a more surgical fix for malformed payloads in :class:`zipfile.Path`
|
||||
+causing infinite loops (gh-122905) without breaking contents using
|
||||
+legitimate characters.
|
||||
93
CVE-2025-12084-minidom-quad-search.patch
Normal file
93
CVE-2025-12084-minidom-quad-search.patch
Normal file
@@ -0,0 +1,93 @@
|
||||
From f4eb9ab014545b521fb261b80adfa6d138e7e092 Mon Sep 17 00:00:00 2001
|
||||
From: Seth Michael Larson <seth@python.org>
|
||||
Date: Wed, 3 Dec 2025 01:16:37 -0600
|
||||
Subject: [PATCH] gh-142145: Remove quadratic behavior in node ID cache
|
||||
clearing (GH-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 08d8e18ad81cd45bc4a27d6da478b51ea49486e4)
|
||||
|
||||
Co-authored-by: Seth Michael Larson <seth@python.org>
|
||||
Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com>
|
||||
---
|
||||
Lib/test/test_minidom.py | 18 ++++++++++
|
||||
Lib/xml/dom/minidom.py | 9 -----
|
||||
Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst | 1
|
||||
3 files changed, 20 insertions(+), 8 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
|
||||
|
||||
Index: Python-3.9.25/Lib/test/test_minidom.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_minidom.py 2025-12-19 23:24:47.176384491 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_minidom.py 2025-12-19 23:27:27.634483015 +0100
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import copy
|
||||
import pickle
|
||||
+import time
|
||||
import io
|
||||
from test import support
|
||||
import unittest
|
||||
@@ -162,6 +163,23 @@
|
||||
self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
|
||||
dom.unlink()
|
||||
|
||||
+ def testAppendChildNoQuadraticComplexity(self):
|
||||
+ impl = getDOMImplementation()
|
||||
+
|
||||
+ newdoc = impl.createDocument(None, "some_tag", None)
|
||||
+ top_element = newdoc.documentElement
|
||||
+ children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
|
||||
+ element = top_element
|
||||
+
|
||||
+ start = time.time()
|
||||
+ for child in children:
|
||||
+ element.appendChild(child)
|
||||
+ element = child
|
||||
+ end = time.time()
|
||||
+
|
||||
+ # This example used to take at least 30 seconds.
|
||||
+ self.assertLess(end - start, 1)
|
||||
+
|
||||
def testAppendChildFragment(self):
|
||||
dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
|
||||
dom.documentElement.appendChild(frag)
|
||||
Index: Python-3.9.25/Lib/xml/dom/minidom.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/xml/dom/minidom.py 2025-10-31 19:40:52.000000000 +0100
|
||||
+++ Python-3.9.25/Lib/xml/dom/minidom.py 2025-12-19 23:27:27.635128214 +0100
|
||||
@@ -292,13 +292,6 @@
|
||||
childNodes.append(node)
|
||||
node.parentNode = self
|
||||
|
||||
-def _in_document(node):
|
||||
- # return True iff node is part of a document tree
|
||||
- while node is not None:
|
||||
- if node.nodeType == Node.DOCUMENT_NODE:
|
||||
- return True
|
||||
- node = node.parentNode
|
||||
- return False
|
||||
|
||||
def _write_data(writer, data):
|
||||
"Writes datachars to writer."
|
||||
@@ -1537,7 +1530,7 @@
|
||||
if node.nodeType == Node.DOCUMENT_NODE:
|
||||
node._id_cache.clear()
|
||||
node._id_search_stack = None
|
||||
- elif _in_document(node):
|
||||
+ elif node.ownerDocument:
|
||||
node.ownerDocument._id_cache.clear()
|
||||
node.ownerDocument._id_search_stack= None
|
||||
|
||||
Index: Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst 2025-12-19 23:27:27.635403950 +0100
|
||||
@@ -0,0 +1 @@
|
||||
+Remove quadratic behavior in ``xml.minidom`` node ID cache clearing.
|
||||
158
CVE-2025-13836-http-resp-cont-len.patch
Normal file
158
CVE-2025-13836-http-resp-cont-len.patch
Normal file
@@ -0,0 +1,158 @@
|
||||
From b3a7998115e195c40e00cfa662bcaa899d937c05 Mon Sep 17 00:00:00 2001
|
||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||
Date: Mon, 1 Dec 2025 17:26:07 +0200
|
||||
Subject: [PATCH] gh-119451: Fix a potential denial of service in http.client
|
||||
(GH-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 5a4c4a033a4a54481be6870aa1896fad732555b5)
|
||||
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
---
|
||||
Lib/http/client.py | 32 +++-
|
||||
Lib/test/test_httplib.py | 66 ++++++++++
|
||||
Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst | 5
|
||||
3 files changed, 95 insertions(+), 8 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
|
||||
|
||||
Index: Python-3.9.25/Lib/http/client.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/http/client.py 2025-10-31 19:40:52.000000000 +0100
|
||||
+++ Python-3.9.25/Lib/http/client.py 2025-12-19 23:26:40.448421016 +0100
|
||||
@@ -113,6 +113,11 @@
|
||||
_MAXLINE = 65536
|
||||
_MAXHEADERS = 100
|
||||
|
||||
+# Data larger than this will be read in chunks, to prevent extreme
|
||||
+# overallocation.
|
||||
+_MIN_READ_BUF_SIZE = 1 << 20
|
||||
+
|
||||
+
|
||||
# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2)
|
||||
#
|
||||
# VCHAR = %x21-7E
|
||||
@@ -621,14 +626,25 @@
|
||||
reading. If the bytes are truly not available (due to EOF), then the
|
||||
IncompleteRead exception can be used to detect the problem.
|
||||
"""
|
||||
- s = []
|
||||
- while amt > 0:
|
||||
- chunk = self.fp.read(min(amt, MAXAMOUNT))
|
||||
- if not chunk:
|
||||
- raise IncompleteRead(b''.join(s), amt)
|
||||
- s.append(chunk)
|
||||
- amt -= len(chunk)
|
||||
- return b"".join(s)
|
||||
+ cursize = min(amt, _MIN_READ_BUF_SIZE)
|
||||
+ data = self.fp.read(cursize)
|
||||
+ if len(data) >= amt:
|
||||
+ return data
|
||||
+ if len(data) < cursize:
|
||||
+ raise IncompleteRead(data, amt - len(data))
|
||||
+
|
||||
+ data = io.BytesIO(data)
|
||||
+ data.seek(0, 2)
|
||||
+ while True:
|
||||
+ # This is a geometric increase in read size (never more than
|
||||
+ # doubling out the current length of data per loop iteration).
|
||||
+ delta = min(cursize, amt - cursize)
|
||||
+ data.write(self.fp.read(delta))
|
||||
+ if data.tell() >= amt:
|
||||
+ return data.getvalue()
|
||||
+ cursize += delta
|
||||
+ if data.tell() < cursize:
|
||||
+ raise IncompleteRead(data.getvalue(), amt - data.tell())
|
||||
|
||||
def _safe_readinto(self, b):
|
||||
"""Same as _safe_read, but for reading into a buffer."""
|
||||
Index: Python-3.9.25/Lib/test/test_httplib.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_httplib.py 2025-10-31 19:40:52.000000000 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_httplib.py 2025-12-19 23:25:14.713217193 +0100
|
||||
@@ -1196,6 +1196,72 @@
|
||||
thread.join()
|
||||
self.assertEqual(result, b"proxied data\n")
|
||||
|
||||
+ def test_large_content_length(self):
|
||||
+ serv = socket.create_server((HOST, 0))
|
||||
+ self.addCleanup(serv.close)
|
||||
+
|
||||
+ def run_server():
|
||||
+ [conn, address] = serv.accept()
|
||||
+ with conn:
|
||||
+ while conn.recv(1024):
|
||||
+ conn.sendall(
|
||||
+ b"HTTP/1.1 200 Ok\r\n"
|
||||
+ b"Content-Length: %d\r\n"
|
||||
+ b"\r\n" % size)
|
||||
+ conn.sendall(b'A' * (size//3))
|
||||
+ conn.sendall(b'B' * (size - size//3))
|
||||
+
|
||||
+ thread = threading.Thread(target=run_server)
|
||||
+ thread.start()
|
||||
+ self.addCleanup(thread.join, 1.0)
|
||||
+
|
||||
+ conn = client.HTTPConnection(*serv.getsockname())
|
||||
+ try:
|
||||
+ for w in range(15, 27):
|
||||
+ size = 1 << w
|
||||
+ conn.request("GET", "/")
|
||||
+ with conn.getresponse() as response:
|
||||
+ self.assertEqual(len(response.read()), size)
|
||||
+ finally:
|
||||
+ conn.close()
|
||||
+ thread.join(1.0)
|
||||
+
|
||||
+ def test_large_content_length_truncated(self):
|
||||
+ serv = socket.create_server((HOST, 0))
|
||||
+ self.addCleanup(serv.close)
|
||||
+
|
||||
+ def run_server():
|
||||
+ while True:
|
||||
+ [conn, address] = serv.accept()
|
||||
+ with conn:
|
||||
+ conn.recv(1024)
|
||||
+ if not size:
|
||||
+ break
|
||||
+ conn.sendall(
|
||||
+ b"HTTP/1.1 200 Ok\r\n"
|
||||
+ b"Content-Length: %d\r\n"
|
||||
+ b"\r\n"
|
||||
+ b"Text" % size)
|
||||
+
|
||||
+ thread = threading.Thread(target=run_server)
|
||||
+ thread.start()
|
||||
+ self.addCleanup(thread.join, 1.0)
|
||||
+
|
||||
+ conn = client.HTTPConnection(*serv.getsockname())
|
||||
+ try:
|
||||
+ for w in range(18, 65):
|
||||
+ size = 1 << w
|
||||
+ conn.request("GET", "/")
|
||||
+ with conn.getresponse() as response:
|
||||
+ self.assertRaises(client.IncompleteRead, response.read)
|
||||
+ conn.close()
|
||||
+ finally:
|
||||
+ conn.close()
|
||||
+ size = 0
|
||||
+ conn.request("GET", "/")
|
||||
+ conn.close()
|
||||
+ thread.join(1.0)
|
||||
+
|
||||
def test_putrequest_override_domain_validation(self):
|
||||
"""
|
||||
It should be possible to override the default validation
|
||||
Index: Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst 2025-12-19 23:25:14.713527850 +0100
|
||||
@@ -0,0 +1,5 @@
|
||||
+Fix a potential memory denial of service in the :mod:`http.client` module.
|
||||
+When connecting to a malicious server, it could cause
|
||||
+an arbitrary amount of memory to be allocated.
|
||||
+This could have led to symptoms including a :exc:`MemoryError`, swapping, out
|
||||
+of memory (OOM) killed processes or containers, or even system crashes.
|
||||
159
CVE-2025-13837-plistlib-mailicious-length.patch
Normal file
159
CVE-2025-13837-plistlib-mailicious-length.patch
Normal file
@@ -0,0 +1,159 @@
|
||||
From e99059d800b741504ef18693803927a0dc062be4 Mon Sep 17 00:00:00 2001
|
||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||
Date: Mon, 1 Dec 2025 17:28:15 +0200
|
||||
Subject: [PATCH] [3.10] gh-119342: Fix a potential denial of service in
|
||||
plistlib (GH-119343)
|
||||
|
||||
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 694922cf40aa3a28f898b5f5ee08b71b4922df70)
|
||||
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
---
|
||||
Lib/plistlib.py | 31 +++++---
|
||||
Lib/test/test_plistlib.py | 37 +++++++++-
|
||||
Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst | 5 +
|
||||
3 files changed, 59 insertions(+), 14 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
|
||||
|
||||
Index: Python-3.9.25/Lib/plistlib.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/plistlib.py 2025-12-23 23:47:30.450823742 +0100
|
||||
+++ Python-3.9.25/Lib/plistlib.py 2025-12-23 23:49:03.726727983 +0100
|
||||
@@ -64,6 +64,9 @@
|
||||
PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
|
||||
globals().update(PlistFormat.__members__)
|
||||
|
||||
+# Data larger than this will be read in chunks, to prevent extreme
|
||||
+# overallocation.
|
||||
+_MIN_READ_BUF_SIZE = 1 << 20
|
||||
|
||||
class UID:
|
||||
def __init__(self, data):
|
||||
@@ -490,12 +493,24 @@
|
||||
|
||||
return tokenL
|
||||
|
||||
+ def _read(self, size):
|
||||
+ cursize = min(size, _MIN_READ_BUF_SIZE)
|
||||
+ data = self._fp.read(cursize)
|
||||
+ while True:
|
||||
+ if len(data) != cursize:
|
||||
+ raise InvalidFileException
|
||||
+ if cursize == size:
|
||||
+ return data
|
||||
+ delta = min(cursize, size - cursize)
|
||||
+ data += self._fp.read(delta)
|
||||
+ cursize += delta
|
||||
+
|
||||
def _read_ints(self, n, size):
|
||||
- data = self._fp.read(size * n)
|
||||
+ data = self._read(size * n)
|
||||
if size in _BINARY_FORMAT:
|
||||
return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
|
||||
else:
|
||||
- if not size or len(data) != size * n:
|
||||
+ if not size:
|
||||
raise InvalidFileException()
|
||||
return tuple(int.from_bytes(data[i: i + size], 'big')
|
||||
for i in range(0, size * n, size))
|
||||
@@ -552,22 +567,16 @@
|
||||
|
||||
elif tokenH == 0x40: # data
|
||||
s = self._get_size(tokenL)
|
||||
- result = self._fp.read(s)
|
||||
- if len(result) != s:
|
||||
- raise InvalidFileException()
|
||||
+ result = self._read(s)
|
||||
|
||||
elif tokenH == 0x50: # ascii string
|
||||
s = self._get_size(tokenL)
|
||||
- data = self._fp.read(s)
|
||||
- if len(data) != s:
|
||||
- raise InvalidFileException()
|
||||
+ data = self._read(s)
|
||||
result = data.decode('ascii')
|
||||
|
||||
elif tokenH == 0x60: # unicode string
|
||||
s = self._get_size(tokenL) * 2
|
||||
- data = self._fp.read(s)
|
||||
- if len(data) != s:
|
||||
- raise InvalidFileException()
|
||||
+ data = self._read(s)
|
||||
result = data.decode('utf-16be')
|
||||
|
||||
elif tokenH == 0x80: # UID
|
||||
Index: Python-3.9.25/Lib/test/test_plistlib.py
|
||||
===================================================================
|
||||
--- Python-3.9.25.orig/Lib/test/test_plistlib.py 2025-12-23 23:47:31.633839488 +0100
|
||||
+++ Python-3.9.25/Lib/test/test_plistlib.py 2025-12-23 23:50:05.844028198 +0100
|
||||
@@ -837,8 +837,7 @@
|
||||
|
||||
class TestBinaryPlistlib(unittest.TestCase):
|
||||
|
||||
- @staticmethod
|
||||
- def decode(*objects, offset_size=1, ref_size=1):
|
||||
+ def build(self, *objects, offset_size=1, ref_size=1):
|
||||
data = [b'bplist00']
|
||||
offset = 8
|
||||
offsets = []
|
||||
@@ -850,7 +849,11 @@
|
||||
len(objects), 0, offset)
|
||||
data.extend(offsets)
|
||||
data.append(tail)
|
||||
- return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)
|
||||
+ return b''.join(data)
|
||||
+
|
||||
+ def decode(self, *objects, offset_size=1, ref_size=1):
|
||||
+ data = self.build(*objects, offset_size=offset_size, ref_size=ref_size)
|
||||
+ return plistlib.loads(data, fmt=plistlib.FMT_BINARY)
|
||||
|
||||
def test_nonstandard_refs_size(self):
|
||||
# Issue #21538: Refs and offsets are 24-bit integers
|
||||
@@ -958,6 +961,34 @@
|
||||
with self.assertRaises(plistlib.InvalidFileException):
|
||||
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
|
||||
|
||||
+ def test_truncated_large_data(self):
|
||||
+ self.addCleanup(support.unlink, support.TESTFN)
|
||||
+ def check(data):
|
||||
+ with open(support.TESTFN, 'wb') as f:
|
||||
+ f.write(data)
|
||||
+ # buffered file
|
||||
+ with open(support.TESTFN, 'rb') as f:
|
||||
+ with self.assertRaises(plistlib.InvalidFileException):
|
||||
+ plistlib.load(f, fmt=plistlib.FMT_BINARY)
|
||||
+ # unbuffered file
|
||||
+ with open(support.TESTFN, 'rb', buffering=0) as f:
|
||||
+ with self.assertRaises(plistlib.InvalidFileException):
|
||||
+ plistlib.load(f, fmt=plistlib.FMT_BINARY)
|
||||
+ for w in range(20, 64):
|
||||
+ s = 1 << w
|
||||
+ # data
|
||||
+ check(self.build(b'\x4f\x13' + s.to_bytes(8, 'big')))
|
||||
+ # ascii string
|
||||
+ check(self.build(b'\x5f\x13' + s.to_bytes(8, 'big')))
|
||||
+ # unicode string
|
||||
+ check(self.build(b'\x6f\x13' + s.to_bytes(8, 'big')))
|
||||
+ # array
|
||||
+ check(self.build(b'\xaf\x13' + s.to_bytes(8, 'big')))
|
||||
+ # dict
|
||||
+ check(self.build(b'\xdf\x13' + s.to_bytes(8, 'big')))
|
||||
+ # number of objects
|
||||
+ check(b'bplist00' + struct.pack('>6xBBQQQ', 1, 1, s, 0, 8))
|
||||
+
|
||||
|
||||
class TestKeyedArchive(unittest.TestCase):
|
||||
def test_keyed_archive_data(self):
|
||||
Index: Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst
|
||||
===================================================================
|
||||
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ Python-3.9.25/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst 2025-12-23 23:49:03.727528792 +0100
|
||||
@@ -0,0 +1,5 @@
|
||||
+Fix a potential memory denial of service in the :mod:`plistlib` module.
|
||||
+When reading a Plist file received from untrusted source, it could cause
|
||||
+an arbitrary amount of memory to be allocated.
|
||||
+This could have led to symptoms including a :exc:`MemoryError`, swapping, out
|
||||
+of memory (OOM) killed processes or containers, or even system crashes.
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d4892cd1618f6458cb851208c030df1482779609d0f3939991bd38184f8c679e
|
||||
size 19682840
|
||||
@@ -1,16 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmX5uMIACgkQsmmV4xAl
|
||||
BWj1tQ//T2qX0m08xWGV7az0D1sH3qjoY+4fEYrknw5uAHqZFiQecRsF27jxv6iH
|
||||
gP/6GAUw+lbH+9UofhCc0NbPOklliS7gFLNqJdKYFB6JXRNxiRYKh3uVx5o2n0ES
|
||||
kR3kRl77S47rtCbSMrKTh6ZoWowyIUZGFsIonk5KsLv+oELXY1AK/Im9i3/iTJ1Z
|
||||
jd/e2oHWuseIxbGZAO8AEP8zOsMMIHfsL3ry8H9xhhPyQM6t5DldqLH3UVE6kq95
|
||||
fs+olGO4FEKif3VDuLaHVlgtGZOUr6aDIYUmWxctPicboSb6RJAq37CCYgWykOyB
|
||||
WQec0ONbU7lxt5jhemLSDRy0mEio7+nXIKsO9rDN0Wk1QMpHUl77/C5qVlzfHal7
|
||||
NhPt8Yl0hBnOjzTq+di+xhAKJcdKp+zZH7/ugAbthuqhNfnkqiF68PANHrCm3gbY
|
||||
myN0eSaQ9yIa/MbHW8Am9NL/nuFbxdJUL/OIKQ9kFHgD7Qid86TZF0G2vbiBH/eF
|
||||
IVYoMxRZLd7eu5dIcwXSef+Ai97pODbx9y7bOCFyBO9FuFrlhPObgc7KXCeAzP+y
|
||||
k5eWvZtWTvvQ+2si2iT22EPBO0D0pnhYWZKpGK5EuKuw8nasNS1yLbhDTVpARynd
|
||||
8buQh3t2wPfILlQr0+JzDY8GSdQ/nIHGgx2IERdSX/v+9Yo2AvU=
|
||||
=gYAl
|
||||
-----END PGP SIGNATURE-----
|
||||
BIN
Python-3.9.20.tar.xz
LFS
BIN
Python-3.9.20.tar.xz
LFS
Binary file not shown.
@@ -1,16 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCAAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmbcKf0ACgkQsmmV4xAl
|
||||
BWh4rg//R5E1EjsifYqhLeIyT+JnrBvbTZeEcdxPXevsgilojYmrxBUKuXXViul0
|
||||
YZFaoDf6wjbHh6NMNgUpqcOH/5S/LsFZvuEcrw0jyGlMr0AMA4KLmNvQ9Wxf+wp4
|
||||
mUmhymQx555nVivsdPiziNnDwubZeA870ZllYEMWP5vXw7p2LbnlZvn7A+LSKjqM
|
||||
S/6xbiKYVexK3vHY/uG0xo4z24FySfvs0/PF11JfRJCxm9+bli7FmHOoFMwpOO6S
|
||||
caZLok4987YWOcPIPY6h+o2sFhDqHs8POGKd8k+0KQNQs5UbEQ4t/eKgnaoATkGn
|
||||
nfcAGXSjX5RSv5uXPzBUc0PulYo6EalIn1b5fu96La/FEg9GLMR/n9g75Fgm/j9L
|
||||
QGYu/DSaastY/c7Ot4QVyB6pxbQKjM438yneQrjhKBILGla4Crh1k6yRCx93j/TH
|
||||
hF9kiuRf7jtLIGTp0cnquELGnatmL1RhOySn/1Y+asMR+oK8d+XQab//w4VsAt7C
|
||||
SIfVXg25PUgZoaiYj/qIjLK9vkcj/EZ1IacivP5qBWb3O1E8gzSV8Z9duGT8Ef3P
|
||||
ch4M/pd6hefVVVfyCoazB3gwDs68O6U2BIRdYLRlet8AuKTBysQKFwOo3EcCMmJV
|
||||
W20KutPnERCzt8jeJdzFd0z3po9mvxNTKDLYaABtNI6NN00LcsM=
|
||||
=svjf
|
||||
-----END PGP SIGNATURE-----
|
||||
BIN
Python-3.9.25.tar.xz
LFS
Normal file
BIN
Python-3.9.25.tar.xz
LFS
Normal file
Binary file not shown.
1
Python-3.9.25.tar.xz.sigstore
Normal file
1
Python-3.9.25.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
36
gh139257-Support-docutils-0.22.patch
Normal file
36
gh139257-Support-docutils-0.22.patch
Normal file
@@ -0,0 +1,36 @@
|
||||
From 19b61747df3d62c822285c488753d6fbdf91e3ac Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||
Date: Tue, 23 Sep 2025 10:20:16 +0200
|
||||
Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22
|
||||
|
||||
---
|
||||
Doc/tools/extensions/pyspecific.py | 12 +++++++++++-
|
||||
1 file changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: Python-3.9.23/Doc/tools/extensions/pyspecific.py
|
||||
===================================================================
|
||||
--- Python-3.9.23.orig/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.712969300 +0200
|
||||
+++ Python-3.9.23/Doc/tools/extensions/pyspecific.py 2025-09-30 18:26:26.763263890 +0200
|
||||
@@ -55,11 +55,21 @@
|
||||
SOURCE_URI = 'https://github.com/python/cpython/tree/3.9/%s'
|
||||
|
||||
# monkey-patch reST parser to disable alphabetic and roman enumerated lists
|
||||
+def _disable_alphabetic_and_roman(text):
|
||||
+ try:
|
||||
+ # docutils >= 0.22
|
||||
+ from docutils.parsers.rst.states import InvalidRomanNumeralError
|
||||
+ raise InvalidRomanNumeralError(text)
|
||||
+ except ImportError:
|
||||
+ # docutils < 0.22
|
||||
+ return None
|
||||
+
|
||||
+
|
||||
from docutils.parsers.rst.states import Body
|
||||
Body.enum.converters['loweralpha'] = \
|
||||
Body.enum.converters['upperalpha'] = \
|
||||
Body.enum.converters['lowerroman'] = \
|
||||
- Body.enum.converters['upperroman'] = lambda x: None
|
||||
+ Body.enum.converters['upperroman'] = _disable_alphabetic_and_roman
|
||||
|
||||
|
||||
# Support for marking up and linking to bugs.python.org issues
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
Lib/test/test_posix.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/Lib/test/test_posix.py
|
||||
+++ b/Lib/test/test_posix.py
|
||||
@@ -422,7 +422,7 @@ class PosixTester(unittest.TestCase):
|
||||
def test_posix_fadvise(self):
|
||||
fd = os.open(support.TESTFN, os.O_RDONLY)
|
||||
try:
|
||||
- posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED)
|
||||
+ posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
409
python39.changes
409
python39.changes
@@ -1,3 +1,321 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Dec 18 10:33:44 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-13836-http-resp-cont-len.patch (bsc#1254400,
|
||||
CVE-2025-13836) to prevent reading an HTTP response from
|
||||
a server, if no read amount is specified, with using
|
||||
Content-Length per default as the length.
|
||||
- Add CVE-2025-12084-minidom-quad-search.patch prevent quadratic
|
||||
behavior in node ID cache clearing (CVE-2025-12084,
|
||||
bsc#1254997).
|
||||
- Add CVE-2025-13837-plistlib-mailicious-length.patch protect
|
||||
against OOM when loading malicious content (CVE-2025-13837,
|
||||
bsc#1254401).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Dec 11 21:44:35 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
* Update to 3.9.25:
|
||||
- Security
|
||||
- gh-137836: Add support of the “plaintext” element, RAWTEXT
|
||||
elements “xmp”, “iframe”, “noembed” and “noframes”, and
|
||||
optionally RAWTEXT element “noscript” in
|
||||
html.parser.HTMLParser.
|
||||
- gh-136063: email.message: ensure linear complexity for
|
||||
legacy HTTP parameters parsing. Patch by Bénédikt Tran.
|
||||
- gh-136065: Fix quadratic complexity in
|
||||
os.path.expandvars() (CVE-2025-6075, bsc#1252974).
|
||||
- Library
|
||||
- gh-98793: Fix argument typechecks in
|
||||
_overlapped.WSAConnect() and
|
||||
_overlapped.Overlapped.WSASendTo() functions. bpo-44817:
|
||||
Ignore WinError 53 (ERROR_BAD_NETPATH), 65
|
||||
(ERROR_NETWORK_ACCESS_DENIED) and 161 (ERROR_BAD_PATHNAME)
|
||||
when using ntpath.realpath().
|
||||
- Core and Builtins
|
||||
- gh-120384: Fix an array out of bounds crash in
|
||||
list_ass_subscript, which could be invoked via some
|
||||
specificly tailored input: including concurrent
|
||||
modification of a list object, where one thread assigns
|
||||
a slice and another clears it.
|
||||
- gh-120298: Fix use-after free in list_richcompare_impl
|
||||
which can be invoked via some specificly tailored evil
|
||||
input.
|
||||
- Remove upstreamed patches:
|
||||
-CVE-2025-6075-expandvars-perf-degrad.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Nov 13 17:13:03 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-6075-expandvars-perf-degrad.patch avoid simple
|
||||
quadratic complexity vulnerabilities of os.path.expandvars()
|
||||
(CVE-2025-6075, bsc#1252974).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Oct 15 06:28:09 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Update to 3.9.24:
|
||||
- Security
|
||||
- gh-139700: Check consistency of the zip64 end of central
|
||||
directory record. Support records with “zip64 extensible data”
|
||||
if there are no bytes prepended to the ZIP file
|
||||
(CVE-2025-8291, bsc#1251305).
|
||||
- gh-139400: xml.parsers.expat: Make sure that parent Expat
|
||||
parsers are only garbage-collected once they are no longer
|
||||
referenced by subparsers created by
|
||||
ExternalEntityParserCreate(). Patch by Sebastian Pipping.
|
||||
- gh-121227: Raise an SSL.SSLError if an empty protocols argument
|
||||
is passed to ssl.SSLContext.set_npn_protocols() to fix
|
||||
CVE-2024-5642.
|
||||
- gh-135661: Fix parsing start and end tags in
|
||||
html.parser.HTMLParser according to the HTML5 standard.
|
||||
* Whitespaces no longer accepted between </ and the tag name.
|
||||
E.g. </ script> does not end the script section.
|
||||
* Vertical tabulation (\v) and non-ASCII whitespaces no longer
|
||||
recognized as whitespaces. The only whitespaces are \t\n\r\f
|
||||
and space.
|
||||
* Null character (U+0000) no longer ends the tag name.
|
||||
* Attributes and slashes after the tag name in end tags are now
|
||||
ignored, instead of terminating after the first > in quoted
|
||||
attribute value. E.g. </script/foo=">"/>.
|
||||
* Multiple slashes and whitespaces between the last attribute
|
||||
and closing > are now ignored in both start and end tags. E.g.
|
||||
<a foo=bar/ //>.
|
||||
* Multiple = between attribute name and value are no longer
|
||||
collapsed. E.g. <a foo==bar> produces attribute “foo” with
|
||||
value “=bar”.
|
||||
- gh-135661: Fix CDATA section parsing in html.parser.HTMLParser
|
||||
according to the HTML5 standard: ] ]> and ]] > no longer end the
|
||||
CDATA section. Add private method _set_support_cdata() which can
|
||||
be used to specify how to parse <[CDATA[ — as a CDATA section in
|
||||
foreign content (SVG or MathML) or as a bogus comment in the
|
||||
HTML namespace.
|
||||
- gh-102555: Fix comment parsing in html.parser.HTMLParser
|
||||
according to the HTML5 standard. --!> now ends the comment. -- >
|
||||
no longer ends the comment. Support abnormally ended empty
|
||||
comments <--> and <--->.
|
||||
- gh-135462: Fix quadratic complexity in processing specially
|
||||
crafted input in html.parser.HTMLParser. End-of-file errors are
|
||||
now handled according to the HTML5 specs – comments and
|
||||
declarations are automatically closed, tags are ignored.
|
||||
- gh-118350: Fix support of escapable raw text mode (elements
|
||||
“textarea” and “title”) in html.parser.HTMLParser.
|
||||
- gh-86155: html.parser.HTMLParser.close() no longer loses data
|
||||
when the <script> tag is not closed. Patch by Waylan Limberg.
|
||||
- Library
|
||||
- gh-139312: Upgrade bundled libexpat to 2.7.3
|
||||
- gh-138998: Update bundled libexpat to 2.7.2
|
||||
- gh-130577: tarfile now validates archives to ensure member
|
||||
offsets are non-negative. (Contributed by Alexander Enrique
|
||||
Urieles Nieto in gh-130577.)
|
||||
- gh-135374: Update the bundled copy of setuptools to 79.0.1.
|
||||
|
||||
- Drop upstreamed patches:
|
||||
- CVE-2025-8194-tarfile-no-neg-offsets.patch
|
||||
- CVE-2025-6069-quad-complex-HTMLParser.patch
|
||||
|
||||
- Refresh patch CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Sep 29 06:52:07 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add gh139257-Support-docutils-0.22.patch to fix build with latest
|
||||
docutils (>=0.22) gh#python/cpython#139257
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Aug 1 20:09:24 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-8194-tarfile-no-neg-offsets.patch which now
|
||||
validates archives to ensure member offsets are non-negative
|
||||
(gh#python/cpython#130577, CVE-2025-8194, bsc#1247249).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Jul 2 14:47:20 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-6069-quad-complex-HTMLParser.patch to avoid worst
|
||||
case quadratic complexity when processing certain crafted
|
||||
malformed inputs with HTMLParser (CVE-2025-6069, bsc#1244705).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Jun 9 16:14:05 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Update to 3.9.23:
|
||||
- Security
|
||||
- gh-135034: Fixes multiple issues that allowed tarfile
|
||||
extraction filters (filter="data" and filter="tar")
|
||||
to be bypassed using crafted symlinks and hard links.
|
||||
Addresses CVE-2024-12718 (bsc#1244056), CVE-2025-4138
|
||||
(bsc#1244059), CVE-2025-4330 (bsc#1244060), and
|
||||
CVE-2025-4517 (bsc#1244032). Also addresses CVE-2025-4435
|
||||
(gh#135034, bsc#1244061).
|
||||
- gh-133767: Fix use-after-free in the “unicode-escape”
|
||||
decoder with a non-“strict” error handler (CVE-2025-4516,
|
||||
bsc#1243273).
|
||||
- gh-128840: Short-circuit the processing of long IPv6
|
||||
addresses early in ipaddress to prevent excessive memory
|
||||
consumption and a minor denial-of-service.
|
||||
- gh-80222: Fix bug in the folding of quoted strings
|
||||
when flattening an email message using a modern email
|
||||
policy. Previously when a quoted string was folded so
|
||||
that it spanned more than one line, the surrounding
|
||||
quotes and internal escapes would be omitted. This could
|
||||
theoretically be used to spoof header lines using a
|
||||
carefully constructed quoted string if the resulting
|
||||
rendered email was transmitted or re-parsed.
|
||||
- Library
|
||||
- gh-128840: Fix parsing long IPv6 addresses with embedded
|
||||
IPv4 address.
|
||||
- gh-134062: ipaddress: fix collisions in __hash__() for
|
||||
IPv4Network and IPv6Network objects.
|
||||
- gh-123409: Fix ipaddress.IPv6Address.reverse_pointer output
|
||||
according to RFC 3596, §2.5. Patch by Bénédikt Tran.
|
||||
- bpo-43633: Improve the textual representation of
|
||||
IPv4-mapped IPv6 addresses (RFC 4291 Sections 2.2, 2.5.5.2)
|
||||
in ipaddress. Patch by Oleksandr Pavliuk.
|
||||
- bpo-25264: os.path.realpath() now accepts a strict
|
||||
keyword-only argument. When set to True, OSError is raised
|
||||
if a path doesn’t exist or a symlink loop is encountered.
|
||||
- Remove upstreamed patches:
|
||||
- CVE-2025-4516-DecodeError-handler.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu May 22 13:01:17 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-4516-DecodeError-handler.patch fixing
|
||||
CVE-2025-4516 (bsc#1243273) blocking DecodeError handling
|
||||
vulnerability, which could lead to DoS.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Sat May 10 11:38:21 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Remove python-3.3.0b1-test-posix_fadvise.patch (not needed
|
||||
since kernel 3.6-rc1)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Apr 9 20:04:17 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Update to 3.9.22:
|
||||
- gh-131809: Update bundled libexpat to 2.7.1
|
||||
- gh-131261: Upgrade to libexpat 2.7.0
|
||||
- gh-105704: When using urllib.parse.urlsplit() and
|
||||
urllib.parse.urlparse() host parsing would not reject domain
|
||||
names containing square brackets ([ and ]). Square brackets
|
||||
are only valid for IPv6 and IPvFuture hosts according to RFC
|
||||
3986 Section 3.2.2 (bsc#1236705, CVE-2025-0938,
|
||||
gh#python/cpython#105704).
|
||||
- gh-121284: Fix bug in the folding of rfc2047 encoded-words
|
||||
when flattening an email message using a modern email
|
||||
policy. Previously when an encoded-word was too long for
|
||||
a line, it would be decoded, split across lines, and
|
||||
re-encoded. But commas and other special characters in the
|
||||
original text could be left unencoded and unquoted. This
|
||||
could theoretically be used to spoof header lines using a
|
||||
carefully constructed encoded-word if the resulting rendered
|
||||
email was transmitted or re-parsed.
|
||||
- gh-119511: Fix a potential denial of service in the imaplib
|
||||
module. When connecting to a malicious server, it could
|
||||
cause an arbitrary amount of memory to be allocated. On many
|
||||
systems this is harmless as unused virtual memory is only
|
||||
a mapping, but if this hit a virtual address size limit
|
||||
it could lead to a MemoryError or other process crash. On
|
||||
unusual systems or builds where all allocated memory is
|
||||
touched and backed by actual ram or storage it could’ve
|
||||
consumed resources doing so until similarly crashing.
|
||||
- gh-121277: Writers of CPython’s documentation can now use
|
||||
next as the version for the versionchanged, versionadded,
|
||||
deprecated directives.
|
||||
- Remote upstreamed patch:
|
||||
- CVE-2025-0938-sq-brackets-domain-names.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Mar 10 15:44:31 UTC 2025 - Bernhard Wiedemann <bwiedemann@suse.com>
|
||||
|
||||
- Skip PGO with %want_reproducible_builds (bsc#1239210)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Feb 4 14:43:13 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2025-0938-sq-brackets-domain-names.patch which
|
||||
disallows square brackets ([ and ]) in domain names for parsed
|
||||
URLs (bsc#1236705, CVE-2025-0938, gh#python/cpython#105704)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Dec 4 19:51:41 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Update to 3.9.21:
|
||||
- Tests
|
||||
- gh-125041: Re-enable skipped tests for zlib on the
|
||||
s390x architecture: only skip checks of the compressed
|
||||
bytes, which can be different between zlib’s software
|
||||
implementation and the hardware-accelerated implementation.
|
||||
- gh-109396: Fix test_socket.test_hmac_sha1() in FIPS
|
||||
mode. Use a longer key: FIPS mode requires at least of at
|
||||
least 112 bits. The previous key was only 32 bits. Patch by
|
||||
Victor Stinner.
|
||||
- gh-100454: Fix SSL tests CI for OpenSSL 3.1+
|
||||
- Security
|
||||
- gh-126623: Upgrade libexpat to 2.6.4
|
||||
- gh-122792: Changed IPv4-mapped ipaddress.IPv6Address to
|
||||
consistently use the mapped IPv4 address value for deciding
|
||||
properties. Properties which have their behavior fixed are
|
||||
is_multicast, is_reserved, is_link_local, is_global, and
|
||||
is_unspecified (bsc#1233307, CVE-2024-11168).
|
||||
- Library
|
||||
- gh-124651: Properly quote template strings in venv
|
||||
activation scripts (bsc#1232241, CVE-2024-9287).
|
||||
- gh-103848: Add checks to ensure that [ bracketed ] hosts
|
||||
found by urllib.parse.urlsplit() are of IPv6 or IPvFuture
|
||||
format.
|
||||
- Documentation
|
||||
- gh-95588: Clarified the conflicting advice given in the ast
|
||||
documentation about ast.literal_eval() being “safe” for use
|
||||
on untrusted input while at the same time warning that it
|
||||
can crash the process. The latter statement is true and is
|
||||
deemed unfixable without a large amount of work unsuitable
|
||||
for a bugfix. So we keep the warning and no longer claim
|
||||
that literal_eval is safe.
|
||||
- Remove upstreamed patches:
|
||||
- CVE-2024-11168-validation-IPv6-addrs.patch
|
||||
- CVE-2024-9287-venv_path_unquoted.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Nov 14 07:06:20 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Remove -IVendor/ from python-config boo#1231795
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Nov 13 13:25:01 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2024-11168-validation-IPv6-addrs.patch
|
||||
fixing bsc#1233307 (CVE-2024-11168,
|
||||
gh#python/cpython#103848): Improper validation of IPv6 and
|
||||
IPvFuture addresses.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Nov 1 21:16:32 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Update CVE-2024-9287-venv_path_unquoted.patch according to the
|
||||
upstream PR gh#python/cpython!126301.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Oct 24 16:09:00 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2024-9287-venv_path_unquoted.patch to properly quote
|
||||
path names provided when creating a virtual environment
|
||||
(bsc#1232241, CVE-2024-9287)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Oct 2 16:18:29 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Drop .pyc files from docdir for reproducible builds
|
||||
(bsc#1230906).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Sep 20 14:57:10 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add sphinx-802.patch to overcome working both with the most
|
||||
recent and older Sphinx versions.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Sep 9 18:02:59 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
@@ -34,7 +352,7 @@ Mon Sep 9 18:02:59 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
:class:`zipfile.Path` causing infinite loops (gh-122905) without breaking
|
||||
contents using legitimate characters (bsc#1229704, CVE-2024-8088).
|
||||
- gh-123067: Fix quadratic complexity in parsing ``"``-quoted cookie values
|
||||
with backslashes by :mod:`http.cookies`.
|
||||
with backslashes by :mod:`http.cookies` (bsc#1229596, CVE-2024-7592).
|
||||
- gh-121650: :mod:`email` headers with embedded newlines are now quoted on
|
||||
output. The :mod:`~email.generator` will now refuse to serialize (write)
|
||||
headers that are unsafely folded or delimited; see
|
||||
@@ -76,8 +394,7 @@ Mon Sep 9 18:02:59 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
Thu Sep 5 13:44:48 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Add CVE-2024-6232-cookies-quad-complex.patch to avoid quadratic
|
||||
complexity in parsing "-quoted cookie values with backslashes
|
||||
(bsc#1229596, CVE-2024-6232).
|
||||
complexity in parsing tarfile headers (bsc#1230227, CVE-2024-6232).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Sep 5 08:11:45 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
@@ -224,12 +541,12 @@ Fri Feb 23 01:06:42 UTC 2024 - Matej Cepl <mcepl@suse.com>
|
||||
- Repurpose skip-failing-tests.patch to increase timeout for
|
||||
test.test_asyncio.test_tasks.TimeoutTests.test_timeout_time,
|
||||
which fails on slow machines in IBS (s390x).
|
||||
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Feb 20 22:14:02 UTC 2024 - Matej Cepl <mcepl@cepl.eu>
|
||||
|
||||
- Remove double definition of /usr/bin/idle%%{version} in
|
||||
%%files.
|
||||
%%files.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Feb 15 10:29:07 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
@@ -368,7 +685,7 @@ Wed May 3 14:09:37 UTC 2023 - Matej Cepl <mcepl@suse.com>
|
||||
-------------------------------------------------------------------
|
||||
Tue Apr 18 05:00:11 UTC 2023 - Steve Kowalik <steven.kowalik@suse.com>
|
||||
|
||||
- Use python3 modules to build the documentation.
|
||||
- Use python3 modules to build the documentation.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Mar 1 14:43:31 UTC 2023 - Matej Cepl <mcepl@suse.com>
|
||||
@@ -941,7 +1258,7 @@ Sat Mar 26 22:22:24 UTC 2022 - Matej Cepl <mcepl@suse.com>
|
||||
Tue Feb 22 05:53:06 UTC 2022 - Steve Kowalik <steven.kowalik@suse.com>
|
||||
|
||||
- Add patch support-expat-245.patch:
|
||||
* Support Expat >= 2.4.5
|
||||
* Support Expat >= 2.4.5
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Jan 19 21:50:04 UTC 2022 - Matej Cepl <mcepl@suse.com>
|
||||
@@ -1384,7 +1701,7 @@ Sat Jun 5 21:21:38 UTC 2021 - Matej Cepl <mcepl@suse.com>
|
||||
-------------------------------------------------------------------
|
||||
Fri Jun 4 21:36:30 UTC 2021 - Dirk Müller <dmueller@suse.com>
|
||||
|
||||
- allow build with Sphinx >= 3.x
|
||||
- allow build with Sphinx >= 3.x
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Jun 2 13:12:04 UTC 2021 - Dan Čermák <dcermak@suse.com>
|
||||
@@ -1936,7 +2253,7 @@ Sat Dec 12 14:29:33 UTC 2020 - Matej Cepl <mcepl@suse.com>
|
||||
Thu Dec 10 00:26:51 UTC 2020 - Benjamin Greiner <code@bnavigator.de>
|
||||
|
||||
- Last try before this results in an editwar:
|
||||
* remove importlib_resources and importlib-metadata
|
||||
* remove importlib_resources and importlib-metadata
|
||||
provides/obsoletes
|
||||
* import importlib_resources is not the same as
|
||||
import importlib.resources, same for metadata
|
||||
@@ -2053,54 +2370,54 @@ Tue Jul 21 09:53:06 UTC 2020 - Callum Farmer <callumjfarmer13@gmail.com>
|
||||
- Removed CVE-2019-20907_tarfile-inf-loop.patch: fixed in upstream
|
||||
- Removed recursion.tar: contained in upstream
|
||||
- Update to 3.9.0b5:
|
||||
- bpo-41304: Fixes python3x._pth being ignored on Windows, caused
|
||||
- bpo-41304: Fixes python3x._pth being ignored on Windows, caused
|
||||
by the fix for bpo-29778 (CVE-2020-15801).
|
||||
- bpo-41162: Audit hooks are now cleared later during
|
||||
finalization to avoid missing events.
|
||||
- bpo-29778: Ensure python3.dll is loaded from correct locations
|
||||
- bpo-29778: Ensure python3.dll is loaded from correct locations
|
||||
when Python is embedded (CVE-2020-15523).
|
||||
- bpo-39603: Prevent http header injection by rejecting control
|
||||
- bpo-39603: Prevent http header injection by rejecting control
|
||||
characters in http.client.putrequest(…).
|
||||
- bpo-41295: Resolve a regression in CPython 3.8.4 where defining
|
||||
“__setattr__” in a multi-inheritance setup and
|
||||
“__setattr__” in a multi-inheritance setup and
|
||||
calling up the hierarchy chain could fail if builtins/extension
|
||||
types were involved in the base types.
|
||||
- bpo-41247: Always cache the running loop holder when running
|
||||
- bpo-41247: Always cache the running loop holder when running
|
||||
asyncio.set_running_loop.
|
||||
- bpo-41252: Fix incorrect refcounting in
|
||||
- bpo-41252: Fix incorrect refcounting in
|
||||
_ssl.c’s _servername_callback().
|
||||
- bpo-41215: Use non-NULL default values in the PEG parser
|
||||
- bpo-41215: Use non-NULL default values in the PEG parser
|
||||
keyword list to overcome a bug that was '
|
||||
preventing Python from being properly compiled when using the
|
||||
XLC compiler. Patch by Pablo Galindo.
|
||||
- bpo-41218: Python 3.8.3 had a regression where compiling with
|
||||
ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would
|
||||
- bpo-41218: Python 3.8.3 had a regression where compiling with
|
||||
ast.PyCF_ALLOW_TOP_LEVEL_AWAIT would
|
||||
aggressively mark list comprehension with CO_COROUTINE. Now only
|
||||
list comprehension making use of async/await will tagged as so.
|
||||
- bpo-41175: Guard against a NULL pointer dereference within
|
||||
- bpo-41175: Guard against a NULL pointer dereference within
|
||||
bytearrayobject triggered by the bytearray() + bytearray() operation.
|
||||
- bpo-39960: The “hackcheck” that prevents sneaking around a type’s
|
||||
__setattr__() by calling the superclass method was
|
||||
- bpo-39960: The “hackcheck” that prevents sneaking around a type’s
|
||||
__setattr__() by calling the superclass method was
|
||||
rewritten to allow C implemented heap types.
|
||||
- bpo-41288: Unpickling invalid NEWOBJ_EX opcode with the
|
||||
- bpo-41288: Unpickling invalid NEWOBJ_EX opcode with the
|
||||
C implementation raises now UnpicklingError instead of crashing.
|
||||
- bpo-39017: Avoid infinite loop when reading specially crafted
|
||||
- bpo-39017: Avoid infinite loop when reading specially crafted
|
||||
TAR files using the tarfile module (CVE-2019-20907, bsc#1174091).
|
||||
- bpo-41235: Fix the error handling in ssl.SSLContext.load_dh_params().
|
||||
- bpo-41207: In distutils.spawn, restore expectation that
|
||||
- bpo-41207: In distutils.spawn, restore expectation that
|
||||
DistutilsExecError is raised when the command is not found.
|
||||
- bpo-39168: Remove the __new__ method of typing.Generic.
|
||||
- bpo-41194: Fix a crash in the _ast module: it can no longer be
|
||||
- bpo-41194: Fix a crash in the _ast module: it can no longer be
|
||||
loaded more than once. It now uses a global state rather than a module state.
|
||||
- bpo-39384: Fixed email.contentmanager to allow set_content() to set a
|
||||
- bpo-39384: Fixed email.contentmanager to allow set_content() to set a
|
||||
null string.
|
||||
- bpo-41300: Save files with non-ascii chars.
|
||||
- bpo-41300: Save files with non-ascii chars.
|
||||
Fix regression released in 3.9.0b4 and 3.8.4.
|
||||
- bpo-37765: Add keywords to module name completion list.
|
||||
- bpo-37765: Add keywords to module name completion list.
|
||||
Rewrite Completions section of IDLE doc.
|
||||
- bpo-40170: Revert PyType_HasFeature() change: it reads
|
||||
again directly the PyTypeObject.tp_flags
|
||||
member when the limited C API is not used, rather than always calling
|
||||
- bpo-40170: Revert PyType_HasFeature() change: it reads
|
||||
again directly the PyTypeObject.tp_flags
|
||||
member when the limited C API is not used, rather than always calling
|
||||
PyType_GetFlags() which hides implementation details.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -2621,7 +2938,7 @@ Wed Jun 5 12:19:09 CEST 2019 - Matej Cepl <mcepl@suse.com>
|
||||
pickling costs between processes
|
||||
- typed_ast is merged back to CPython
|
||||
- LOAD_GLOBAL is now 40% faster
|
||||
- pickle now uses Protocol 4 by default, improving performance
|
||||
- pickle now uses Protocol 4 by default, improving performance
|
||||
- Remove patches which were included in the upstream:
|
||||
- 00251-change-user-install-location.patch
|
||||
- 00316-mark-bdist_wininst-unsupported.patch
|
||||
@@ -2766,7 +3083,7 @@ Mon Dec 17 17:24:49 CET 2018 - mcepl@suse.com
|
||||
|
||||
- Upgrade to 3.7.2rc1:
|
||||
* bugfix release, for the full list of all changes see
|
||||
https://docs.python.org/3.7/whatsnew/changelog.html#changelog
|
||||
https://docs.python.org/3.7/whatsnew/changelog.html#changelog
|
||||
- Make run of the test suite more verbose
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -3193,7 +3510,7 @@ Mon Mar 13 14:04:22 UTC 2017 - jmatejek@suse.com
|
||||
Sat Feb 25 20:55:57 UTC 2017 - bwiedemann@suse.com
|
||||
|
||||
- Add 0001-allow-for-reproducible-builds-of-python-packages.patch
|
||||
upstream https://github.com/python/cpython/pull/296
|
||||
upstream https://github.com/python/cpython/pull/296
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Feb 8 12:30:20 UTC 2017 - jmatejek@suse.com
|
||||
@@ -3259,7 +3576,7 @@ Mon Mar 7 20:38:11 UTC 2016 - toddrme2178@gmail.com
|
||||
|
||||
- Add Python-3.5.1-fix_lru_cache_copying.patch
|
||||
Fix copying the lru_cache() wrapper object.
|
||||
Fixes deep-copying lru_cache regression, which worked on
|
||||
Fixes deep-copying lru_cache regression, which worked on
|
||||
previous versions of python but fails on python 3.5.
|
||||
This fixes a bunch of packages in devel:languages:python3.
|
||||
See: https://bugs.python.org/issue25447
|
||||
@@ -3397,7 +3714,7 @@ Sun Jan 11 13:01:30 UTC 2015 - p.drouand@gmail.com
|
||||
-------------------------------------------------------------------
|
||||
Sat Oct 18 20:14:54 UTC 2014 - crrodriguez@opensuse.org
|
||||
|
||||
- Only pkgconfig(x11) is required for build, not the whole
|
||||
- Only pkgconfig(x11) is required for build, not the whole
|
||||
set of packages provided by xorg-x11-devel metapackage.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -3457,7 +3774,7 @@ Wed Mar 26 15:24:46 UTC 2014 - jmatejek@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Mon Mar 24 17:29:31 UTC 2014 - dmueller@suse.com
|
||||
|
||||
- remove blacklisting of test_posix on aarch64: qemu bug is fixed
|
||||
- remove blacklisting of test_posix on aarch64: qemu bug is fixed
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Mar 17 18:26:58 UTC 2014 - jmatejek@suse.com
|
||||
@@ -3560,7 +3877,7 @@ Tue Nov 19 14:28:41 UTC 2013 - jmatejek@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 15 17:44:08 UTC 2013 - crrodriguez@opensuse.org
|
||||
|
||||
- build with -DOPENSSL_LOAD_CONF for the same reasons
|
||||
- build with -DOPENSSL_LOAD_CONF for the same reasons
|
||||
described in the python2 package.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -3572,7 +3889,7 @@ Fri Aug 16 11:35:15 UTC 2013 - jmatejek@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Thu Aug 8 14:54:49 UTC 2013 - dvaleev@suse.com
|
||||
|
||||
- Exclue test_faulthandler from tests on powerpc due to bnc#831629
|
||||
- Exclue test_faulthandler from tests on powerpc due to bnc#831629
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Jun 13 15:05:34 UTC 2013 - jmatejek@suse.com
|
||||
@@ -3631,7 +3948,7 @@ Fri Mar 1 07:42:21 UTC 2013 - dmueller@suse.com
|
||||
|
||||
- add ctypes-libffi-aarch64.patch:
|
||||
* import aarch64 support for libffi in _ctypes module
|
||||
- add aarch64 to the list of lib64 based archs
|
||||
- add aarch64 to the list of lib64 based archs
|
||||
- add movetogetdents64.diff:
|
||||
* port to getdents64, as SYS_getdents is not implemented everywhere
|
||||
|
||||
@@ -3685,9 +4002,9 @@ Mon Oct 29 18:21:45 UTC 2012 - dmueller@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Thu Oct 25 08:14:36 UTC 2012 - Rene.vanPaassen@gmail.com
|
||||
|
||||
- exclude test_math for SLE 11; math library fails on negative
|
||||
- exclude test_math for SLE 11; math library fails on negative
|
||||
gamma function values close to integers and 0, probably
|
||||
due to imprecision in -lm on SLE_11_SP2.
|
||||
due to imprecision in -lm on SLE_11_SP2.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 16 12:15:34 UTC 2012 - coolo@suse.com
|
||||
@@ -3711,7 +4028,7 @@ Mon Oct 1 08:53:03 UTC 2012 - idonmez@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Thu Sep 27 12:35:01 UTC 2012 - idonmez@suse.com
|
||||
|
||||
- Correct dependency for python3-testsuite,
|
||||
- Correct dependency for python3-testsuite,
|
||||
python3-tkinter -> python3-tk
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -3744,7 +4061,7 @@ Fri Aug 3 12:09:34 UTC 2012 - jmatejek@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Fri Jul 27 09:02:41 UTC 2012 - dvaleev@suse.com
|
||||
|
||||
- skip test_io on ppc
|
||||
- skip test_io on ppc
|
||||
- drop test_io ppc patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
@@ -3793,8 +4110,8 @@ Wed Jan 18 15:49:47 UTC 2012 - jmatejek@suse.com
|
||||
-------------------------------------------------------------------
|
||||
Sun Dec 25 13:25:01 UTC 2011 - idonmez@suse.com
|
||||
|
||||
- Use system ffi, included one is broken see
|
||||
http://bugs.python.org/issue11729 and
|
||||
- Use system ffi, included one is broken see
|
||||
http://bugs.python.org/issue11729 and
|
||||
http://bugs.python.org/issue12081
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
106
python39.spec
106
python39.spec
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# spec file for package python39
|
||||
#
|
||||
# Copyright (c) 2024 SUSE LLC
|
||||
# Copyright (c) 2025 SUSE LLC and contributors
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
@@ -36,7 +36,7 @@
|
||||
%bcond_without general
|
||||
%endif
|
||||
|
||||
%if 0%{?do_profiling}
|
||||
%if 0%{?do_profiling} && !0%{?want_reproducible_builds}
|
||||
%bcond_without profileopt
|
||||
%else
|
||||
%bcond_with profileopt
|
||||
@@ -99,13 +99,13 @@
|
||||
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
||||
%bcond_without profileopt
|
||||
Name: %{python_pkg_name}%{psuffix}
|
||||
Version: 3.9.20
|
||||
Version: 3.9.25
|
||||
Release: 0
|
||||
Summary: Python 3 Interpreter
|
||||
License: Python-2.0
|
||||
URL: https://www.python.org/
|
||||
Source0: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz
|
||||
Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.asc
|
||||
Source1: https://www.python.org/ftp/python/%{folderversion}/%{tarname}.tar.xz.sigstore
|
||||
Source2: baselibs.conf
|
||||
Source3: README.SUSE
|
||||
Source7: macros.python3
|
||||
@@ -134,10 +134,6 @@ Source100: PACKAGING-NOTES
|
||||
# to /usr/local if executable is /usr/bin/python* and RPM build
|
||||
# is not detected to make pip and distutils install into separate location
|
||||
Patch02: F00251-change-user-install-location.patch
|
||||
# PATCH-FEATURE-UPSTREAM decimal.patch bsc#1189356 mcepl@suse.com
|
||||
# fix building with mpdecimal
|
||||
# https://www.bytereef.org/contrib/decimal.diff
|
||||
Patch05: decimal.patch
|
||||
# PATCH-FEATURE-UPSTREAM distutils-reproducible-compile.patch gh#python/cpython#8057 mcepl@suse.com
|
||||
# Improve reproduceability
|
||||
Patch06: distutils-reproducible-compile.patch
|
||||
@@ -145,8 +141,6 @@ Patch06: distutils-reproducible-compile.patch
|
||||
Patch07: python-3.3.0b1-localpath.patch
|
||||
# replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds
|
||||
Patch08: python-3.3.0b1-fix_date_time_compiler.patch
|
||||
# POSIX_FADV_WILLNEED throws EINVAL. Use a different constant in test
|
||||
Patch09: python-3.3.0b1-test-posix_fadvise.patch
|
||||
# Raise timeout value for test_subprocess
|
||||
Patch15: subprocess-raise-timeout.patch
|
||||
Patch25: python3-imp-returntype.patch
|
||||
@@ -161,37 +155,54 @@ Patch33: no-skipif-doctests.patch
|
||||
# PATCH-FIX-SLE skip-test_pyobject_freed_is_freed.patch mcepl@suse.com
|
||||
# skip a test failing on SLE-15
|
||||
Patch34: skip-test_pyobject_freed_is_freed.patch
|
||||
# PATCH-FEATURE-UPSTREAM decimal.patch bsc#1189356 mcepl@suse.com
|
||||
# fix building with mpdecimal
|
||||
# https://www.bytereef.org/contrib/decimal.diff
|
||||
Patch35: decimal.patch
|
||||
# PATCH-FIX-UPSTREAM support-expat-CVE-2022-25236-patched.patch jsc#SLE-21253 mcepl@suse.com
|
||||
# Makes Python resilient to changes of API of libexpat
|
||||
Patch35: support-expat-CVE-2022-25236-patched.patch
|
||||
Patch40: support-expat-CVE-2022-25236-patched.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2023-52425-libexpat-2.6.0-backport.patch gh#python/cpython#117187 mcepl@suse.com
|
||||
# Make the test suite work with libexpat < 2.6.0
|
||||
Patch36: CVE-2023-52425-libexpat-2.6.0-backport.patch
|
||||
Patch41: CVE-2023-52425-libexpat-2.6.0-backport.patch
|
||||
# PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com
|
||||
# this patch makes things totally awesome
|
||||
Patch37: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch
|
||||
Patch42: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch
|
||||
# PATCH-FIX-UPSTREAM bpo-37596-make-set-marshalling.patch bsc#1211765 mcepl@suse.com
|
||||
# Make `set` and `frozenset` marshalling deterministic
|
||||
Patch38: bpo-37596-make-set-marshalling.patch
|
||||
Patch43: bpo-37596-make-set-marshalling.patch
|
||||
# PATCH-FIX-UPSTREAM gh-78214-marshal_stabilize_FLAG_REF.patch bsc#1213463 mcepl@suse.com
|
||||
# marshal: Stabilize FLAG_REF usage
|
||||
Patch39: gh-78214-marshal_stabilize_FLAG_REF.patch
|
||||
Patch44: gh-78214-marshal_stabilize_FLAG_REF.patch
|
||||
# PATCH-FIX-UPSTREAM 99366-patch.dict-can-decorate-async.patch bsc#[0-9]+ mcepl@suse.com
|
||||
# Patch for gh#python/cpython#98086
|
||||
Patch40: 99366-patch.dict-can-decorate-async.patch
|
||||
Patch45: 99366-patch.dict-can-decorate-async.patch
|
||||
# PATCH-FIX-OPENSUSE downport-Sphinx-features.patch mcepl@suse.com
|
||||
# Make documentation build with older Sphinx
|
||||
Patch41: downport-Sphinx-features.patch
|
||||
Patch46: downport-Sphinx-features.patch
|
||||
# PATCH-FIX-UPSTREAM bso1227999-reproducible-builds.patch bsc#1227999 mcepl@suse.com
|
||||
# reproducibility patches
|
||||
Patch46: bso1227999-reproducible-builds.patch
|
||||
Patch47: bso1227999-reproducible-builds.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch bsc#1227233 mcepl@suse.com
|
||||
# Remove for support for anything but OpenSSL 1.1.1 or newer
|
||||
Patch48: CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch
|
||||
# PATCH-FIX-UPSTREAM gh120226-fix-sendfile-test-kernel-610.patch gh#python/cpython#120226 mcepl@suse.com
|
||||
# Fix test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 (GH-120227)
|
||||
Patch50: gh120226-fix-sendfile-test-kernel-610.patch
|
||||
|
||||
# PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com
|
||||
# status_iterator method moved between the Sphinx versions
|
||||
Patch51: sphinx-802.patch
|
||||
# PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com
|
||||
Patch52: gh139257-Support-docutils-0.22.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2025-13836-http-resp-cont-len.patch bsc#1254400 mcepl@suse.com
|
||||
# Avoid loading possibly compromised length of HTTP response
|
||||
Patch53: CVE-2025-13836-http-resp-cont-len.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2025-12084-minidom-quad-search.patch bsc#1254997 mcepl@suse.com
|
||||
# prevent quadratic behavior in node ID cache clearing
|
||||
Patch54: CVE-2025-12084-minidom-quad-search.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2025-13837-plistlib-mailicious-length.patch bsc#1254401 mcepl@suse.com
|
||||
# protect against OOM when loading malicious content
|
||||
Patch55: CVE-2025-13837-plistlib-mailicious-length.patch
|
||||
BuildRequires: autoconf-archive
|
||||
BuildRequires: automake
|
||||
BuildRequires: fdupes
|
||||
@@ -430,34 +441,43 @@ other applications.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{tarname}
|
||||
%patch -P 02 -p1
|
||||
%patch -P 06 -p1
|
||||
%patch -P 07 -p1
|
||||
%patch -P 08 -p1
|
||||
%patch -P 09 -p1
|
||||
%patch -P 15 -p1
|
||||
%patch -P 25 -p1
|
||||
%patch -P 29 -p1
|
||||
%patch -P 32 -p1
|
||||
|
||||
%patch -p1 -P 02
|
||||
%patch -p1 -P 06
|
||||
%patch -p1 -P 07
|
||||
%patch -p1 -P 08
|
||||
%patch -p1 -P 15
|
||||
%patch -p1 -P 25
|
||||
%patch -p1 -P 29
|
||||
%patch -p1 -P 32
|
||||
|
||||
%if 0%{?sle_version}
|
||||
%patch -P 33 -p1
|
||||
%patch -P 34 -p1
|
||||
%patch -p1 -P 33
|
||||
%patch -p1 -P 34
|
||||
%endif
|
||||
%if %{with mpdecimal}
|
||||
%patch -P 05 -p1
|
||||
%patch -p1 -P 35
|
||||
%endif
|
||||
%patch -P 35 -p1
|
||||
%patch -P 36 -p1
|
||||
%patch -P 37 -p1
|
||||
%patch -P 38 -p1
|
||||
%patch -P 39 -p1
|
||||
%patch -P 40 -p1
|
||||
%if 0%{?sle_version} && 0%{?sle_version} <= 150500
|
||||
|
||||
%patch -p1 -P 40
|
||||
%patch -p1 -P 41
|
||||
%endif
|
||||
%patch -p1 -P 42
|
||||
%patch -p1 -P 43
|
||||
%patch -p1 -P 44
|
||||
%patch -p1 -P 45
|
||||
|
||||
%if 0%{?sle_version} && 0%{?sle_version} <= 150500
|
||||
%patch -p1 -P 46
|
||||
%endif
|
||||
|
||||
%patch -p1 -P 47
|
||||
%patch -p1 -P 48
|
||||
%patch -p1 -P 50
|
||||
%patch -p1 -P 51
|
||||
%patch -p1 -P 52
|
||||
%patch -p1 -P 53
|
||||
%patch -p1 -P 54
|
||||
%patch -p1 -P 55
|
||||
|
||||
# drop Autoconf version requirement
|
||||
sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac
|
||||
@@ -764,6 +784,9 @@ install -m 755 -D Tools/gdb/libpython.py %{buildroot}%{_datadir}/gdb/auto-load/%
|
||||
# install devel files to /config
|
||||
#cp Makefile Makefile.pre.in Makefile.pre $RPM_BUILD_ROOT%{sitedir}/config-%{python_abi}/
|
||||
|
||||
# Remove -IVendor/ from python-config boo#1231795
|
||||
sed -i 's/-IVendor\///' %{buildroot}%{_bindir}/python%{python_abi}-config
|
||||
|
||||
# RPM macros
|
||||
%if %{primary_interpreter}
|
||||
mkdir -p %{buildroot}%{_rpmconfigdir}/macros.d/
|
||||
@@ -792,6 +815,11 @@ LD_LIBRARY_PATH=. ./python -O -c "from py_compile import compile; compile('$FAIL
|
||||
echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth
|
||||
%endif
|
||||
|
||||
# For the purposes of reproducibility, it is necessary to eliminate any *.pyc files inside documentation dirs
|
||||
if [ -d %{buildroot}%{_defaultdocdir} ] ; then
|
||||
find %{buildroot}%{_defaultdocdir} -type f -name \*.pyc -ls -exec rm -vf '{}' \;
|
||||
fi
|
||||
|
||||
%if %{with general}
|
||||
%files -n %{python_pkg_name}-tk
|
||||
%{sitedir}/tkinter
|
||||
|
||||
30
sphinx-802.patch
Normal file
30
sphinx-802.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
Doc/tools/extensions/pyspecific.py | 10 ++++++++--
|
||||
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/Doc/tools/extensions/pyspecific.py
|
||||
+++ b/Doc/tools/extensions/pyspecific.py
|
||||
@@ -28,7 +28,13 @@ try:
|
||||
except ImportError:
|
||||
from sphinx.environment import NoUri
|
||||
from sphinx.locale import _ as sphinx_gettext
|
||||
-from sphinx.util import status_iterator, logging
|
||||
+try:
|
||||
+ from sphinx.util.display import status_iterator
|
||||
+except ImportError:
|
||||
+ # This method was moved into sphinx.util.display in Sphinx 6.1.0. Before
|
||||
+ # that it resided in sphinx.util.
|
||||
+ from sphinx.util import status_iterator
|
||||
+from sphinx.util import logging
|
||||
from sphinx.util.nodes import split_explicit_title
|
||||
from sphinx.writers.text import TextWriter, TextTranslator
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
@@ -338,7 +344,7 @@ class PyAbstractMethod(PyMethod):
|
||||
def expand_version_arg(argument, release):
|
||||
"""Expand "next" to the current version"""
|
||||
if argument == 'next':
|
||||
- return translators['sphinx'].gettext('{} (unreleased)').format(release)
|
||||
+ return sphinx_gettext('{} (unreleased)').format(release)
|
||||
return argument
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
Doc/tools/extensions/pyspecific.py | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/Doc/tools/extensions/pyspecific.py
|
||||
+++ b/Doc/tools/extensions/pyspecific.py
|
||||
@@ -385,7 +385,12 @@ class DeprecatedRemoved(Directive):
|
||||
Index: Python-3.9.22/Doc/tools/extensions/pyspecific.py
|
||||
===================================================================
|
||||
--- Python-3.9.22.orig/Doc/tools/extensions/pyspecific.py 2025-04-08 17:21:55.000000000 +0200
|
||||
+++ Python-3.9.22/Doc/tools/extensions/pyspecific.py 2025-04-11 09:49:58.417019238 +0200
|
||||
@@ -407,7 +407,12 @@
|
||||
translatable=False)
|
||||
node.append(para)
|
||||
env = self.state.document.settings.env
|
||||
|
||||
Reference in New Issue
Block a user