Compare commits
35 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daba5b738c | ||
|
|
b4205523e7 | ||
|
|
7da201e900 | ||
|
|
c30eb40478 | ||
|
|
e6d9413cd5 | ||
|
|
16aa6aa9ff | ||
|
|
458effe6fa | ||
|
|
c9e6166456 | ||
|
|
e0dac6a2c5 | ||
|
|
6c0fab4bc9 | ||
|
|
311900d817 | ||
|
|
14cfcd8b1f | ||
|
|
bd23e23416 | ||
|
|
a3bdce551c | ||
|
|
b1fc74dd76 | ||
|
|
207259522d | ||
|
|
9d6cf58680 | ||
|
|
ec3cbfebe9 | ||
|
|
6c238008d1 | ||
|
|
75538d3684 | ||
|
|
772c5976f4 | ||
|
|
234d41ca6b | ||
|
|
a48837eb4a | ||
|
|
aceb601ce3 | ||
|
|
df9826a90e | ||
|
|
ee2c642d12 | ||
|
|
dfe7d50957 | ||
|
df15baee47 |
|||
|
|
10df377823 | ||
|
|
9af4582cf9 | ||
|
|
5c73fd0f8e | ||
|
|
cdabc2abe7 | ||
|
|
6d6290fa95 | ||
|
|
67b71083fa | ||
|
|
f7d93eee18 |
17 changed files with 654 additions and 661 deletions
|
|
@ -1,9 +1,10 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: David Malcolm <dmalcolm@redhat.com>
|
From: David Malcolm <dmalcolm@redhat.com>
|
||||||
Date: Wed, 13 Jan 2010 21:25:18 +0000
|
Date: Wed, 13 Jan 2010 21:25:18 +0000
|
||||||
Subject: [PATCH] 00001: Fixup distutils/unixccompiler.py to remove standard
|
Subject: 00001: Fixup distutils/unixccompiler.py to remove standard library
|
||||||
library path from rpath Was Patch0 in ivazquez' python3000 specfile
|
path from rpath
|
||||||
|
|
||||||
|
Was Patch0 in ivazquez' python3000 specfile
|
||||||
---
|
---
|
||||||
Lib/distutils/unixccompiler.py | 9 +++++++++
|
Lib/distutils/unixccompiler.py | 9 +++++++++
|
||||||
1 file changed, 9 insertions(+)
|
1 file changed, 9 insertions(+)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
||||||
Date: Mon, 15 Feb 2021 12:19:27 +0100
|
Date: Mon, 15 Feb 2021 12:19:27 +0100
|
||||||
Subject: [PATCH] 00251: Change user install location
|
Subject: 00251: Change user install location
|
||||||
MIME-Version: 1.0
|
MIME-Version: 1.0
|
||||||
Content-Type: text/plain; charset=UTF-8
|
Content-Type: text/plain; charset=UTF-8
|
||||||
Content-Transfer-Encoding: 8bit
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= <thrnciar@redhat.com>
|
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= <thrnciar@redhat.com>
|
||||||
Date: Tue, 7 Dec 2021 14:41:59 +0100
|
Date: Tue, 7 Dec 2021 14:41:59 +0100
|
||||||
Subject: [PATCH] 00371: Revert "bpo-1596321: Fix threading._shutdown() for the
|
Subject: 00371: Revert "bpo-1596321: Fix threading._shutdown() for the main
|
||||||
main thread (GH-28549) (GH-28589)"
|
thread (GH-28549) (GH-28589)"
|
||||||
|
|
||||||
This reverts commit 38c67738c64304928c68d5c2bd78bbb01d979b94. It
|
This reverts commit 38c67738c64304928c68d5c2bd78bbb01d979b94. It
|
||||||
introduced regression causing FreeIPA's tests to fail.
|
introduced regression causing FreeIPA's tests to fail.
|
||||||
|
|
|
||||||
|
|
@ -1,500 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Victor Stinner <vstinner@python.org>
|
|
||||||
Date: Fri, 15 Dec 2023 16:10:40 +0100
|
|
||||||
Subject: [PATCH] 00415: [CVE-2023-27043] gh-102988: Reject malformed addresses
|
|
||||||
in email.parseaddr() (#111116)
|
|
||||||
|
|
||||||
Detect email address parsing errors and return empty tuple to
|
|
||||||
indicate the parsing error (old API). Add an optional 'strict'
|
|
||||||
parameter to getaddresses() and parseaddr() functions. Patch by
|
|
||||||
Thomas Dwyer.
|
|
||||||
|
|
||||||
Co-Authored-By: Thomas Dwyer <github@tomd.tel>
|
|
||||||
---
|
|
||||||
Doc/library/email.utils.rst | 19 +-
|
|
||||||
Lib/email/utils.py | 151 ++++++++++++-
|
|
||||||
Lib/test/test_email/test_email.py | 204 +++++++++++++++++-
|
|
||||||
...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 +
|
|
||||||
4 files changed, 361 insertions(+), 21 deletions(-)
|
|
||||||
create mode 100644 Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst
|
|
||||||
|
|
||||||
diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
|
|
||||||
index 0e266b6a45..6723dc4f13 100644
|
|
||||||
--- 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)
|
|
||||||
|
|
||||||
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
|
|
||||||
index cfdfeb3f1a..9522341fab 100644
|
|
||||||
--- a/Lib/email/utils.py
|
|
||||||
+++ b/Lib/email/utils.py
|
|
||||||
@@ -48,6 +48,7 @@
|
|
||||||
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 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
|
|
||||||
+
|
|
||||||
+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
|
|
||||||
+
|
|
||||||
+ 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):
|
|
||||||
@@ -205,16 +321,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]
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
|
|
||||||
index 8b16cca9bf..5b19bb38f6 100644
|
|
||||||
--- a/Lib/test/test_email/test_email.py
|
|
||||||
+++ b/Lib/test/test_email/test_email.py
|
|
||||||
@@ -16,6 +16,7 @@
|
|
||||||
|
|
||||||
import email
|
|
||||||
import email.policy
|
|
||||||
+import email.utils
|
|
||||||
|
|
||||||
from email.charset import Charset
|
|
||||||
from email.generator import Generator, DecodedGenerator, BytesGenerator
|
|
||||||
@@ -3288,15 +3289,154 @@ def test_getaddresses(self):
|
|
||||||
[('Al Person', 'aperson@dom.ain'),
|
|
||||||
('Bud Person', 'bperson@dom.ain')])
|
|
||||||
|
|
||||||
+ def test_getaddresses_comma_in_name(self):
|
|
||||||
+ """GH-106669 regression test."""
|
|
||||||
+ self.assertEqual(
|
|
||||||
+ utils.getaddresses(
|
|
||||||
+ [
|
|
||||||
+ '"Bud, Person" <bperson@dom.ain>',
|
|
||||||
+ 'aperson@dom.ain (Al Person)',
|
|
||||||
+ '"Mariusz Felisiak" <to@example.com>',
|
|
||||||
+ ]
|
|
||||||
+ ),
|
|
||||||
+ [
|
|
||||||
+ ('Bud, Person', 'bperson@dom.ain'),
|
|
||||||
+ ('Al Person', 'aperson@dom.ain'),
|
|
||||||
+ ('Mariusz Felisiak', 'to@example.com'),
|
|
||||||
+ ],
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+ 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"""
|
|
||||||
@@ -3485,6 +3625,54 @@ def test_mime_classes_policy_argument(self):
|
|
||||||
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):
|
|
||||||
diff --git a/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst b/Misc/NEWS.d/next/Library/2023-10-20-15-28-08.gh-issue-102988.dStNO7.rst
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000000..3d0e9e4078
|
|
||||||
--- /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.
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
|
||||||
Date: Tue, 5 Dec 2023 21:02:06 +0100
|
|
||||||
Subject: [PATCH] 00419: gh-112769: test_zlib: Fix comparison of
|
|
||||||
ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774)
|
|
||||||
|
|
||||||
zlib-ng defines the version as "1.3.0.zlib-ng".
|
|
||||||
(cherry picked from commit d384813ff18b33280a90b6d2011654528a2b6ad1)
|
|
||||||
---
|
|
||||||
Lib/test/test_zlib.py | 28 ++++++++++++++++------------
|
|
||||||
1 file changed, 16 insertions(+), 12 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
|
|
||||||
index cb0610837b..98cac4fb91 100644
|
|
||||||
--- a/Lib/test/test_zlib.py
|
|
||||||
+++ b/Lib/test/test_zlib.py
|
|
||||||
@@ -19,6 +19,20 @@
|
|
||||||
'requires Decompress.copy()')
|
|
||||||
|
|
||||||
|
|
||||||
+def _zlib_runtime_version_tuple(zlib_version=zlib.ZLIB_RUNTIME_VERSION):
|
|
||||||
+ # Register "1.2.3" as "1.2.3.0"
|
|
||||||
+ # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux"
|
|
||||||
+ v = zlib_version.split('-', 1)[0].split('.')
|
|
||||||
+ if len(v) < 4:
|
|
||||||
+ v.append('0')
|
|
||||||
+ elif not v[-1].isnumeric():
|
|
||||||
+ v[-1] = '0'
|
|
||||||
+ return tuple(map(int, v))
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ZLIB_RUNTIME_VERSION_TUPLE = _zlib_runtime_version_tuple()
|
|
||||||
+
|
|
||||||
+
|
|
||||||
class VersionTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_library_version(self):
|
|
||||||
@@ -445,9 +459,8 @@ def test_flushes(self):
|
|
||||||
sync_opt = ['Z_NO_FLUSH', 'Z_SYNC_FLUSH', 'Z_FULL_FLUSH',
|
|
||||||
'Z_PARTIAL_FLUSH']
|
|
||||||
|
|
||||||
- ver = tuple(int(v) for v in zlib.ZLIB_RUNTIME_VERSION.split('.'))
|
|
||||||
# Z_BLOCK has a known failure prior to 1.2.5.3
|
|
||||||
- if ver >= (1, 2, 5, 3):
|
|
||||||
+ if ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 5, 3):
|
|
||||||
sync_opt.append('Z_BLOCK')
|
|
||||||
|
|
||||||
sync_opt = [getattr(zlib, opt) for opt in sync_opt
|
|
||||||
@@ -776,16 +789,7 @@ def test_large_unconsumed_tail(self, size):
|
|
||||||
|
|
||||||
def test_wbits(self):
|
|
||||||
# wbits=0 only supported since zlib v1.2.3.5
|
|
||||||
- # Register "1.2.3" as "1.2.3.0"
|
|
||||||
- # or "1.2.0-linux","1.2.0.f","1.2.0.f-linux"
|
|
||||||
- v = zlib.ZLIB_RUNTIME_VERSION.split('-', 1)[0].split('.')
|
|
||||||
- if len(v) < 4:
|
|
||||||
- v.append('0')
|
|
||||||
- elif not v[-1].isnumeric():
|
|
||||||
- v[-1] = '0'
|
|
||||||
-
|
|
||||||
- v = tuple(map(int, v))
|
|
||||||
- supports_wbits_0 = v >= (1, 2, 3, 5)
|
|
||||||
+ supports_wbits_0 = ZLIB_RUNTIME_VERSION_TUPLE >= (1, 2, 3, 5)
|
|
||||||
|
|
||||||
co = zlib.compressobj(level=1, wbits=15)
|
|
||||||
zlib15 = co.compress(HAMLET_SCENE) + co.flush()
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Mon, 31 Mar 2025 20:29:04 +0200
|
||||||
|
Subject: 00452: Properly apply exported CFLAGS for dtrace/systemtap builds
|
||||||
|
|
||||||
|
When using --with-dtrace the resulting object file could be missing
|
||||||
|
specific CFLAGS exported by the build system due to the systemtap
|
||||||
|
script using specific defaults.
|
||||||
|
|
||||||
|
Exporting the CC and CFLAGS variables before the dtrace invocation
|
||||||
|
allows us to properly apply CFLAGS exported by the build system
|
||||||
|
even when cross-compiling.
|
||||||
|
|
||||||
|
Co-authored-by: stratakis <cstratak@redhat.com>
|
||||||
|
---
|
||||||
|
Makefile.pre.in | 4 ++--
|
||||||
|
.../next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst | 2 ++
|
||||||
|
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst
|
||||||
|
|
||||||
|
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
||||||
|
index fa99dd86c4..e9ba702a5c 100644
|
||||||
|
--- a/Makefile.pre.in
|
||||||
|
+++ b/Makefile.pre.in
|
||||||
|
@@ -1001,7 +1001,7 @@ Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_externa
|
||||||
|
# an include guard, so we can't use a pipeline to transform its output.
|
||||||
|
Include/pydtrace_probes.h: $(srcdir)/Include/pydtrace.d
|
||||||
|
$(MKDIR_P) Include
|
||||||
|
- $(DTRACE) $(DFLAGS) -o $@ -h -s $<
|
||||||
|
+ CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -h -s $<
|
||||||
|
: sed in-place edit with POSIX-only tools
|
||||||
|
sed 's/PYTHON_/PyDTrace_/' $@ > $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
@@ -1011,7 +1011,7 @@ Python/import.o: $(srcdir)/Include/pydtrace.h
|
||||||
|
Modules/gcmodule.o: $(srcdir)/Include/pydtrace.h
|
||||||
|
|
||||||
|
Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS)
|
||||||
|
- $(DTRACE) $(DFLAGS) -o $@ -G -s $< $(DTRACE_DEPS)
|
||||||
|
+ CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $< $(DTRACE_DEPS)
|
||||||
|
|
||||||
|
Objects/typeobject.o: Objects/typeslots.inc
|
||||||
|
|
||||||
|
diff --git a/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst b/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..a287e0b228
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Build/2025-03-31-19-22-41.gh-issue-131865.PIJy7X.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+The DTrace build now properly passes the ``CC`` and ``CFLAGS`` variables
|
||||||
|
+to the ``dtrace`` command when utilizing SystemTap on Linux.
|
||||||
214
00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
Normal file
214
00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: yevgeny hong <hongyevgeny@gmail.com>
|
||||||
|
Date: Tue, 26 Mar 2024 16:45:43 +0900
|
||||||
|
Subject: 00462: Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
|
|
||||||
|
Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and
|
||||||
|
SSL_read_ex(), but did not update handling of the return value.
|
||||||
|
|
||||||
|
Change error handling so that the return value is not examined.
|
||||||
|
OSError (not EOF) is now returned when retval is 0.
|
||||||
|
|
||||||
|
This resolves the issue of failing tests when a system is
|
||||||
|
stressed on OpenSSL 3.5.
|
||||||
|
|
||||||
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/test/test_ssl.py | 41 +++++++---------
|
||||||
|
...-02-18-09-50-31.gh-issue-115627.HGchj0.rst | 2 +
|
||||||
|
Modules/_ssl.c | 48 +++++++------------
|
||||||
|
3 files changed, 38 insertions(+), 53 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
|
||||||
|
index f4bba8ff03..f772bd6ab6 100644
|
||||||
|
--- a/Lib/test/test_ssl.py
|
||||||
|
+++ b/Lib/test/test_ssl.py
|
||||||
|
@@ -2607,16 +2607,18 @@ def run(self):
|
||||||
|
self.write(msg.lower())
|
||||||
|
except OSError as e:
|
||||||
|
# handles SSLError and socket errors
|
||||||
|
+ if isinstance(e, ConnectionError):
|
||||||
|
+ # OpenSSL 1.1.1 sometimes raises
|
||||||
|
+ # ConnectionResetError when connection is not
|
||||||
|
+ # shut down gracefully.
|
||||||
|
+ if self.server.chatty and support.verbose:
|
||||||
|
+ print(f" Connection reset by peer: {self.addr}")
|
||||||
|
+
|
||||||
|
+ self.close()
|
||||||
|
+ self.running = False
|
||||||
|
+ return
|
||||||
|
if self.server.chatty and support.verbose:
|
||||||
|
- if isinstance(e, ConnectionError):
|
||||||
|
- # OpenSSL 1.1.1 sometimes raises
|
||||||
|
- # ConnectionResetError when connection is not
|
||||||
|
- # shut down gracefully.
|
||||||
|
- print(
|
||||||
|
- f" Connection reset by peer: {self.addr}"
|
||||||
|
- )
|
||||||
|
- else:
|
||||||
|
- handle_error("Test server failure:\n")
|
||||||
|
+ handle_error("Test server failure:\n")
|
||||||
|
try:
|
||||||
|
self.write(b"ERROR\n")
|
||||||
|
except OSError:
|
||||||
|
@@ -3298,23 +3300,16 @@ def test_wrong_cert_tls13(self):
|
||||||
|
client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=hostname,
|
||||||
|
suppress_ragged_eofs=False) as s:
|
||||||
|
- # TLS 1.3 perform client cert exchange after handshake
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
- try:
|
||||||
|
+ with self.assertRaisesRegex(
|
||||||
|
+ OSError,
|
||||||
|
+ 'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA|closed by the remote host|Connection reset by peer'
|
||||||
|
+ ):
|
||||||
|
+ # TLS 1.3 perform client cert exchange after handshake
|
||||||
|
s.write(b'data')
|
||||||
|
s.read(1000)
|
||||||
|
s.write(b'should have failed already')
|
||||||
|
s.read(1000)
|
||||||
|
- except ssl.SSLError as e:
|
||||||
|
- if support.verbose:
|
||||||
|
- sys.stdout.write("\nSSLError is %r\n" % e)
|
||||||
|
- except OSError as e:
|
||||||
|
- if e.errno != errno.ECONNRESET:
|
||||||
|
- raise
|
||||||
|
- if support.verbose:
|
||||||
|
- sys.stdout.write("\nsocket.error is %r\n" % e)
|
||||||
|
- else:
|
||||||
|
- self.fail("Use of invalid cert should have failed!")
|
||||||
|
|
||||||
|
def test_rude_shutdown(self):
|
||||||
|
"""A brutal shutdown of an SSL server should raise an OSError
|
||||||
|
@@ -4560,8 +4555,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
|
||||||
|
# test sometimes fails with EOF error. Test passes as long as
|
||||||
|
# server aborts connection with an error.
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
- ssl.SSLError,
|
||||||
|
- '(certificate required|EOF occurred)'
|
||||||
|
+ OSError,
|
||||||
|
+ 'certificate required|EOF occurred|closed by the remote host|Connection reset by peer'
|
||||||
|
):
|
||||||
|
# receive CertificateRequest
|
||||||
|
data = s.recv(1024)
|
||||||
|
diff --git a/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..75d926ab59
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-02-18-09-50-31.gh-issue-115627.HGchj0.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Fix the :mod:`ssl` module error handling of connection terminate by peer.
|
||||||
|
+It now throws an OSError with the appropriate error code instead of an EOFError.
|
||||||
|
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
|
||||||
|
index af770c7f18..7b3cf61474 100644
|
||||||
|
--- a/Modules/_ssl.c
|
||||||
|
+++ b/Modules/_ssl.c
|
||||||
|
@@ -582,7 +582,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
-PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
+PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno)
|
||||||
|
{
|
||||||
|
PyObject *type;
|
||||||
|
char *errstr = NULL;
|
||||||
|
@@ -595,7 +595,6 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
_sslmodulestate *state = get_state_sock(sslsock);
|
||||||
|
type = state->PySSLErrorObject;
|
||||||
|
|
||||||
|
- assert(ret <= 0);
|
||||||
|
e = ERR_peek_last_error();
|
||||||
|
|
||||||
|
if (sslsock->ssl != NULL) {
|
||||||
|
@@ -628,32 +627,21 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
{
|
||||||
|
if (e == 0) {
|
||||||
|
- PySocketSockObject *s = GET_SOCKET(sslsock);
|
||||||
|
- if (ret == 0 || (((PyObject *)s) == Py_None)) {
|
||||||
|
+ /* underlying BIO reported an I/O error */
|
||||||
|
+ ERR_clear_error();
|
||||||
|
+#ifdef MS_WINDOWS
|
||||||
|
+ if (err.ws) {
|
||||||
|
+ return PyErr_SetFromWindowsErr(err.ws);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ if (err.c) {
|
||||||
|
+ errno = err.c;
|
||||||
|
+ return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
+ }
|
||||||
|
+ else {
|
||||||
|
p = PY_SSL_ERROR_EOF;
|
||||||
|
type = state->PySSLEOFErrorObject;
|
||||||
|
errstr = "EOF occurred in violation of protocol";
|
||||||
|
- } else if (s && ret == -1) {
|
||||||
|
- /* underlying BIO reported an I/O error */
|
||||||
|
- ERR_clear_error();
|
||||||
|
-#ifdef MS_WINDOWS
|
||||||
|
- if (err.ws) {
|
||||||
|
- return PyErr_SetFromWindowsErr(err.ws);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
- if (err.c) {
|
||||||
|
- errno = err.c;
|
||||||
|
- return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
- }
|
||||||
|
- else {
|
||||||
|
- p = PY_SSL_ERROR_EOF;
|
||||||
|
- type = state->PySSLEOFErrorObject;
|
||||||
|
- errstr = "EOF occurred in violation of protocol";
|
||||||
|
- }
|
||||||
|
- } else { /* possible? */
|
||||||
|
- p = PY_SSL_ERROR_SYSCALL;
|
||||||
|
- type = state->PySSLSyscallErrorObject;
|
||||||
|
- errstr = "Some I/O error occurred";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ERR_GET_LIB(e) == ERR_LIB_SYS) {
|
||||||
|
@@ -1014,7 +1002,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
|
||||||
|
err.ssl == SSL_ERROR_WANT_WRITE);
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
if (ret < 1)
|
||||||
|
- return PySSL_SetError(self, ret, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
if (PySSL_ChainExceptions(self) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
@@ -2433,7 +2421,7 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
|
||||||
|
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
if (retval == 0)
|
||||||
|
- return PySSL_SetError(self, retval, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
if (PySSL_ChainExceptions(self) < 0)
|
||||||
|
return NULL;
|
||||||
|
return PyLong_FromSize_t(count);
|
||||||
|
@@ -2463,7 +2451,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
|
||||||
|
self->err = err;
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
- return PySSL_SetError(self, count, __FILE__, __LINE__);
|
||||||
|
+ return PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
else
|
||||||
|
return PyLong_FromLong(count);
|
||||||
|
}
|
||||||
|
@@ -2585,7 +2573,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
|
||||||
|
err.ssl == SSL_ERROR_WANT_WRITE);
|
||||||
|
|
||||||
|
if (retval == 0) {
|
||||||
|
- PySSL_SetError(self, retval, __FILE__, __LINE__);
|
||||||
|
+ PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (self->exc_type != NULL)
|
||||||
|
@@ -2709,7 +2697,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
Py_XDECREF(sock);
|
||||||
|
- PySSL_SetError(self, ret, __FILE__, __LINE__);
|
||||||
|
+ PySSL_SetError(self, __FILE__, __LINE__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (self->exc_type != NULL)
|
||||||
61
00474-cve-2025-15366.patch
Normal file
61
00474-cve-2025-15366.patch
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Seth Michael Larson <seth@python.org>
|
||||||
|
Date: Tue, 20 Jan 2026 14:45:42 -0600
|
||||||
|
Subject: 00474: CVE-2025-15366
|
||||||
|
|
||||||
|
gh-143921: Reject control characters in IMAP commands
|
||||||
|
|
||||||
|
(cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45)
|
||||||
|
---
|
||||||
|
Lib/imaplib.py | 4 +++-
|
||||||
|
Lib/test/test_imaplib.py | 6 ++++++
|
||||||
|
.../Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst | 1 +
|
||||||
|
3 files changed, 10 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
|
||||||
|
index 54122f985b..9ab8e43bbf 100644
|
||||||
|
--- a/Lib/imaplib.py
|
||||||
|
+++ b/Lib/imaplib.py
|
||||||
|
@@ -132,7 +132,7 @@
|
||||||
|
# We compile these in _mode_xxx.
|
||||||
|
_Literal = br'.*{(?P<size>\d+)}$'
|
||||||
|
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
|
||||||
|
-
|
||||||
|
+_control_chars = re.compile(b'[\x00-\x1F\x7F]')
|
||||||
|
|
||||||
|
|
||||||
|
class IMAP4:
|
||||||
|
@@ -994,6 +994,8 @@ def _command(self, name, *args):
|
||||||
|
if arg is None: continue
|
||||||
|
if isinstance(arg, str):
|
||||||
|
arg = bytes(arg, self._encoding)
|
||||||
|
+ if _control_chars.search(arg):
|
||||||
|
+ raise ValueError("Control characters not allowed in commands")
|
||||||
|
data = data + b' ' + arg
|
||||||
|
|
||||||
|
literal = self.literal
|
||||||
|
diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py
|
||||||
|
index f817707743..9ce59b3040 100644
|
||||||
|
--- a/Lib/test/test_imaplib.py
|
||||||
|
+++ b/Lib/test/test_imaplib.py
|
||||||
|
@@ -505,6 +505,12 @@ def test_login(self):
|
||||||
|
self.assertEqual(data[0], b'LOGIN completed')
|
||||||
|
self.assertEqual(client.state, 'AUTH')
|
||||||
|
|
||||||
|
+ def test_control_characters(self):
|
||||||
|
+ client, _ = self._setup(SimpleIMAPHandler)
|
||||||
|
+ for c0 in support.control_characters_c0():
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ client.login(f'user{c0}', 'pass')
|
||||||
|
+
|
||||||
|
def test_logout(self):
|
||||||
|
client, _ = self._setup(SimpleIMAPHandler)
|
||||||
|
typ, data = client.login('user', 'pass')
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..4e13fe92bc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Reject control characters in IMAP commands.
|
||||||
61
00475-cve-2025-15367.patch
Normal file
61
00475-cve-2025-15367.patch
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Seth Michael Larson <seth@python.org>
|
||||||
|
Date: Tue, 20 Jan 2026 14:46:32 -0600
|
||||||
|
Subject: 00475: CVE-2025-15367
|
||||||
|
|
||||||
|
gh-143923: Reject control characters in POP3 commands
|
||||||
|
|
||||||
|
(cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7)
|
||||||
|
---
|
||||||
|
Lib/poplib.py | 2 ++
|
||||||
|
Lib/test/test_poplib.py | 8 ++++++++
|
||||||
|
.../2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst | 1 +
|
||||||
|
3 files changed, 11 insertions(+)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/poplib.py b/Lib/poplib.py
|
||||||
|
index 0f8587317c..f563030f7f 100644
|
||||||
|
--- a/Lib/poplib.py
|
||||||
|
+++ b/Lib/poplib.py
|
||||||
|
@@ -122,6 +122,8 @@ def _putline(self, line):
|
||||||
|
def _putcmd(self, line):
|
||||||
|
if self._debugging: print('*cmd*', repr(line))
|
||||||
|
line = bytes(line, self.encoding)
|
||||||
|
+ if re.search(b'[\x00-\x1F\x7F]', line):
|
||||||
|
+ raise ValueError('Control characters not allowed in commands')
|
||||||
|
self._putline(line)
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py
|
||||||
|
index 1220ca32ef..2655e366fd 100644
|
||||||
|
--- a/Lib/test/test_poplib.py
|
||||||
|
+++ b/Lib/test/test_poplib.py
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
import unittest
|
||||||
|
from unittest import TestCase, skipUnless
|
||||||
|
from test import support as test_support
|
||||||
|
+from test.support import control_characters_c0
|
||||||
|
from test.support import hashlib_helper
|
||||||
|
from test.support import socket_helper
|
||||||
|
from test.support import threading_helper
|
||||||
|
@@ -365,6 +366,13 @@ def test_quit(self):
|
||||||
|
self.assertIsNone(self.client.sock)
|
||||||
|
self.assertIsNone(self.client.file)
|
||||||
|
|
||||||
|
+ def test_control_characters(self):
|
||||||
|
+ for c0 in control_characters_c0():
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ self.client.user(f'user{c0}')
|
||||||
|
+ with self.assertRaises(ValueError):
|
||||||
|
+ self.client.pass_(f'{c0}pass')
|
||||||
|
+
|
||||||
|
@requires_ssl
|
||||||
|
def test_stls_capa(self):
|
||||||
|
capa = self.client.capa()
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..3cde4df3e0
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Reject control characters in POP3 commands.
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Karolina Surma <ksurma@redhat.com>
|
||||||
|
Date: Fri, 14 Aug 2026 09:38:26 +0200
|
||||||
|
Subject: 00494: Increase the timeout of test_large_content_length_truncated
|
||||||
|
|
||||||
|
It has started to fail randomly when run on s390x architecture.
|
||||||
|
---
|
||||||
|
Lib/test/test_httpservers.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
|
||||||
|
index 5eb3c82fbd..aa8ad1c323 100644
|
||||||
|
--- a/Lib/test/test_httpservers.py
|
||||||
|
+++ b/Lib/test/test_httpservers.py
|
||||||
|
@@ -872,7 +872,7 @@ def test_large_content_length(self):
|
||||||
|
self.assertEqual(res.read(), b'%d %d' % (size, size) + self.linesep)
|
||||||
|
|
||||||
|
def test_large_content_length_truncated(self):
|
||||||
|
- with support.swap_attr(self.request_handler, 'timeout', 0.001):
|
||||||
|
+ with support.swap_attr(self.request_handler, 'timeout', support.LOOPBACK_TIMEOUT):
|
||||||
|
for w in range(18, 65):
|
||||||
|
size = 1 << w
|
||||||
|
headers = {'Content-Length' : str(size)}
|
||||||
65
plan.fmf
Normal file
65
plan.fmf
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
execute:
|
||||||
|
how: tmt
|
||||||
|
|
||||||
|
provision:
|
||||||
|
hardware:
|
||||||
|
memory: '>= 3 GB'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
pybasever: '3.10'
|
||||||
|
|
||||||
|
discover:
|
||||||
|
- name: tests_python
|
||||||
|
how: shell
|
||||||
|
url: https://src.fedoraproject.org/tests/python.git
|
||||||
|
tests:
|
||||||
|
- name: smoke
|
||||||
|
path: /smoke
|
||||||
|
test: "VERSION=${pybasever} ./venv.sh"
|
||||||
|
- name: smoke_virtualenv
|
||||||
|
path: /smoke
|
||||||
|
test: "VERSION=${pybasever} METHOD=virtualenv ./venv.sh"
|
||||||
|
- name: debugsmoke
|
||||||
|
path: /smoke
|
||||||
|
test: "PYTHON=python${pybasever}d TOX=false VERSION=${pybasever} ./venv.sh"
|
||||||
|
- name: selftest
|
||||||
|
path: /selftest
|
||||||
|
test: "VERSION=${pybasever} X='' ./parallel.sh"
|
||||||
|
- name: debugtest
|
||||||
|
path: /selftest
|
||||||
|
test: "VERSION=${pybasever} PYTHON=python${pybasever}d X='' ./parallel.sh"
|
||||||
|
- name: debugflags
|
||||||
|
path: /flags
|
||||||
|
test: "python${pybasever}d ./assertflags.py -O0"
|
||||||
|
- name: marshalparser
|
||||||
|
path: /marshalparser
|
||||||
|
test: "VERSION=${pybasever} SAMPLE=10 ./test_marshalparser_compatibility.sh"
|
||||||
|
- name: required_symbols
|
||||||
|
path: /required-symbols
|
||||||
|
test: "VERSION=${pybasever} ./check.sh"
|
||||||
|
|
||||||
|
prepare:
|
||||||
|
- name: Install dependencies
|
||||||
|
how: install
|
||||||
|
package:
|
||||||
|
- gcc # for extension building in venv and selftest
|
||||||
|
- gdb # for test_gdb
|
||||||
|
- "python${pybasever}" # the test subject
|
||||||
|
- "python${pybasever}-debug" # for leak testing
|
||||||
|
- "python${pybasever}-devel" # for extension building in venv and selftest
|
||||||
|
- "python${pybasever}-tkinter" # for selftest
|
||||||
|
- "python${pybasever}-test" # for selftest
|
||||||
|
- tox # for venv tests
|
||||||
|
- virtualenv # for virtualenv tests
|
||||||
|
- glibc-all-langpacks # for locale tests
|
||||||
|
- marshalparser # for testing compatibility (magic numbers) with marshalparser
|
||||||
|
- binutils # for nm (symbol inspection)
|
||||||
|
- rpm # for debugging
|
||||||
|
- dnf # for upgrade and downgrade
|
||||||
|
- name: Update packages
|
||||||
|
how: shell
|
||||||
|
script: dnf upgrade -y
|
||||||
|
- name: rpm_qa
|
||||||
|
order: 100
|
||||||
|
how: shell
|
||||||
|
script: rpm -qa | sort | tee $TMT_PLAN_DATA/rpmqa.txt
|
||||||
186
python3.10.spec
186
python3.10.spec
|
|
@ -13,11 +13,11 @@ URL: https://www.python.org/
|
||||||
|
|
||||||
# WARNING When rebasing to a new Python version,
|
# WARNING When rebasing to a new Python version,
|
||||||
# remember to update the python3-docs package as well
|
# remember to update the python3-docs package as well
|
||||||
%global general_version %{pybasever}.14
|
%global general_version %{pybasever}.21
|
||||||
#global prerel ...
|
#global prerel ...
|
||||||
%global upstream_version %{general_version}%{?prerel}
|
%global upstream_version %{general_version}%{?prerel}
|
||||||
Version: %{general_version}%{?prerel:~%{prerel}}
|
Version: %{general_version}%{?prerel:~%{prerel}}
|
||||||
Release: 2%{?dist}
|
Release: 1%{?dist}
|
||||||
License: Python-2.0.1
|
License: Python-2.0.1
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ License: Python-2.0.1
|
||||||
# from Python with the versions below.
|
# from Python with the versions below.
|
||||||
# This needs to be manually updated when we update Python.
|
# This needs to be manually updated when we update Python.
|
||||||
%global pip_version 23.0.1
|
%global pip_version 23.0.1
|
||||||
%global setuptools_version 65.5.0
|
%global setuptools_version 79.0.1
|
||||||
|
|
||||||
# Expensive optimizations (mainly, profile-guided optimizations)
|
# Expensive optimizations (mainly, profile-guided optimizations)
|
||||||
%bcond_without optimizations
|
%bcond_without optimizations
|
||||||
|
|
@ -192,8 +192,7 @@ BuildRequires: bluez-libs-devel
|
||||||
BuildRequires: bzip2
|
BuildRequires: bzip2
|
||||||
BuildRequires: bzip2-devel
|
BuildRequires: bzip2-devel
|
||||||
BuildRequires: desktop-file-utils
|
BuildRequires: desktop-file-utils
|
||||||
# See the runtime requirement in the -libs subpackage
|
BuildRequires: expat-devel
|
||||||
BuildRequires: expat-devel >= 2.6
|
|
||||||
|
|
||||||
BuildRequires: findutils
|
BuildRequires: findutils
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
|
|
@ -211,12 +210,12 @@ BuildRequires: libnsl2-devel
|
||||||
BuildRequires: libtirpc-devel
|
BuildRequires: libtirpc-devel
|
||||||
BuildRequires: libGL-devel
|
BuildRequires: libGL-devel
|
||||||
BuildRequires: libuuid-devel
|
BuildRequires: libuuid-devel
|
||||||
|
BuildRequires: libxcrypt-devel
|
||||||
BuildRequires: libX11-devel
|
BuildRequires: libX11-devel
|
||||||
BuildRequires: make
|
BuildRequires: make
|
||||||
BuildRequires: mpdecimal-devel
|
BuildRequires: mpdecimal-devel
|
||||||
BuildRequires: ncurses-devel
|
BuildRequires: ncurses-devel
|
||||||
|
|
||||||
BuildRequires: openssl-devel
|
|
||||||
BuildRequires: pkgconfig
|
BuildRequires: pkgconfig
|
||||||
BuildRequires: readline-devel
|
BuildRequires: readline-devel
|
||||||
BuildRequires: redhat-rpm-config >= 127
|
BuildRequires: redhat-rpm-config >= 127
|
||||||
|
|
@ -224,11 +223,15 @@ BuildRequires: sqlite-devel
|
||||||
BuildRequires: gdb
|
BuildRequires: gdb
|
||||||
|
|
||||||
BuildRequires: tar
|
BuildRequires: tar
|
||||||
BuildRequires: tcl-devel
|
BuildRequires: tcl-devel < 1:9
|
||||||
BuildRequires: tix-devel
|
BuildRequires: tix-devel
|
||||||
BuildRequires: tk-devel
|
BuildRequires: tk-devel < 1:9
|
||||||
BuildRequires: tzdata
|
BuildRequires: tzdata
|
||||||
|
|
||||||
|
# Support for OpenSSL 4 only landed in Python 3.15 for now
|
||||||
|
# https://github.com/python/cpython/issues/146207
|
||||||
|
BuildRequires: (openssl-devel < 1:4 or openssl3-devel)
|
||||||
|
|
||||||
%if %{with valgrind}
|
%if %{with valgrind}
|
||||||
BuildRequires: valgrind-devel
|
BuildRequires: valgrind-devel
|
||||||
%endif
|
%endif
|
||||||
|
|
@ -236,6 +239,7 @@ BuildRequires: valgrind-devel
|
||||||
BuildRequires: xz-devel
|
BuildRequires: xz-devel
|
||||||
BuildRequires: zlib-devel
|
BuildRequires: zlib-devel
|
||||||
|
|
||||||
|
BuildRequires: systemtap-sdt-devel
|
||||||
BuildRequires: /usr/bin/dtrace
|
BuildRequires: /usr/bin/dtrace
|
||||||
|
|
||||||
# workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig)
|
# workaround http://bugs.python.org/issue19804 (test_uuid requires ifconfig)
|
||||||
|
|
@ -244,6 +248,9 @@ BuildRequires: /usr/sbin/ifconfig
|
||||||
%if %{with rpmwheels}
|
%if %{with rpmwheels}
|
||||||
BuildRequires: %{python_wheel_pkg_prefix}-setuptools-wheel
|
BuildRequires: %{python_wheel_pkg_prefix}-setuptools-wheel
|
||||||
BuildRequires: %{python_wheel_pkg_prefix}-pip-wheel
|
BuildRequires: %{python_wheel_pkg_prefix}-pip-wheel
|
||||||
|
%else
|
||||||
|
# For %%python_wheel_inject_sbom
|
||||||
|
BuildRequires: python-rpm-macros
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if %{without bootstrap}
|
%if %{without bootstrap}
|
||||||
|
|
@ -285,6 +292,7 @@ Source11: idle3.appdata.xml
|
||||||
|
|
||||||
# 00001 # d06a8853cf4bae9e115f45e1d531d2dc152c5cc8
|
# 00001 # d06a8853cf4bae9e115f45e1d531d2dc152c5cc8
|
||||||
# Fixup distutils/unixccompiler.py to remove standard library path from rpath
|
# Fixup distutils/unixccompiler.py to remove standard library path from rpath
|
||||||
|
#
|
||||||
# Was Patch0 in ivazquez' python3000 specfile
|
# Was Patch0 in ivazquez' python3000 specfile
|
||||||
Patch1: 00001-rpath.patch
|
Patch1: 00001-rpath.patch
|
||||||
|
|
||||||
|
|
@ -320,20 +328,52 @@ Patch251: 00251-change-user-install-location.patch
|
||||||
# https://github.com/GrahamDumpleton/mod_wsgi/issues/730
|
# https://github.com/GrahamDumpleton/mod_wsgi/issues/730
|
||||||
Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch
|
Patch371: 00371-revert-bpo-1596321-fix-threading-_shutdown-for-the-main-thread-gh-28549-gh-28589.patch
|
||||||
|
|
||||||
# 00415 # 9ca4533e0b4a03d919953017026f66c6a060756e
|
# 00452 # eb11d070c5af7d1b5e47f4e02186152d08eaf793
|
||||||
# [CVE-2023-27043] gh-102988: Reject malformed addresses in email.parseaddr() (#111116)
|
# Properly apply exported CFLAGS for dtrace/systemtap builds
|
||||||
#
|
#
|
||||||
# Detect email address parsing errors and return empty tuple to
|
# When using --with-dtrace the resulting object file could be missing
|
||||||
# indicate the parsing error (old API). Add an optional 'strict'
|
# specific CFLAGS exported by the build system due to the systemtap
|
||||||
# parameter to getaddresses() and parseaddr() functions. Patch by
|
# script using specific defaults.
|
||||||
# Thomas Dwyer.
|
#
|
||||||
Patch415: 00415-cve-2023-27043-gh-102988-reject-malformed-addresses-in-email-parseaddr-111116.patch
|
# Exporting the CC and CFLAGS variables before the dtrace invocation
|
||||||
|
# allows us to properly apply CFLAGS exported by the build system
|
||||||
|
# even when cross-compiling.
|
||||||
|
Patch452: 00452-properly-apply-exported-cflags-for-dtrace-systemtap-builds.patch
|
||||||
|
|
||||||
# 00419 # f13682530cc7e4daec2e40acd56508846fdd3aad
|
# 00462 # f0db87ee65704fa5545ea25f2cca8c43fc639fab
|
||||||
# gh-112769: test_zlib: Fix comparison of ZLIB_RUNTIME_VERSION with non-int suffix (GH-112771) (GH-112774)
|
# Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
#
|
#
|
||||||
# zlib-ng defines the version as "1.3.0.zlib-ng".
|
# Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and
|
||||||
Patch419: 00419-gh-112769-test_zlib-fix-comparison-of-zlib_runtime_version-with-non-int-suffix-gh-112771-gh-112774.patch
|
# SSL_read_ex(), but did not update handling of the return value.
|
||||||
|
#
|
||||||
|
# Change error handling so that the return value is not examined.
|
||||||
|
# OSError (not EOF) is now returned when retval is 0.
|
||||||
|
#
|
||||||
|
# This resolves the issue of failing tests when a system is
|
||||||
|
# stressed on OpenSSL 3.5.
|
||||||
|
Patch462: 00462-fix-pyssl_seterror-handling-ssl_error_syscall.patch
|
||||||
|
|
||||||
|
# 00474 # 837ddca0372fa87ff9cee47142200caa21e77def
|
||||||
|
# CVE-2025-15366
|
||||||
|
#
|
||||||
|
# gh-143921: Reject control characters in IMAP commands
|
||||||
|
#
|
||||||
|
# (cherry-picked from commit 6262704b134db2a4ba12e85ecfbd968534f28b45)
|
||||||
|
Patch474: 00474-cve-2025-15366.patch
|
||||||
|
|
||||||
|
# 00475 # 3748209a316662d4e85981ca1a7418547a1d25c6
|
||||||
|
# CVE-2025-15367
|
||||||
|
#
|
||||||
|
# gh-143923: Reject control characters in POP3 commands
|
||||||
|
#
|
||||||
|
# (cherry-picked from commit b234a2b67539f787e191d2ef19a7cbdce32874e7)
|
||||||
|
Patch475: 00475-cve-2025-15367.patch
|
||||||
|
|
||||||
|
# 00494 # 430aab133397ed44cc9ee621fd311e02fee317b5
|
||||||
|
# Increase the timeout of test_large_content_length_truncated
|
||||||
|
#
|
||||||
|
# It has started to fail randomly when run on s390x architecture.
|
||||||
|
Patch494: 00494-increase-the-timeout-of-test_large_content_length_truncated.patch
|
||||||
|
|
||||||
# (New patches go here ^^^)
|
# (New patches go here ^^^)
|
||||||
#
|
#
|
||||||
|
|
@ -481,12 +521,24 @@ Recommends: (%{pkgname}-tkinter%{?_isa} = %{version}-%{release} if tk%{?_isa})
|
||||||
Requires: tzdata
|
Requires: tzdata
|
||||||
|
|
||||||
# The requirement on libexpat is generated, but we need to version it.
|
# The requirement on libexpat is generated, but we need to version it.
|
||||||
# When built with expat >= 2.6, but installed with older expat, we get:
|
# When built with a specific expat version, but installed with an older one,
|
||||||
|
# we sometimes get:
|
||||||
# ImportError: /usr/lib64/python3.X/lib-dynload/pyexpat.cpython-....so:
|
# ImportError: /usr/lib64/python3.X/lib-dynload/pyexpat.cpython-....so:
|
||||||
# undefined symbol: XML_SetReparseDeferralEnabled
|
# undefined symbol: XML_...
|
||||||
|
# The pyexpat module has build-time checks for expat version to only use the
|
||||||
|
# available symbols. However, there is no runtime protection, so when the module
|
||||||
|
# is later installed with an older expat, it may error due to undefined symbols.
|
||||||
# This breaks many things, including python -m venv.
|
# This breaks many things, including python -m venv.
|
||||||
|
# We avoid this problem by requiring at least the same version of expat that
|
||||||
|
# was used during the build time.
|
||||||
# Other subpackages (like -debug) also need this, but they all depend on -libs.
|
# Other subpackages (like -debug) also need this, but they all depend on -libs.
|
||||||
Requires: expat >= 2.6
|
# Since expat 2.7.4, the library has versioned symbols and this is no longer needed,
|
||||||
|
# as the generated requirement will be in the form of libexpat.so.1(LIBEXPAT_2.7.2) etc.
|
||||||
|
%global expat_version %(LANG=C rpm -q --qf '%%{version}' expat.%{_target_cpu} | sed 's/.*not installed/0/')
|
||||||
|
%if v"%{expat_version}" < v"2.7.4"
|
||||||
|
Requires: expat%{?_isa} >= %{expat_version}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
# Since patch 251 changed from distutils to sysconfig, pip needed to be adapted
|
# Since patch 251 changed from distutils to sysconfig, pip needed to be adapted
|
||||||
# The previous versions could cause serious bugs during `sudo pip install --upgrade ...`
|
# The previous versions could cause serious bugs during `sudo pip install --upgrade ...`
|
||||||
|
|
@ -1051,6 +1103,11 @@ for file in %{buildroot}%{pylibdir}/pydoc_data/topics.py $(grep --include='*.py'
|
||||||
rm ${directory}/{__pycache__/${module}.cpython-%{pyshortver}.opt-?.pyc,${module}.py}
|
rm ${directory}/{__pycache__/${module}.cpython-%{pyshortver}.opt-?.pyc,${module}.py}
|
||||||
done
|
done
|
||||||
|
|
||||||
|
%if %{without rpmwheels}
|
||||||
|
# Inject SBOM into the installed wheels (if the macro is available)
|
||||||
|
%{?python_wheel_inject_sbom:%python_wheel_inject_sbom %{buildroot}%{pylibdir}/ensurepip/_bundled/*.whl}
|
||||||
|
%endif
|
||||||
|
|
||||||
# ======================================================
|
# ======================================================
|
||||||
# Checks for packaging issues
|
# Checks for packaging issues
|
||||||
# ======================================================
|
# ======================================================
|
||||||
|
|
@ -1109,6 +1166,8 @@ CheckPython() {
|
||||||
# test_distutils
|
# test_distutils
|
||||||
# distutils.tests.test_bdist_rpm tests fail when bootstraping the Python
|
# distutils.tests.test_bdist_rpm tests fail when bootstraping the Python
|
||||||
# package: rpmbuild requires /usr/bin/pythonX.Y to be installed
|
# package: rpmbuild requires /usr/bin/pythonX.Y to be installed
|
||||||
|
# test_sendfile_close_peer_in_the_middle_of_receiving:
|
||||||
|
# https://github.com/python/cpython/issues/120226
|
||||||
LD_LIBRARY_PATH=$ConfDir $ConfDir/python -m test.regrtest \
|
LD_LIBRARY_PATH=$ConfDir $ConfDir/python -m test.regrtest \
|
||||||
-wW --slowest -j0 --timeout=1800 \
|
-wW --slowest -j0 --timeout=1800 \
|
||||||
%if %{with bootstrap}
|
%if %{with bootstrap}
|
||||||
|
|
@ -1117,6 +1176,9 @@ CheckPython() {
|
||||||
%ifarch %{mips64}
|
%ifarch %{mips64}
|
||||||
-x test_ctypes \
|
-x test_ctypes \
|
||||||
%endif
|
%endif
|
||||||
|
%ifarch ppc64le
|
||||||
|
-i test_sendfile_close_peer_in_the_middle_of_receiving \
|
||||||
|
%endif
|
||||||
|
|
||||||
echo FINISHED: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName
|
echo FINISHED: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName
|
||||||
|
|
||||||
|
|
@ -1616,6 +1678,86 @@ CheckPython optimized
|
||||||
# ======================================================
|
# ======================================================
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Aug 13 2026 Karolina Surma <ksurma@redhat.com> - 3.10.21-1
|
||||||
|
- Update to Python 3.10.21
|
||||||
|
|
||||||
|
* Thu Jul 30 2026 Miro Hrončok <mhroncok@redhat.com> - 3.10.20-4
|
||||||
|
- Skip UDP Lite tests if it's not supported
|
||||||
|
- Fixes FTBFS on Linux kernel 7.1 and newer
|
||||||
|
|
||||||
|
* Thu Jul 16 2026 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.20-3
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild
|
||||||
|
|
||||||
|
* Sat Apr 11 2026 Miro Hrončok <mhroncok@redhat.com> - 3.10.20-2
|
||||||
|
- Explicitly build with OpenSSL 3
|
||||||
|
- Fix ssl.SSLError: [ASN1: NOT_ENOUGH_DATA] not enough data with OpenSSL 3.5.7+
|
||||||
|
|
||||||
|
* Tue Mar 03 2026 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.10.20-1
|
||||||
|
- Update to 3.10.20
|
||||||
|
|
||||||
|
* Mon Feb 09 2026 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.10.19-4
|
||||||
|
- Security fixes for CVE-2026-0865, CVE-2025-15366 and CVE-2025-15367
|
||||||
|
|
||||||
|
* Sat Jan 17 2026 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.19-3
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild
|
||||||
|
|
||||||
|
* Tue Jan 06 2026 Karolina Surma <ksurma@redhat.com> - 3.10.19-2
|
||||||
|
- Require at least the same expat version as used during the build time
|
||||||
|
|
||||||
|
* Fri Oct 10 2025 Karolina Surma <ksurma@redhat.com> - 3.10.19-1
|
||||||
|
- Update to Python 3.10.19
|
||||||
|
|
||||||
|
* Fri Jul 25 2025 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.18-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild
|
||||||
|
|
||||||
|
* Wed Jun 04 2025 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.10.18-1
|
||||||
|
- Update to 3.10.18
|
||||||
|
|
||||||
|
* Tue May 13 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.10.17-3
|
||||||
|
- Fix PySSL_SetError handling SSL_ERROR_SYSCALL
|
||||||
|
- This fixes random flakiness of test_ssl on stressed machines
|
||||||
|
|
||||||
|
* Wed Apr 16 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.10.17-2
|
||||||
|
- test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler
|
||||||
|
- Fixes: rhbz#2355052
|
||||||
|
|
||||||
|
* Wed Apr 09 2025 Miro Hrončok <mhroncok@redhat.com> - 3.10.17-1
|
||||||
|
- Update to 3.10.17
|
||||||
|
|
||||||
|
* Mon Mar 31 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.10.16-6
|
||||||
|
- Properly apply exported CFLAGS for dtrace/systemtap builds
|
||||||
|
- Fixes: rhbz#2356303
|
||||||
|
|
||||||
|
* Mon Feb 10 2025 Charalampos Stratakis <cstratak@redhat.com> - 3.10.16-5
|
||||||
|
- Security fix for CVE-2025-0938
|
||||||
|
- Fixes: rhbz#2343276
|
||||||
|
|
||||||
|
* Thu Feb 06 2025 Miro Hrončok <mhroncok@redhat.com> - 3.10.16-4
|
||||||
|
- Rebuilt with mpdecimal 4.0.0
|
||||||
|
|
||||||
|
* Sat Feb 01 2025 Björn Esser <besser82@fedoraproject.org> - 3.10.16-3
|
||||||
|
- Add explicit BR: libxcrypt-devel
|
||||||
|
|
||||||
|
* Sat Jan 18 2025 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.16-2
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild
|
||||||
|
|
||||||
|
* Wed Dec 04 2024 Charalampos Stratakis <cstratak@redhat.com> - 3.10.16-1
|
||||||
|
- Update to 3.10.16
|
||||||
|
- Security fix for CVE-2024-9287
|
||||||
|
Resolves: rhbz#2321654
|
||||||
|
|
||||||
|
* Wed Sep 11 2024 Miro Hrončok <mhroncok@redhat.com> - 3.10.15-2
|
||||||
|
- Fix ThreadedVSOCKSocketStreamTest
|
||||||
|
|
||||||
|
* Mon Sep 09 2024 Tomáš Hrnčiar <thrnciar@redhat.com> - 3.10.15-1
|
||||||
|
- Update to 3.10.15
|
||||||
|
|
||||||
|
* Tue Jul 23 2024 Lumír Balhar <lbalhar@redhat.com> - 3.10.14-4
|
||||||
|
- Require systemtap-sdt-devel for sys/sdt.h
|
||||||
|
|
||||||
|
* Fri Jul 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.14-3
|
||||||
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild
|
||||||
|
|
||||||
* Wed Apr 17 2024 Miro Hrončok <mhroncok@redhat.com> - 3.10.14-2
|
* Wed Apr 17 2024 Miro Hrončok <mhroncok@redhat.com> - 3.10.14-2
|
||||||
- Require expat >= 2.6 to prevent errors when creating venvs with older expat
|
- Require expat >= 2.6 to prevent errors when creating venvs with older expat
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
# exclude test XML data (not always valid) from XML validity check:
|
# exclude test XML data (not always valid) from XML validity check:
|
||||||
xml:
|
xml:
|
||||||
ignore:
|
ignore:
|
||||||
- /usr/lib*/python*/test/xmltestdata/*
|
- '/usr/lib*/python*/test/xmltestdata/*'
|
||||||
- /usr/lib*/python*/test/xmltestdata/*/*
|
- '/usr/lib*/python*/test/xmltestdata/*/*'
|
||||||
|
|
||||||
# exclude _socket from ipv4 only functions check, it has both ipv4 and ipv6 only
|
# exclude _socket from ipv4 only functions check, it has both ipv4 and ipv6 only
|
||||||
badfuncs:
|
badfuncs:
|
||||||
allowed:
|
allowed:
|
||||||
/usr/lib*/python*/lib-dynload/_socket.*:
|
'/usr/lib*/python*/lib-dynload/_socket.*':
|
||||||
- inet_aton
|
- inet_aton
|
||||||
- inet_ntoa
|
- inet_ntoa
|
||||||
|
|
||||||
# exclude the debug build from annocheck entirely
|
# exclude the debug build from annocheck entirely
|
||||||
annocheck:
|
annocheck:
|
||||||
ignore:
|
ignore:
|
||||||
- /usr/bin/python*d
|
- '/usr/bin/python*d'
|
||||||
- /usr/lib*/libpython*d.so.1.0
|
- '/usr/lib*/libpython*d.so.1.0'
|
||||||
- /usr/lib*/python*/lib-dynload/*.cpython-*d-*-*-*.so
|
- '/usr/lib*/python*/lib-dynload/*.cpython-*d-*-*-*.so'
|
||||||
|
|
||||||
# don't report changed content of compiled files
|
# don't report changed content of compiled files
|
||||||
# that is expected with every toolchain update and not reproducible yet
|
# that is expected with every toolchain update and not reproducible yet
|
||||||
|
|
|
||||||
4
sources
4
sources
|
|
@ -1,2 +1,2 @@
|
||||||
SHA512 (Python-3.10.14.tar.xz) = adee638aeca898743da4b8245c0fa1e9b94b516f22e716e60c99038f0bb3dcbf726317aa86656404be1719b5a7c58eb09533720ebc0badbb04bd9a534dd48fef
|
SHA512 (Python-3.10.21.tar.xz) = 6f6de7c5e4c0457f2d189ed5d111c83fb8775e19123afe4f9fd0ae2b93f3fa2bbb7ad849ade6bb5227d4a6a3d63abc8e167fbe5fb54fa715660c89fd6274daee
|
||||||
SHA512 (Python-3.10.14.tar.xz.asc) = 821046b8eb559c3ef800f26739c995e7a71469a1e765ee7d516690236e16d2637ada7b2f9535b8c0dceaacf243e7b059f6f106b4a25faae365aa2099d1ff538c
|
SHA512 (Python-3.10.21.tar.xz.asc) = 72d6aeaa0f51e527f82392cf1f969b0ad50a48fe10e8b7e5feb6117718223d00757f7b375b3ac82e257571d5a61f8162e9dd59b81fcf8f2a441c6deb42be71cb
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
standard-inventory-qcow2:
|
|
||||||
qemu:
|
|
||||||
m: 3G # Amount of VM memory
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
---
|
|
||||||
- hosts: localhost
|
|
||||||
tags:
|
|
||||||
- classic
|
|
||||||
tasks:
|
|
||||||
- dnf:
|
|
||||||
name: "*"
|
|
||||||
state: latest
|
|
||||||
|
|
||||||
- hosts: localhost
|
|
||||||
roles:
|
|
||||||
- role: standard-test-basic
|
|
||||||
tags:
|
|
||||||
- classic
|
|
||||||
repositories:
|
|
||||||
- repo: "https://src.fedoraproject.org/tests/python.git"
|
|
||||||
dest: "python"
|
|
||||||
pybasever: "3.10"
|
|
||||||
tests:
|
|
||||||
- rpm_qa:
|
|
||||||
run: rpm -qa
|
|
||||||
- smoke:
|
|
||||||
dir: python/smoke
|
|
||||||
run: "VERSION={{ pybasever }} ./venv.sh"
|
|
||||||
- smoke_virtualenv:
|
|
||||||
dir: python/smoke
|
|
||||||
run: "VERSION={{ pybasever }} METHOD=virtualenv ./venv.sh"
|
|
||||||
- debugsmoke:
|
|
||||||
dir: python/smoke
|
|
||||||
run: "PYTHON=python{{ pybasever }}d TOX=false VERSION={{ pybasever }} ./venv.sh"
|
|
||||||
- selftest:
|
|
||||||
dir: python/selftest
|
|
||||||
run: "VERSION={{ pybasever }} X='' ./parallel.sh"
|
|
||||||
- debugtest:
|
|
||||||
dir: python/selftest
|
|
||||||
run: "VERSION={{ pybasever }} PYTHON=python{{ pybasever }}d X='' ./parallel.sh"
|
|
||||||
- debugflags:
|
|
||||||
dir: python/flags
|
|
||||||
run: "python{{ pybasever }}d ./assertflags.py -O0"
|
|
||||||
- marshalparser:
|
|
||||||
dir: python/marshalparser
|
|
||||||
run: "VERSION={{ pybasever }} SAMPLE=10 test_marshalparser_compatibility.sh"
|
|
||||||
required_packages:
|
|
||||||
- gcc # for extension building in venv and selftest
|
|
||||||
- gdb # for test_gdb
|
|
||||||
- "python{{ pybasever }}" # the test subject
|
|
||||||
- "python{{ pybasever }}-debug" # for leak testing
|
|
||||||
- "python{{ pybasever }}-devel" # for extension building in venv and selftest
|
|
||||||
- "python{{ pybasever }}-tkinter" # for selftest
|
|
||||||
- "python{{ pybasever }}-test" # for selftest
|
|
||||||
- tox # for venv tests
|
|
||||||
- virtualenv # for virtualenv tests
|
|
||||||
- glibc-all-langpacks # for locale tests
|
|
||||||
- marshalparser # for testing compatibility (magic numbers) with marshalparser
|
|
||||||
- rpm # for debugging
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue