Compare commits
10 Commits
a7762d83a9
...
caebf6a75a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caebf6a75a | ||
|
|
fdd4347c4b | ||
|
|
7f1b1a2665 | ||
|
|
e9a34e198f | ||
|
|
e9d921e1e8 | ||
|
|
25d18d0e7d | ||
|
|
c9709da6e2 | ||
|
|
5f32441d05 | ||
|
|
358ea4752a | ||
|
|
8d29a3ae78 |
24
0001-expected_algs-list-to-include-TLS_SM4.patch
Normal file
24
0001-expected_algs-list-to-include-TLS_SM4.patch
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
From e9125a4a29e9c615a2562446f07c1b1ffaa6061f Mon Sep 17 00:00:00 2001
|
||||||
|
From: wangshuo <wangshuo@kylinos.cn>
|
||||||
|
Date: Wed, 11 Dec 2024 01:05:25 +0800
|
||||||
|
Subject: [PATCH] expected_algs list to include TLS_SM4
|
||||||
|
|
||||||
|
---
|
||||||
|
Lib/test/test_ssl.py | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
|
||||||
|
index 50734f6..0db51bc 100644
|
||||||
|
--- a/Lib/test/test_ssl.py
|
||||||
|
+++ b/Lib/test/test_ssl.py
|
||||||
|
@@ -4222,6 +4222,7 @@ class ThreadedTests(unittest.TestCase):
|
||||||
|
"AES256", "AES-256",
|
||||||
|
# TLS 1.3 ciphers are always enabled
|
||||||
|
"TLS_CHACHA20", "TLS_AES",
|
||||||
|
+ "TLS_SM4",
|
||||||
|
]
|
||||||
|
|
||||||
|
stats = server_params_test(client_context, server_context,
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
||||||
61
Add-loongarch-support.patch
Normal file
61
Add-loongarch-support.patch
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
From b87dad459825a407084c9acde88f42d86139715e Mon Sep 17 00:00:00 2001
|
||||||
|
From: GuoCe <guoce@kylinos.cn>
|
||||||
|
Date: Wed, 6 Mar 2024 18:17:32 +0800
|
||||||
|
Subject: [PATCH] Add loongarch support
|
||||||
|
|
||||||
|
---
|
||||||
|
config.guess | 3 +++
|
||||||
|
config.sub | 2 ++
|
||||||
|
configure.ac | 2 ++
|
||||||
|
3 files changed, 7 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/config.guess b/config.guess
|
||||||
|
index 256083a..33fafea 100755
|
||||||
|
--- a/config.guess
|
||||||
|
+++ b/config.guess
|
||||||
|
@@ -970,6 +970,9 @@ EOF
|
||||||
|
m68*:Linux:*:*)
|
||||||
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
|
exit ;;
|
||||||
|
+ loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
|
||||||
|
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
|
+ exit ;;
|
||||||
|
mips:Linux:*:* | mips64:Linux:*:*)
|
||||||
|
eval "$set_cc_for_build"
|
||||||
|
sed 's/^ //' << EOF > "$dummy.c"
|
||||||
|
diff --git a/config.sub b/config.sub
|
||||||
|
index ba37cf9..d971b78 100755
|
||||||
|
--- a/config.sub
|
||||||
|
+++ b/config.sub
|
||||||
|
@@ -265,6 +265,7 @@ case $basic_machine in
|
||||||
|
| k1om \
|
||||||
|
| le32 | le64 \
|
||||||
|
| lm32 \
|
||||||
|
+ | loongarch32 | loongarch64 | loongarchx32 \
|
||||||
|
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||||
|
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
|
||||||
|
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||||
|
@@ -394,6 +395,7 @@ case $basic_machine in
|
||||||
|
| k1om-* \
|
||||||
|
| le32-* | le64-* \
|
||||||
|
| lm32-* \
|
||||||
|
+ | loongarch32 | loongarch64 | loongarchx32 \
|
||||||
|
| m32c-* | m32r-* | m32rle-* \
|
||||||
|
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||||||
|
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
|
||||||
|
diff --git a/configure.ac b/configure.ac
|
||||||
|
index c2e9fbb..b83fdcf 100644
|
||||||
|
--- a/configure.ac
|
||||||
|
+++ b/configure.ac
|
||||||
|
@@ -779,6 +779,8 @@ cat >> conftest.c <<EOF
|
||||||
|
hppa-linux-gnu
|
||||||
|
# elif defined(__ia64__)
|
||||||
|
ia64-linux-gnu
|
||||||
|
+# elif defined(__loongarch64)
|
||||||
|
+ loongarch64-linux-gnu
|
||||||
|
# elif defined(__m68k__) && !defined(__mcoldfire__)
|
||||||
|
m68k-linux-gnu
|
||||||
|
# elif defined(__mips_hard_float) && defined(__mips_isa_rev) && (__mips_isa_rev >=6) && defined(_MIPSEL)
|
||||||
|
--
|
||||||
|
2.27.0
|
||||||
|
|
||||||
2469
backport-3.7-gh-102950-Implement-PEP-706-Filter-for-tarfile.e.patch
Normal file
2469
backport-3.7-gh-102950-Implement-PEP-706-Filter-for-tarfile.e.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
|||||||
|
From 4e2dd0c3626649224b87b757a292959d94152a00 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Fri, 26 May 2023 23:41:46 -0700
|
||||||
|
Subject: [PATCH] [3.7] gh-104049: do not expose on-disk location from
|
||||||
|
SimpleHTTPRequestHandler (GH-104122)
|
||||||
|
|
||||||
|
Do not expose the local server's on-disk location from `SimpleHTTPRequestHandler` when generating a directory index. (unnecessary information disclosure)
|
||||||
|
|
||||||
|
(cherry picked from commit c7c3a60c88de61a79ded9fdaf6bc6a29da4efb9a)
|
||||||
|
|
||||||
|
Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
|
||||||
|
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||||
|
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
|
||||||
|
---
|
||||||
|
Lib/http/server.py | 2 +-
|
||||||
|
Lib/test/test_httpservers.py | 8 ++++++++
|
||||||
|
.../2023-05-01-15-03-25.gh-issue-104049.b01Y3g.rst | 2 ++
|
||||||
|
3 files changed, 11 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2023-05-01-15-03-25.gh-issue-104049.b01Y3g.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/http/server.py b/Lib/http/server.py
|
||||||
|
index ba2acbc98bf..beabe3de7ab 100644
|
||||||
|
--- a/Lib/http/server.py
|
||||||
|
+++ b/Lib/http/server.py
|
||||||
|
@@ -777,7 +777,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||||
|
displaypath = urllib.parse.unquote(self.path,
|
||||||
|
errors='surrogatepass')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
- displaypath = urllib.parse.unquote(path)
|
||||||
|
+ displaypath = urllib.parse.unquote(self.path)
|
||||||
|
displaypath = html.escape(displaypath, quote=False)
|
||||||
|
enc = sys.getfilesystemencoding()
|
||||||
|
title = 'Directory listing for %s' % displaypath
|
||||||
|
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
|
||||||
|
index b3e15c475a4..8c9be689003 100644
|
||||||
|
--- a/Lib/test/test_httpservers.py
|
||||||
|
+++ b/Lib/test/test_httpservers.py
|
||||||
|
@@ -413,6 +413,14 @@ class SimpleHTTPServerTestCase(BaseTestCase):
|
||||||
|
self.check_status_and_reason(response, HTTPStatus.OK,
|
||||||
|
data=support.TESTFN_UNDECODABLE)
|
||||||
|
|
||||||
|
+ def test_undecodable_parameter(self):
|
||||||
|
+ # sanity check using a valid parameter
|
||||||
|
+ response = self.request(self.base_url + '/?x=123').read()
|
||||||
|
+ self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1'))
|
||||||
|
+ # now the bogus encoding
|
||||||
|
+ response = self.request(self.base_url + '/?x=%bb').read()
|
||||||
|
+ self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1'))
|
||||||
|
+
|
||||||
|
def test_get_dir_redirect_location_domain_injection_bug(self):
|
||||||
|
"""Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location.
|
||||||
|
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2023-05-01-15-03-25.gh-issue-104049.b01Y3g.rst b/Misc/NEWS.d/next/Security/2023-05-01-15-03-25.gh-issue-104049.b01Y3g.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000..969deb26bfe
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2023-05-01-15-03-25.gh-issue-104049.b01Y3g.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Do not expose the local on-disk location in directory indexes
|
||||||
|
+produced by :class:`http.client.SimpleHTTPRequestHandler`.
|
||||||
|
--
|
||||||
|
2.25.1
|
||||||
|
|
||||||
@ -0,0 +1,308 @@
|
|||||||
|
From 4e32d16aa771abb1787e5e9faecb0bec0d639e3c Mon Sep 17 00:00:00 2001
|
||||||
|
From: wangshuo <wangshuo@kylinos.cn>
|
||||||
|
Date: Thu, 24 Oct 2024 18:25:51 +0800
|
||||||
|
Subject: [PATCH 2/3] [3.7] gh-107845: Fix symlink handling for
|
||||||
|
tarfile.data_filter (GH-107846)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
(cherry picked from commit acbd3f9)
|
||||||
|
https://github.com/python/cpython/commit/acbd3f9c5c5f23e95267714e41236140d84fe962
|
||||||
|
|
||||||
|
Co-authored-by: Petr Viktorin <encukou@gmail.com>
|
||||||
|
Co-authored-by: Victor Stinner <vstinner@python.org>
|
||||||
|
Co-authored-by: Lumír 'Frenzy' Balhar <frenzy.madness@gmail.com>
|
||||||
|
|
||||||
|
Refer to:
|
||||||
|
https://github.com/python/cpython/issues/107845
|
||||||
|
https://github.com/encukou/cpython/commit/63556bccc21ef6726ad7bc5769c2dbb08cf5910f
|
||||||
|
https://github.com/encukou/cpython/commit/8e15c2e44cbdbd48522db678ab2519a50f9d41b1
|
||||||
|
---
|
||||||
|
Doc/library/tarfile.rst | 5 +
|
||||||
|
Lib/tarfile.py | 11 +-
|
||||||
|
Lib/test/test_tarfile.py | 144 +++++++++++++++++-
|
||||||
|
...-08-10-17-36-22.gh-issue-107845.dABiMJ.rst | 3 +
|
||||||
|
4 files changed, 154 insertions(+), 9 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst
|
||||||
|
|
||||||
|
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
|
||||||
|
index 3f544c3..950e00d 100644
|
||||||
|
--- a/Doc/library/tarfile.rst
|
||||||
|
+++ b/Doc/library/tarfile.rst
|
||||||
|
@@ -715,6 +715,11 @@ A ``TarInfo`` object has the following public data attributes:
|
||||||
|
Name of the target file name, which is only present in :class:`TarInfo` objects
|
||||||
|
of type :const:`LNKTYPE` and :const:`SYMTYPE`.
|
||||||
|
|
||||||
|
+ For symbolic links (``SYMTYPE``), the *linkname* is relative to the directory
|
||||||
|
+ that contains the link.
|
||||||
|
+ For hard links (``LNKTYPE``), the *linkname* is relative to the root of
|
||||||
|
+ the archive.
|
||||||
|
+
|
||||||
|
|
||||||
|
.. attribute:: TarInfo.uid
|
||||||
|
|
||||||
|
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
|
||||||
|
index 71c5112..9a8d2dd 100755
|
||||||
|
--- a/Lib/tarfile.py
|
||||||
|
+++ b/Lib/tarfile.py
|
||||||
|
@@ -750,7 +750,7 @@ class SpecialFileError(FilterError):
|
||||||
|
class AbsoluteLinkError(FilterError):
|
||||||
|
def __init__(self, tarinfo):
|
||||||
|
self.tarinfo = tarinfo
|
||||||
|
- super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path')
|
||||||
|
+ super().__init__(f'{tarinfo.name!r} is a link to an absolute path')
|
||||||
|
|
||||||
|
class LinkOutsideDestinationError(FilterError):
|
||||||
|
def __init__(self, tarinfo, path):
|
||||||
|
@@ -810,7 +810,14 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
|
||||||
|
if member.islnk() or member.issym():
|
||||||
|
if os.path.isabs(member.linkname):
|
||||||
|
raise AbsoluteLinkError(member)
|
||||||
|
- target_path = os.path.realpath(os.path.join(dest_path, member.linkname))
|
||||||
|
+ if member.issym():
|
||||||
|
+ target_path = os.path.join(dest_path,
|
||||||
|
+ os.path.dirname(name),
|
||||||
|
+ member.linkname)
|
||||||
|
+ else:
|
||||||
|
+ target_path = os.path.join(dest_path,
|
||||||
|
+ member.linkname)
|
||||||
|
+ target_path = os.path.realpath(target_path)
|
||||||
|
if os.path.commonpath([target_path, dest_path]) != dest_path:
|
||||||
|
raise LinkOutsideDestinationError(member, target_path)
|
||||||
|
return new_attrs
|
||||||
|
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
|
||||||
|
index f1aed04..a3db33d 100644
|
||||||
|
--- a/Lib/test/test_tarfile.py
|
||||||
|
+++ b/Lib/test/test_tarfile.py
|
||||||
|
@@ -2984,10 +2984,12 @@ class ArchiveMaker:
|
||||||
|
self.bio = None
|
||||||
|
|
||||||
|
def add(self, name, *, type=None, symlink_to=None, hardlink_to=None,
|
||||||
|
- mode=None, **kwargs):
|
||||||
|
+ mode=None, size=None, **kwargs):
|
||||||
|
"""Add a member to the test archive. Call within `with`."""
|
||||||
|
name = str(name)
|
||||||
|
tarinfo = tarfile.TarInfo(name).replace(**kwargs)
|
||||||
|
+ if size is not None:
|
||||||
|
+ tarinfo.size = size
|
||||||
|
if mode:
|
||||||
|
tarinfo.mode = _filemode_to_int(mode)
|
||||||
|
if symlink_to is not None:
|
||||||
|
@@ -3060,7 +3062,8 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
raise self.raised_exception
|
||||||
|
self.assertEqual(self.expected_paths, set())
|
||||||
|
|
||||||
|
- def expect_file(self, name, type=None, symlink_to=None, mode=None):
|
||||||
|
+ def expect_file(self, name, type=None, symlink_to=None, mode=None,
|
||||||
|
+ size=None):
|
||||||
|
"""Check a single file. See check_context."""
|
||||||
|
if self.raised_exception:
|
||||||
|
raise self.raised_exception
|
||||||
|
@@ -3094,6 +3097,8 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
self.assertTrue(path.is_fifo())
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(type)
|
||||||
|
+ if size is not None:
|
||||||
|
+ self.assertEqual(path.stat().st_size, size)
|
||||||
|
for parent in path.parents:
|
||||||
|
self.expected_paths.discard(parent)
|
||||||
|
|
||||||
|
@@ -3139,8 +3144,15 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
# Test interplaying symlinks
|
||||||
|
# Inspired by 'dirsymlink2a' in jwilk/traversal-archives
|
||||||
|
with ArchiveMaker() as arc:
|
||||||
|
+
|
||||||
|
+ # `current` links to `.` which is both:
|
||||||
|
+ # - the destination directory
|
||||||
|
+ # - `current` itself
|
||||||
|
arc.add('current', symlink_to='.')
|
||||||
|
+
|
||||||
|
+ # effectively points to ./../
|
||||||
|
arc.add('parent', symlink_to='current/..')
|
||||||
|
+
|
||||||
|
arc.add('parent/evil')
|
||||||
|
|
||||||
|
if support.can_symlink():
|
||||||
|
@@ -3181,9 +3193,46 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
def test_parent_symlink2(self):
|
||||||
|
# Test interplaying symlinks
|
||||||
|
# Inspired by 'dirsymlink2b' in jwilk/traversal-archives
|
||||||
|
+
|
||||||
|
+ # Posix and Windows have different pathname resolution:
|
||||||
|
+ # either symlink or a '..' component resolve first.
|
||||||
|
+ # Let's see which we are on.
|
||||||
|
+ if support.can_symlink():
|
||||||
|
+ testpath = os.path.join(TEMPDIR, 'resolution_test')
|
||||||
|
+ os.mkdir(testpath)
|
||||||
|
+
|
||||||
|
+ # testpath/current links to `.` which is all of:
|
||||||
|
+ # - `testpath`
|
||||||
|
+ # - `testpath/current`
|
||||||
|
+ # - `testpath/current/current`
|
||||||
|
+ # - etc.
|
||||||
|
+ os.symlink('.', os.path.join(testpath, 'current'))
|
||||||
|
+
|
||||||
|
+ # we'll test where `testpath/current/../file` ends up
|
||||||
|
+ with open(os.path.join(testpath, 'current', '..', 'file'), 'w'):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ if os.path.exists(os.path.join(testpath, 'file')):
|
||||||
|
+ # Windows collapses 'current\..' to '.' first, leaving
|
||||||
|
+ # 'testpath\file'
|
||||||
|
+ dotdot_resolves_early = True
|
||||||
|
+ elif os.path.exists(os.path.join(testpath, '..', 'file')):
|
||||||
|
+ # Posix resolves 'current' to '.' first, leaving
|
||||||
|
+ # 'testpath/../file'
|
||||||
|
+ dotdot_resolves_early = False
|
||||||
|
+ else:
|
||||||
|
+ raise AssertionError('Could not determine link resolution')
|
||||||
|
+
|
||||||
|
with ArchiveMaker() as arc:
|
||||||
|
+
|
||||||
|
+ # `current` links to `.` which is both the destination directory
|
||||||
|
+ # and `current` itself
|
||||||
|
arc.add('current', symlink_to='.')
|
||||||
|
+
|
||||||
|
+ # `current/parent` is also available as `./parent`,
|
||||||
|
+ # and effectively points to `./../`
|
||||||
|
arc.add('current/parent', symlink_to='..')
|
||||||
|
+
|
||||||
|
arc.add('parent/evil')
|
||||||
|
|
||||||
|
with self.check_context(arc.open(), 'fully_trusted'):
|
||||||
|
@@ -3197,6 +3246,7 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
|
||||||
|
with self.check_context(arc.open(), 'tar'):
|
||||||
|
if support.can_symlink():
|
||||||
|
+ # Fail when extracting a file outside destination
|
||||||
|
self.expect_exception(
|
||||||
|
tarfile.OutsideDestinationError,
|
||||||
|
"'parent/evil' would be extracted to "
|
||||||
|
@@ -3207,10 +3257,24 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
self.expect_file('parent/evil')
|
||||||
|
|
||||||
|
with self.check_context(arc.open(), 'data'):
|
||||||
|
- self.expect_exception(
|
||||||
|
- tarfile.LinkOutsideDestinationError,
|
||||||
|
- """'current/parent' would link to ['"].*['"], """
|
||||||
|
- + "which is outside the destination")
|
||||||
|
+ if support.can_symlink():
|
||||||
|
+ if dotdot_resolves_early:
|
||||||
|
+ # Fail when extracting a file outside destination
|
||||||
|
+ self.expect_exception(
|
||||||
|
+ tarfile.OutsideDestinationError,
|
||||||
|
+ "'parent/evil' would be extracted to "
|
||||||
|
+ + """['"].*evil['"], which is outside """
|
||||||
|
+ + "the destination")
|
||||||
|
+ else:
|
||||||
|
+ # Fail as soon as we have a symlink outside the destination
|
||||||
|
+ self.expect_exception(
|
||||||
|
+ tarfile.LinkOutsideDestinationError,
|
||||||
|
+ "'current/parent' would link to "
|
||||||
|
+ + """['"].*outerdir['"], which is outside """
|
||||||
|
+ + "the destination")
|
||||||
|
+ else:
|
||||||
|
+ self.expect_file('current/')
|
||||||
|
+ self.expect_file('parent/evil')
|
||||||
|
|
||||||
|
def test_absolute_symlink(self):
|
||||||
|
# Test symlink to an absolute path
|
||||||
|
@@ -3239,11 +3303,29 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
with self.check_context(arc.open(), 'data'):
|
||||||
|
self.expect_exception(
|
||||||
|
tarfile.AbsoluteLinkError,
|
||||||
|
- "'parent' is a symlink to an absolute path")
|
||||||
|
+ "'parent' is a link to an absolute path")
|
||||||
|
+
|
||||||
|
+ def test_absolute_hardlink(self):
|
||||||
|
+ # Test hardlink to an absolute path
|
||||||
|
+ # Inspired by 'dirsymlink' in https://github.com/jwilk/traversal-archives
|
||||||
|
+ with ArchiveMaker() as arc:
|
||||||
|
+ arc.add('parent', hardlink_to=self.outerdir / 'foo')
|
||||||
|
+
|
||||||
|
+ with self.check_context(arc.open(), 'fully_trusted'):
|
||||||
|
+ self.expect_exception(KeyError, ".*foo. not found")
|
||||||
|
+
|
||||||
|
+ with self.check_context(arc.open(), 'tar'):
|
||||||
|
+ self.expect_exception(KeyError, ".*foo. not found")
|
||||||
|
+
|
||||||
|
+ with self.check_context(arc.open(), 'data'):
|
||||||
|
+ self.expect_exception(
|
||||||
|
+ tarfile.AbsoluteLinkError,
|
||||||
|
+ "'parent' is a link to an absolute path")
|
||||||
|
|
||||||
|
def test_sly_relative0(self):
|
||||||
|
# Inspired by 'relative0' in jwilk/traversal-archives
|
||||||
|
with ArchiveMaker() as arc:
|
||||||
|
+ # points to `../../tmp/moo`
|
||||||
|
arc.add('../moo', symlink_to='..//tmp/moo')
|
||||||
|
|
||||||
|
try:
|
||||||
|
@@ -3293,6 +3375,54 @@ class TestExtractionFilters(unittest.TestCase):
|
||||||
|
+ """['"].*moo['"], which is outside the """
|
||||||
|
+ "destination")
|
||||||
|
|
||||||
|
+ def test_deep_symlink(self):
|
||||||
|
+ # Test that symlinks and hardlinks inside a directory
|
||||||
|
+ # point to the correct file (`target` of size 3).
|
||||||
|
+ # If links aren't supported we get a copy of the file.
|
||||||
|
+ with ArchiveMaker() as arc:
|
||||||
|
+ arc.add('targetdir/target', size=3)
|
||||||
|
+ # a hardlink's linkname is relative to the archive
|
||||||
|
+ arc.add('linkdir/hardlink', hardlink_to=os.path.join(
|
||||||
|
+ 'targetdir', 'target'))
|
||||||
|
+ # a symlink's linkname is relative to the link's directory
|
||||||
|
+ arc.add('linkdir/symlink', symlink_to=os.path.join(
|
||||||
|
+ '..', 'targetdir', 'target'))
|
||||||
|
+
|
||||||
|
+ for filter in 'tar', 'data', 'fully_trusted':
|
||||||
|
+ with self.check_context(arc.open(), filter):
|
||||||
|
+ self.expect_file('targetdir/target', size=3)
|
||||||
|
+ self.expect_file('linkdir/hardlink', size=3)
|
||||||
|
+ if support.can_symlink():
|
||||||
|
+ self.expect_file('linkdir/symlink', size=3,
|
||||||
|
+ symlink_to='../targetdir/target')
|
||||||
|
+ else:
|
||||||
|
+ self.expect_file('linkdir/symlink', size=3)
|
||||||
|
+
|
||||||
|
+ def test_chains(self):
|
||||||
|
+ # Test chaining of symlinks/hardlinks.
|
||||||
|
+ # Symlinks are created before the files they point to.
|
||||||
|
+ with ArchiveMaker() as arc:
|
||||||
|
+ arc.add('linkdir/symlink', symlink_to='hardlink')
|
||||||
|
+ arc.add('symlink2', symlink_to=os.path.join(
|
||||||
|
+ 'linkdir', 'hardlink2'))
|
||||||
|
+ arc.add('targetdir/target', size=3)
|
||||||
|
+ arc.add('linkdir/hardlink', hardlink_to='targetdir/target')
|
||||||
|
+ arc.add('linkdir/hardlink2', hardlink_to='linkdir/symlink')
|
||||||
|
+
|
||||||
|
+ for filter in 'tar', 'data', 'fully_trusted':
|
||||||
|
+ with self.check_context(arc.open(), filter):
|
||||||
|
+ self.expect_file('targetdir/target', size=3)
|
||||||
|
+ self.expect_file('linkdir/hardlink', size=3)
|
||||||
|
+ self.expect_file('linkdir/hardlink2', size=3)
|
||||||
|
+ if support.can_symlink():
|
||||||
|
+ self.expect_file('linkdir/symlink', size=3,
|
||||||
|
+ symlink_to='hardlink')
|
||||||
|
+ self.expect_file('symlink2', size=3,
|
||||||
|
+ symlink_to='linkdir/hardlink2')
|
||||||
|
+ else:
|
||||||
|
+ self.expect_file('linkdir/symlink', size=3)
|
||||||
|
+ self.expect_file('symlink2', size=3)
|
||||||
|
+
|
||||||
|
def test_modes(self):
|
||||||
|
# Test how file modes are extracted
|
||||||
|
# (Note that the modes are ignored on platforms without working chmod)
|
||||||
|
diff --git a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..32c1fb9
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+:func:`tarfile.data_filter` now takes the location of symlinks into account
|
||||||
|
+when determining their target, so it will no longer reject some valid
|
||||||
|
+tarballs with ``LinkOutsideDestinationError``.
|
||||||
|
--
|
||||||
|
2.33.0
|
||||||
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
From b6a790412ccacd9b90486fdb86e29f2e49c8fa6c Mon Sep 17 00:00:00 2001
|
||||||
|
From: wangshuo <wangshuo@kylinos.cn>
|
||||||
|
Date: Fri, 25 Oct 2024 10:13:37 +0800
|
||||||
|
Subject: [PATCH 3/3] [3.7] gh-115133: Fix test_xml_etree error with expat
|
||||||
|
versions that fix CVE-2023-52425
|
||||||
|
|
||||||
|
Feeding the parser by too small chunks defers parsing to prevent CVE-2023-52425.
|
||||||
|
According to the upstream solution, chunk_size=22 is the smallest value
|
||||||
|
that can pass the tests.
|
||||||
|
|
||||||
|
See https://github.com/python/cpython/issues/115133
|
||||||
|
---
|
||||||
|
Lib/test/test_xml_etree.py | 4 +++-
|
||||||
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
|
||||||
|
index 5ba0de8..7b225ad 100644
|
||||||
|
--- a/Lib/test/test_xml_etree.py
|
||||||
|
+++ b/Lib/test/test_xml_etree.py
|
||||||
|
@@ -1060,7 +1060,9 @@ class XMLPullParserTest(unittest.TestCase):
|
||||||
|
expected)
|
||||||
|
|
||||||
|
def test_simple_xml(self):
|
||||||
|
- for chunk_size in (None, 1, 5):
|
||||||
|
+ # Feeding the parser by too small chunks defers parsing to prevent CVE-2023-52425.
|
||||||
|
+ # See https://github.com/python/cpython/issues/115133
|
||||||
|
+ for chunk_size in (None, 22, 25):
|
||||||
|
with self.subTest(chunk_size=chunk_size):
|
||||||
|
parser = ET.XMLPullParser()
|
||||||
|
self.assert_event_tags(parser, [])
|
||||||
|
--
|
||||||
|
2.33.0
|
||||||
|
|
||||||
300
backport-3.7-gh-124651-CVE-2024-9287.patch
Normal file
300
backport-3.7-gh-124651-CVE-2024-9287.patch
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
From c2ca86f086de75333051bff50b0e7d886302553f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Victor Stinner <vstinner@python.org>
|
||||||
|
Date: Wed, 19 Dec 2024 11:23:56 +0800
|
||||||
|
Subject: [PATCH] [3.7] gh-124651: Quote template strings in `venv` activation
|
||||||
|
scripts (GH-124712) (GH-126185) (GH-126269) (GH-126301)
|
||||||
|
|
||||||
|
Fix CVE-2024-9287, backported to version 3.7 by wangshuo@kylinos.cn
|
||||||
|
|
||||||
|
Refer to:
|
||||||
|
https://github.com/python/cpython/issues/124651
|
||||||
|
https://github.com/python/cpython/pull/126301
|
||||||
|
https://github.com/python/cpython/commit/633555735a023d3e4d92ba31da35b1205f9ecbd7
|
||||||
|
---
|
||||||
|
Lib/test/test_venv.py | 81 +++++++++++++++++++
|
||||||
|
Lib/venv/__init__.py | 42 ++++++++--
|
||||||
|
Lib/venv/scripts/common/activate | 8 +-
|
||||||
|
Lib/venv/scripts/nt/activate.bat | 4 +-
|
||||||
|
Lib/venv/scripts/posix/activate.csh | 4 +-
|
||||||
|
Lib/venv/scripts/posix/activate.fish | 6 +-
|
||||||
|
...-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 +
|
||||||
|
7 files changed, 130 insertions(+), 16 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
|
||||||
|
index a1fc675..2945907 100644
|
||||||
|
--- a/Lib/test/test_venv.py
|
||||||
|
+++ b/Lib/test/test_venv.py
|
||||||
|
@@ -14,6 +14,7 @@ import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
+import shlex
|
||||||
|
from test.support import (captured_stdout, captured_stderr, requires_zlib,
|
||||||
|
can_symlink, EnvironmentVarGuard, rmtree)
|
||||||
|
import threading
|
||||||
|
@@ -83,6 +84,10 @@ class BaseTest(unittest.TestCase):
|
||||||
|
result = f.read()
|
||||||
|
return result
|
||||||
|
|
||||||
|
+ def assertEndsWith(self, string, tail):
|
||||||
|
+ if not string.endswith(tail):
|
||||||
|
+ self.fail(f"String {string!r} does not end with {tail!r}")
|
||||||
|
+
|
||||||
|
class BasicTest(BaseTest):
|
||||||
|
"""Test venv module functionality."""
|
||||||
|
|
||||||
|
@@ -293,6 +298,82 @@ class BasicTest(BaseTest):
|
||||||
|
'import sys; print(sys.executable)'])
|
||||||
|
self.assertEqual(out.strip(), envpy.encode())
|
||||||
|
|
||||||
|
+ # gh-124651: test quoted strings
|
||||||
|
+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
|
||||||
|
+ def test_special_chars_bash(self):
|
||||||
|
+ """
|
||||||
|
+ Test that the template strings are quoted properly (bash)
|
||||||
|
+ """
|
||||||
|
+ rmtree(self.env_dir)
|
||||||
|
+ bash = shutil.which('bash')
|
||||||
|
+ if bash is None:
|
||||||
|
+ self.skipTest('bash required for this test')
|
||||||
|
+ env_name = '"\';&&$e|\'"'
|
||||||
|
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
|
||||||
|
+ builder = venv.EnvBuilder(clear=True)
|
||||||
|
+ builder.create(env_dir)
|
||||||
|
+ activate = os.path.join(env_dir, self.bindir, 'activate')
|
||||||
|
+ test_script = os.path.join(self.env_dir, 'test_special_chars.sh')
|
||||||
|
+ with open(test_script, "w") as f:
|
||||||
|
+ f.write(f'source {shlex.quote(activate)}\n'
|
||||||
|
+ 'python -c \'import sys; print(sys.executable)\'\n'
|
||||||
|
+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
|
||||||
|
+ 'deactivate\n')
|
||||||
|
+ out, err = check_output([bash, test_script])
|
||||||
|
+ lines = out.splitlines()
|
||||||
|
+ self.assertTrue(env_name.encode() in lines[0])
|
||||||
|
+ self.assertEndsWith(lines[1], env_name.encode())
|
||||||
|
+
|
||||||
|
+ # gh-124651: test quoted strings
|
||||||
|
+ @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows')
|
||||||
|
+ def test_special_chars_csh(self):
|
||||||
|
+ """
|
||||||
|
+ Test that the template strings are quoted properly (csh)
|
||||||
|
+ """
|
||||||
|
+ rmtree(self.env_dir)
|
||||||
|
+ csh = shutil.which('tcsh') or shutil.which('csh')
|
||||||
|
+ if csh is None:
|
||||||
|
+ self.skipTest('csh required for this test')
|
||||||
|
+ env_name = '"\';&&$e|\'"'
|
||||||
|
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
|
||||||
|
+ builder = venv.EnvBuilder(clear=True)
|
||||||
|
+ builder.create(env_dir)
|
||||||
|
+ activate = os.path.join(env_dir, self.bindir, 'activate.csh')
|
||||||
|
+ test_script = os.path.join(self.env_dir, 'test_special_chars.csh')
|
||||||
|
+ with open(test_script, "w") as f:
|
||||||
|
+ f.write(f'source {shlex.quote(activate)}\n'
|
||||||
|
+ 'python -c \'import sys; print(sys.executable)\'\n'
|
||||||
|
+ 'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n'
|
||||||
|
+ 'deactivate\n')
|
||||||
|
+ out, err = check_output([csh, test_script])
|
||||||
|
+ lines = out.splitlines()
|
||||||
|
+ self.assertTrue(env_name.encode() in lines[0])
|
||||||
|
+ self.assertEndsWith(lines[1], env_name.encode())
|
||||||
|
+
|
||||||
|
+ # gh-124651: test quoted strings on Windows
|
||||||
|
+ @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
|
||||||
|
+ def test_special_chars_windows(self):
|
||||||
|
+ """
|
||||||
|
+ Test that the template strings are quoted properly on Windows
|
||||||
|
+ """
|
||||||
|
+ rmtree(self.env_dir)
|
||||||
|
+ env_name = "'&&^$e"
|
||||||
|
+ env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
|
||||||
|
+ builder = venv.EnvBuilder(clear=True)
|
||||||
|
+ builder.create(env_dir)
|
||||||
|
+ activate = os.path.join(env_dir, self.bindir, 'activate.bat')
|
||||||
|
+ test_batch = os.path.join(self.env_dir, 'test_special_chars.bat')
|
||||||
|
+ with open(test_batch, "w") as f:
|
||||||
|
+ f.write('@echo off\n'
|
||||||
|
+ f'"{activate}" & '
|
||||||
|
+ f'{self.exe} -c "import sys; print(sys.executable)" & '
|
||||||
|
+ f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & '
|
||||||
|
+ 'deactivate')
|
||||||
|
+ out, err = check_output([test_batch])
|
||||||
|
+ lines = out.splitlines()
|
||||||
|
+ self.assertTrue(env_name.encode() in lines[0])
|
||||||
|
+ self.assertEndsWith(lines[1], env_name.encode())
|
||||||
|
+
|
||||||
|
@unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
|
||||||
|
def test_unicode_in_batch_file(self):
|
||||||
|
"""
|
||||||
|
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
|
||||||
|
index c454082..de4d3eb 100644
|
||||||
|
--- a/Lib/venv/__init__.py
|
||||||
|
+++ b/Lib/venv/__init__.py
|
||||||
|
@@ -11,6 +11,7 @@ import subprocess
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
import types
|
||||||
|
+import shlex
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@@ -323,11 +324,41 @@ class EnvBuilder:
|
||||||
|
:param context: The information for the environment creation request
|
||||||
|
being processed.
|
||||||
|
"""
|
||||||
|
- text = text.replace('__VENV_DIR__', context.env_dir)
|
||||||
|
- text = text.replace('__VENV_NAME__', context.env_name)
|
||||||
|
- text = text.replace('__VENV_PROMPT__', context.prompt)
|
||||||
|
- text = text.replace('__VENV_BIN_NAME__', context.bin_name)
|
||||||
|
- text = text.replace('__VENV_PYTHON__', context.env_exe)
|
||||||
|
+ replacements = {
|
||||||
|
+ '__VENV_DIR__': context.env_dir,
|
||||||
|
+ '__VENV_NAME__': context.env_name,
|
||||||
|
+ '__VENV_PROMPT__': context.prompt,
|
||||||
|
+ '__VENV_BIN_NAME__': context.bin_name,
|
||||||
|
+ '__VENV_PYTHON__': context.env_exe,
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ def quote_ps1(s):
|
||||||
|
+ """
|
||||||
|
+ This should satisfy PowerShell quoting rules [1], unless the quoted
|
||||||
|
+ string is passed directly to Windows native commands [2].
|
||||||
|
+ [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
|
||||||
|
+ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
|
||||||
|
+ """
|
||||||
|
+ s = s.replace("'", "''")
|
||||||
|
+ return f"'{s}'"
|
||||||
|
+
|
||||||
|
+ def quote_bat(s):
|
||||||
|
+ return s
|
||||||
|
+
|
||||||
|
+ # gh-124651: need to quote the template strings properly
|
||||||
|
+ quote = shlex.quote
|
||||||
|
+ script_path = context.script_path
|
||||||
|
+ if script_path.endswith('.ps1'):
|
||||||
|
+ quote = quote_ps1
|
||||||
|
+ elif script_path.endswith('.bat'):
|
||||||
|
+ quote = quote_bat
|
||||||
|
+ else:
|
||||||
|
+ # fallbacks to POSIX shell compliant quote
|
||||||
|
+ quote = shlex.quote
|
||||||
|
+
|
||||||
|
+ replacements = {key: quote(s) for key, s in replacements.items()}
|
||||||
|
+ for key, quoted in replacements.items():
|
||||||
|
+ text = text.replace(key, quoted)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def install_scripts(self, context, path):
|
||||||
|
@@ -367,6 +398,7 @@ class EnvBuilder:
|
||||||
|
with open(srcfile, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
if not srcfile.endswith(('.exe', '.pdb')):
|
||||||
|
+ context.script_path = srcfile
|
||||||
|
try:
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
data = self.replace_variables(data, context)
|
||||||
|
diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate
|
||||||
|
index b9d498f..a002005 100644
|
||||||
|
--- a/Lib/venv/scripts/common/activate
|
||||||
|
+++ b/Lib/venv/scripts/common/activate
|
||||||
|
@@ -37,11 +37,11 @@ deactivate () {
|
||||||
|
# unset irrelevant variables
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
-VIRTUAL_ENV="__VENV_DIR__"
|
||||||
|
+VIRTUAL_ENV=__VENV_DIR__
|
||||||
|
export VIRTUAL_ENV
|
||||||
|
|
||||||
|
_OLD_VIRTUAL_PATH="$PATH"
|
||||||
|
-PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
|
||||||
|
+PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
# unset PYTHONHOME if set
|
||||||
|
@@ -54,8 +54,8 @@ fi
|
||||||
|
|
||||||
|
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||||
|
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||||
|
- if [ "x__VENV_PROMPT__" != x ] ; then
|
||||||
|
- PS1="__VENV_PROMPT__${PS1:-}"
|
||||||
|
+ if [ x__VENV_PROMPT__ != x ] ; then
|
||||||
|
+ PS1=__VENV_PROMPT__"${PS1:-}"
|
||||||
|
else
|
||||||
|
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
|
||||||
|
# special case for Aspen magic directories
|
||||||
|
diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat
|
||||||
|
index f61413e..11210c7 100644
|
||||||
|
--- a/Lib/venv/scripts/nt/activate.bat
|
||||||
|
+++ b/Lib/venv/scripts/nt/activate.bat
|
||||||
|
@@ -8,7 +8,7 @@ if defined _OLD_CODEPAGE (
|
||||||
|
"%SystemRoot%\System32\chcp.com" 65001 > nul
|
||||||
|
)
|
||||||
|
|
||||||
|
-set VIRTUAL_ENV=__VENV_DIR__
|
||||||
|
+set "VIRTUAL_ENV=__VENV_DIR__"
|
||||||
|
|
||||||
|
if not defined PROMPT set PROMPT=$P$G
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ set PYTHONHOME=
|
||||||
|
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
|
||||||
|
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
|
||||||
|
|
||||||
|
-set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%
|
||||||
|
+set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%"
|
||||||
|
|
||||||
|
:END
|
||||||
|
if defined _OLD_CODEPAGE (
|
||||||
|
diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh
|
||||||
|
index b0c7028..ad53fd3 100644
|
||||||
|
--- a/Lib/venv/scripts/posix/activate.csh
|
||||||
|
+++ b/Lib/venv/scripts/posix/activate.csh
|
||||||
|
@@ -8,10 +8,10 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
|
||||||
|
# Unset irrelevant variables.
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
-setenv VIRTUAL_ENV "__VENV_DIR__"
|
||||||
|
+setenv VIRTUAL_ENV __VENV_DIR__
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PATH="$PATH"
|
||||||
|
-setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
|
||||||
|
+setenv PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
|
||||||
|
|
||||||
|
|
||||||
|
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||||
|
diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/posix/activate.fish
|
||||||
|
index b401058..16723a2 100644
|
||||||
|
--- a/Lib/venv/scripts/posix/activate.fish
|
||||||
|
+++ b/Lib/venv/scripts/posix/activate.fish
|
||||||
|
@@ -29,10 +29,10 @@ end
|
||||||
|
# unset irrelevant variables
|
||||||
|
deactivate nondestructive
|
||||||
|
|
||||||
|
-set -gx VIRTUAL_ENV "__VENV_DIR__"
|
||||||
|
+set -gx VIRTUAL_ENV __VENV_DIR__
|
||||||
|
|
||||||
|
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||||
|
-set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH
|
||||||
|
+set -gx PATH "$VIRTUAL_ENV/"__VENV_BIN_NAME__ $PATH
|
||||||
|
|
||||||
|
# unset PYTHONHOME if set
|
||||||
|
if set -q PYTHONHOME
|
||||||
|
@@ -53,7 +53,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||||
|
|
||||||
|
# Prompt override?
|
||||||
|
if test -n "__VENV_PROMPT__"
|
||||||
|
- printf "%s%s" "__VENV_PROMPT__" (set_color normal)
|
||||||
|
+ printf "%s%s" __VENV_PROMPT__ (set_color normal)
|
||||||
|
else
|
||||||
|
# ...Otherwise, prepend env
|
||||||
|
set -l _checkbase (basename "$VIRTUAL_ENV")
|
||||||
|
diff --git a/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..17fc917
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+Properly quote template strings in :mod:`venv` activation scripts.
|
||||||
|
--
|
||||||
|
2.27.0
|
||||||
|
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
From 1ce801b81ce63867ce382f6e9f56873a844c2bc6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Miss Islington (bot)"
|
||||||
|
<31488909+miss-islington@users.noreply.github.com>
|
||||||
|
Date: Sat, 27 May 2023 00:04:28 -0700
|
||||||
|
Subject: [PATCH] [3.7] gh-99889: Fix directory traversal security flaw in
|
||||||
|
uu.decode() (GH-104333)
|
||||||
|
|
||||||
|
(cherry picked from commit 0aeda297931820436a50b78f4f7f0597274b5df4)
|
||||||
|
Co-authored-by: Sam Carroll <70000253+samcarroll42@users.noreply.github.com>
|
||||||
|
---
|
||||||
|
Lib/test/test_uu.py | 28 +++++++++++++++++++
|
||||||
|
Lib/uu.py | 9 +++++-
|
||||||
|
...3-05-02-17-56-32.gh-issue-99889.l664SU.rst | 2 ++
|
||||||
|
3 files changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
mode change 100755 => 100644 Lib/uu.py
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py
|
||||||
|
index c8709f7a0d6..e5d93d6cd1c 100644
|
||||||
|
--- a/Lib/test/test_uu.py
|
||||||
|
+++ b/Lib/test/test_uu.py
|
||||||
|
@@ -145,6 +145,34 @@ class UUTest(unittest.TestCase):
|
||||||
|
uu.encode(inp, out, filename)
|
||||||
|
self.assertIn(safefilename, out.getvalue())
|
||||||
|
|
||||||
|
+ def test_no_directory_traversal(self):
|
||||||
|
+ relative_bad = b"""\
|
||||||
|
+begin 644 ../../../../../../../../tmp/test1
|
||||||
|
+$86)C"@``
|
||||||
|
+`
|
||||||
|
+end
|
||||||
|
+"""
|
||||||
|
+ with self.assertRaisesRegex(uu.Error, 'directory'):
|
||||||
|
+ uu.decode(io.BytesIO(relative_bad))
|
||||||
|
+ if os.altsep:
|
||||||
|
+ relative_bad_bs = relative_bad.replace(b'/', b'\\')
|
||||||
|
+ with self.assertRaisesRegex(uu.Error, 'directory'):
|
||||||
|
+ uu.decode(io.BytesIO(relative_bad_bs))
|
||||||
|
+
|
||||||
|
+ absolute_bad = b"""\
|
||||||
|
+begin 644 /tmp/test2
|
||||||
|
+$86)C"@``
|
||||||
|
+`
|
||||||
|
+end
|
||||||
|
+"""
|
||||||
|
+ with self.assertRaisesRegex(uu.Error, 'directory'):
|
||||||
|
+ uu.decode(io.BytesIO(absolute_bad))
|
||||||
|
+ if os.altsep:
|
||||||
|
+ absolute_bad_bs = absolute_bad.replace(b'/', b'\\')
|
||||||
|
+ with self.assertRaisesRegex(uu.Error, 'directory'):
|
||||||
|
+ uu.decode(io.BytesIO(absolute_bad_bs))
|
||||||
|
+
|
||||||
|
+
|
||||||
|
class UUStdIOTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
diff --git a/Lib/uu.py b/Lib/uu.py
|
||||||
|
old mode 100755
|
||||||
|
new mode 100644
|
||||||
|
index 9f1f37f1a64..9fe252a639e
|
||||||
|
--- a/Lib/uu.py
|
||||||
|
+++ b/Lib/uu.py
|
||||||
|
@@ -130,7 +130,14 @@ def decode(in_file, out_file=None, mode=None, quiet=False):
|
||||||
|
# If the filename isn't ASCII, what's up with that?!?
|
||||||
|
out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
|
||||||
|
if os.path.exists(out_file):
|
||||||
|
- raise Error('Cannot overwrite existing file: %s' % out_file)
|
||||||
|
+ raise Error(f'Cannot overwrite existing file: {out_file}')
|
||||||
|
+ if (out_file.startswith(os.sep) or
|
||||||
|
+ f'..{os.sep}' in out_file or (
|
||||||
|
+ os.altsep and
|
||||||
|
+ (out_file.startswith(os.altsep) or
|
||||||
|
+ f'..{os.altsep}' in out_file))
|
||||||
|
+ ):
|
||||||
|
+ raise Error(f'Refusing to write to {out_file} due to directory traversal')
|
||||||
|
if mode is None:
|
||||||
|
mode = int(hdrfields[1], 8)
|
||||||
|
#
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst b/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000..b7002e81b6b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2023-05-02-17-56-32.gh-issue-99889.l664SU.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Fixed a security in flaw in :func:`uu.decode` that could allow for
|
||||||
|
+directory traversal based on the input if no ``out_file`` was specified.
|
||||||
|
--
|
||||||
|
2.25.1
|
||||||
|
|
||||||
489
backport-CVE-2023-27043.patch
Normal file
489
backport-CVE-2023-27043.patch
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
From 213a9208f830980b5241a9f24a79778cb699b2c7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: GuoCe <guoce@kylinos.cn>
|
||||||
|
Date: Tue, 5 Mar 2024 16:47:59 +0800
|
||||||
|
Subject: [PATCH 3/3] Subject: [PATCH] [Backport] CVE-2023-27043 Reject
|
||||||
|
malformed addresses in email.parseaddr()
|
||||||
|
|
||||||
|
Reference: https://github.com/python/cpython/pull/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.
|
||||||
|
|
||||||
|
Offering: CloudBu CMP
|
||||||
|
|
||||||
|
CVE: CVE-2023-27043
|
||||||
|
---
|
||||||
|
Doc/library/email.utils.rst | 19 +-
|
||||||
|
Lib/email/utils.py | 151 +++++++++++++-
|
||||||
|
Lib/test/test_email/test_email.py | 187 +++++++++++++++++-
|
||||||
|
...-10-20-15-28-08.gh-issue-102988.dStNO7.rst | 8 +
|
||||||
|
4 files changed, 344 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 4d0e920..104229e 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 e7a4149..14f73ea 100644
|
||||||
|
--- 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
|
||||||
|
+
|
||||||
|
+ if start < len(addr):
|
||||||
|
+ result.append(addr[start:])
|
||||||
|
+
|
||||||
|
+ return ''.join(result)
|
||||||
|
|
||||||
|
-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
|
||||||
|
+
|
||||||
|
+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]
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
|
||||||
|
index 66aaee1..56a8063 100644
|
||||||
|
--- 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
|
||||||
|
@@ -3230,15 +3231,137 @@ Foo
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
+ 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"""
|
||||||
|
@@ -3422,6 +3545,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):
|
||||||
|
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 0000000..3d0e9e4
|
||||||
|
--- /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.
|
||||||
|
--
|
||||||
|
2.27.0
|
||||||
|
|
||||||
267
backport-Fix-parsing-errors-in-email-_parseaddr.py.patch
Normal file
267
backport-Fix-parsing-errors-in-email-_parseaddr.py.patch
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
From 42fe8d3b828bbea6c530225a6369eda8ad86c9f3 Mon Sep 17 00:00:00 2001
|
||||||
|
From: GuoCe <guoce@kylinos.cn>
|
||||||
|
Date: Tue, 5 Mar 2024 15:33:14 +0800
|
||||||
|
Subject: [PATCH 1/3] Subject: [PATCH] [Backport] Fix parsing errors in
|
||||||
|
email/_parseaddr.py
|
||||||
|
|
||||||
|
Reference: https://github.com/python/cpython/issues/102988
|
||||||
|
|
||||||
|
The e-mail module of Python 0 - 2.7.18, 3.x - 3.11 incorrectly parses e-mail addresses which contain a special character. This vulnerability allows attackers to send messages from e-mail addresses that would otherwise be rejected.
|
||||||
|
|
||||||
|
Offering: CloudBu CMP
|
||||||
|
|
||||||
|
CVE: CVE-2023-27043
|
||||||
|
---
|
||||||
|
Doc/library/email.utils.rst | 26 +++++-
|
||||||
|
Lib/email/utils.py | 63 +++++++++++++--
|
||||||
|
Lib/test/test_email/test_email.py | 81 ++++++++++++++++++-
|
||||||
|
...-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 4 +
|
||||||
|
4 files changed, 164 insertions(+), 10 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
|
||||||
|
diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
|
||||||
|
index 4d0e920..97df962 100644
|
||||||
|
--- a/Doc/library/email.utils.rst
|
||||||
|
+++ b/Doc/library/email.utils.rst
|
||||||
|
@@ -67,6 +67,11 @@ of the new API.
|
||||||
|
*email address* parts. Returns a tuple of that information, unless the parse
|
||||||
|
fails, in which case a 2-tuple of ``('', '')`` is returned.
|
||||||
|
|
||||||
|
+ .. versionchanged:: 3.12
|
||||||
|
+ For security reasons, addresses that were ambiguous and could parse into
|
||||||
|
+ multiple different addresses now cause ``('', '')`` to be returned
|
||||||
|
+ instead of only one of the *potential* addresses.
|
||||||
|
+
|
||||||
|
|
||||||
|
.. function:: formataddr(pair, charset='utf-8')
|
||||||
|
|
||||||
|
@@ -89,7 +94,7 @@ of the new API.
|
||||||
|
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::
|
||||||
|
+ example that gets all the recipients of a message:
|
||||||
|
|
||||||
|
from email.utils import getaddresses
|
||||||
|
|
||||||
|
@@ -99,6 +104,25 @@ of the new API.
|
||||||
|
resent_ccs = msg.get_all('resent-cc', [])
|
||||||
|
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
|
||||||
|
|
||||||
|
+ When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')``
|
||||||
|
+ is returned in its place. Other errors in parsing the list of
|
||||||
|
+ addresses such as a fieldvalue seemingly parsing into multiple
|
||||||
|
+ addresses may result in a list containing a single empty 2-tuple
|
||||||
|
+ ``[('', '')]`` being returned rather than returning potentially
|
||||||
|
+ invalid output.
|
||||||
|
+
|
||||||
|
+ Example malformed input parsing:
|
||||||
|
+
|
||||||
|
+ .. doctest::
|
||||||
|
+
|
||||||
|
+ >>> from email.utils import getaddresses
|
||||||
|
+ >>> getaddresses(['alice@example.com <bob@example.com>', 'me@example.com'])
|
||||||
|
+ [('', '')]
|
||||||
|
+
|
||||||
|
+ .. versionchanged:: 3.12
|
||||||
|
+ The 2-tuple of ``('', '')`` in the returned values when parsing
|
||||||
|
+ fails were added as to address a security issue.
|
||||||
|
+
|
||||||
|
|
||||||
|
.. function:: parsedate(date)
|
||||||
|
|
||||||
|
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
|
||||||
|
index 858f620..8b1d4d1 100644
|
||||||
|
--- a/Lib/email/utils.py
|
||||||
|
+++ b/Lib/email/utils.py
|
||||||
|
@@ -106,12 +106,54 @@ def formataddr(pair, charset='utf-8'):
|
||||||
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
+def _pre_parse_validation(email_header_fields):
|
||||||
|
+ accepted_values = []
|
||||||
|
+ for v in email_header_fields:
|
||||||
|
+ s = v.replace('\\(', '').replace('\\)', '')
|
||||||
|
+ if s.count('(') != s.count(')'):
|
||||||
|
+ 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 getaddresses(fieldvalues):
|
||||||
|
- """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
|
||||||
|
- all = COMMASPACE.join(fieldvalues)
|
||||||
|
+ """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 the resulting list of parsed address is not the same as the number of
|
||||||
|
+ fieldvalues in the input list a parsing error has occurred. A list
|
||||||
|
+ containing a single empty 2-tuple [('', '')] is returned in its place.
|
||||||
|
+ This is done to avoid invalid output.
|
||||||
|
+ """
|
||||||
|
+ fieldvalues = [str(v) for v in fieldvalues]
|
||||||
|
+ fieldvalues = _pre_parse_validation(fieldvalues)
|
||||||
|
+ all = COMMASPACE.join(v for v in fieldvalues)
|
||||||
|
a = _AddressList(all)
|
||||||
|
- return a.addresslist
|
||||||
|
+ result = _post_parse_validation(a.addresslist)
|
||||||
|
+
|
||||||
|
+ n = 0
|
||||||
|
+ for v in fieldvalues:
|
||||||
|
+ n += v.count(',') + 1
|
||||||
|
+
|
||||||
|
+ if len(result) != n:
|
||||||
|
+ return [('', '')]
|
||||||
|
+
|
||||||
|
+ return result
|
||||||
|
|
||||||
|
|
||||||
|
def _format_timetuple_and_zone(timetuple, zone):
|
||||||
|
@@ -209,9 +251,18 @@ def parseaddr(addr):
|
||||||
|
Return a tuple of realname and email address, unless the parse fails, in
|
||||||
|
which case return a 2-tuple of ('', '').
|
||||||
|
"""
|
||||||
|
- addrs = _AddressList(addr).addresslist
|
||||||
|
- if not addrs:
|
||||||
|
- return '', ''
|
||||||
|
+ 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 64bcdcc..8b5c9c1 100644
|
||||||
|
--- a/Lib/test/test_email/test_email.py
|
||||||
|
+++ b/Lib/test/test_email/test_email.py
|
||||||
|
@@ -3213,15 +3213,90 @@ Foo
|
||||||
|
[('Al Person', 'aperson@dom.ain'),
|
||||||
|
('Bud Person', 'bperson@dom.ain')])
|
||||||
|
|
||||||
|
+ def test_getaddresses_parsing_errors(self):
|
||||||
|
+ """Test for parsing errors from CVE-2023-27043"""
|
||||||
|
+ eq = self.assertEqual
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org(<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org)<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org<<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org><bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org@<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org,<bob@example.com>']),
|
||||||
|
+ [('', 'alice@example.org'), ('', 'bob@example.com')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org;<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org:<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org.<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org"<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org[<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(['alice@example.org]<bob@example.com>']),
|
||||||
|
+ [('', '')])
|
||||||
|
+
|
||||||
|
+ def test_parseaddr_parsing_errors(self):
|
||||||
|
+ """Test for parsing errors from CVE-2023-27043"""
|
||||||
|
+ eq = self.assertEqual
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org(<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org)<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org<<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org><bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org@<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org,<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org;<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org:<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org.<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org"<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org[<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+ eq(utils.parseaddr(['alice@example.org]<bob@example.com>']),
|
||||||
|
+ ('', ''))
|
||||||
|
+
|
||||||
|
def test_getaddresses_nasty(self):
|
||||||
|
eq = self.assertEqual
|
||||||
|
eq(utils.getaddresses(['foo: ;']), [('', '')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['[]*-- =~$']),
|
||||||
|
- [('', ''), ('', ''), ('', '*--')])
|
||||||
|
+ eq(utils.getaddresses(['[]*-- =~$']), [('', '')])
|
||||||
|
eq(utils.getaddresses(
|
||||||
|
['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
|
||||||
|
[('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ [r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>']),
|
||||||
|
+ [('Pete (A nice ) chap his account his host)', 'pete@silly.test')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']),
|
||||||
|
+ [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['John Doe <jdoe@machine(comment). example>']),
|
||||||
|
+ [('John Doe (comment)', 'jdoe@machine.example')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['"Mary Smith: Personal Account" <smith@home.example>']),
|
||||||
|
+ [('Mary Smith: Personal Account', 'smith@home.example')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['Undisclosed recipients:;']),
|
||||||
|
+ [('', '')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ [r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>']),
|
||||||
|
+ [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')])
|
||||||
|
|
||||||
|
def test_getaddresses_embedded_comment(self):
|
||||||
|
"""Test proper handling of a nested comment"""
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst b/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..e0434cc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
@@ -0,0 +1,4 @@
|
||||||
|
+CVE-2023-27043: Prevent :func:`email.utils.parseaddr`
|
||||||
|
+and :func:`email.utils.getaddresses` from returning the realname portion of an
|
||||||
|
+invalid RFC2822 email header in the email address portion of the 2-tuple
|
||||||
|
+returned after being parsed by :class:`email._parseaddr.AddressList`.
|
||||||
|
--
|
||||||
|
2.27.0
|
||||||
|
|
||||||
286
backport-Revert-fixes-for-CVE-2023-27043.patch
Normal file
286
backport-Revert-fixes-for-CVE-2023-27043.patch
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
From 2c367ef26f6cb9897a862f5871047703fe0af31e Mon Sep 17 00:00:00 2001
|
||||||
|
From: GuoCe <guoce@kylinos.cn>
|
||||||
|
Date: Tue, 5 Mar 2024 16:03:17 +0800
|
||||||
|
Subject: [PATCH 2/3] Subject: [PATCH] [Backport] Revert fixes for
|
||||||
|
CVE-2023-27043
|
||||||
|
|
||||||
|
Reference: https://github.com/python/cpython/pull/106733
|
||||||
|
|
||||||
|
Revert "gh-102988: Detect email address parsing errors and return empty tuple to indicate the parsing error (old API) (#105127)"
|
||||||
|
This reverts commit and adds the regression test suggested in the issue.
|
||||||
|
|
||||||
|
Offering: CloudBu CMP
|
||||||
|
|
||||||
|
CVE: CVE-2023-27043
|
||||||
|
---
|
||||||
|
Doc/library/email.utils.rst | 26 +----
|
||||||
|
Lib/email/utils.py | 63 ++----------
|
||||||
|
Lib/test/test_email/test_email.py | 96 ++++---------------
|
||||||
|
...-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst | 8 +-
|
||||||
|
4 files changed, 30 insertions(+), 163 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
|
||||||
|
index 97df962..4d0e920 100644
|
||||||
|
--- a/Doc/library/email.utils.rst
|
||||||
|
+++ b/Doc/library/email.utils.rst
|
||||||
|
@@ -67,11 +67,6 @@ of the new API.
|
||||||
|
*email address* parts. Returns a tuple of that information, unless the parse
|
||||||
|
fails, in which case a 2-tuple of ``('', '')`` is returned.
|
||||||
|
|
||||||
|
- .. versionchanged:: 3.12
|
||||||
|
- For security reasons, addresses that were ambiguous and could parse into
|
||||||
|
- multiple different addresses now cause ``('', '')`` to be returned
|
||||||
|
- instead of only one of the *potential* addresses.
|
||||||
|
-
|
||||||
|
|
||||||
|
.. function:: formataddr(pair, charset='utf-8')
|
||||||
|
|
||||||
|
@@ -94,7 +89,7 @@ of the new API.
|
||||||
|
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:
|
||||||
|
+ example that gets all the recipients of a message::
|
||||||
|
|
||||||
|
from email.utils import getaddresses
|
||||||
|
|
||||||
|
@@ -104,25 +99,6 @@ of the new API.
|
||||||
|
resent_ccs = msg.get_all('resent-cc', [])
|
||||||
|
all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
|
||||||
|
|
||||||
|
- When parsing fails for a single fieldvalue, a 2-tuple of ``('', '')``
|
||||||
|
- is returned in its place. Other errors in parsing the list of
|
||||||
|
- addresses such as a fieldvalue seemingly parsing into multiple
|
||||||
|
- addresses may result in a list containing a single empty 2-tuple
|
||||||
|
- ``[('', '')]`` being returned rather than returning potentially
|
||||||
|
- invalid output.
|
||||||
|
-
|
||||||
|
- Example malformed input parsing:
|
||||||
|
-
|
||||||
|
- .. doctest::
|
||||||
|
-
|
||||||
|
- >>> from email.utils import getaddresses
|
||||||
|
- >>> getaddresses(['alice@example.com <bob@example.com>', 'me@example.com'])
|
||||||
|
- [('', '')]
|
||||||
|
-
|
||||||
|
- .. versionchanged:: 3.12
|
||||||
|
- The 2-tuple of ``('', '')`` in the returned values when parsing
|
||||||
|
- fails were added as to address a security issue.
|
||||||
|
-
|
||||||
|
|
||||||
|
.. function:: parsedate(date)
|
||||||
|
|
||||||
|
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
|
||||||
|
index 8b1d4d1..e7a4149 100644
|
||||||
|
--- a/Lib/email/utils.py
|
||||||
|
+++ b/Lib/email/utils.py
|
||||||
|
@@ -106,54 +106,12 @@ def formataddr(pair, charset='utf-8'):
|
||||||
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
-def _pre_parse_validation(email_header_fields):
|
||||||
|
- accepted_values = []
|
||||||
|
- for v in email_header_fields:
|
||||||
|
- s = v.replace('\\(', '').replace('\\)', '')
|
||||||
|
- if s.count('(') != s.count(')'):
|
||||||
|
- 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 getaddresses(fieldvalues):
|
||||||
|
- """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 the resulting list of parsed address is not the same as the number of
|
||||||
|
- fieldvalues in the input list a parsing error has occurred. A list
|
||||||
|
- containing a single empty 2-tuple [('', '')] is returned in its place.
|
||||||
|
- This is done to avoid invalid output.
|
||||||
|
- """
|
||||||
|
- fieldvalues = [str(v) for v in fieldvalues]
|
||||||
|
- fieldvalues = _pre_parse_validation(fieldvalues)
|
||||||
|
- all = COMMASPACE.join(v for v in fieldvalues)
|
||||||
|
+ """Return a list of (REALNAME, EMAIL) for each fieldvalue."""
|
||||||
|
+ all = COMMASPACE.join(str(v) for v in fieldvalues)
|
||||||
|
a = _AddressList(all)
|
||||||
|
- result = _post_parse_validation(a.addresslist)
|
||||||
|
-
|
||||||
|
- n = 0
|
||||||
|
- for v in fieldvalues:
|
||||||
|
- n += v.count(',') + 1
|
||||||
|
-
|
||||||
|
- if len(result) != n:
|
||||||
|
- return [('', '')]
|
||||||
|
-
|
||||||
|
- return result
|
||||||
|
+ return a.addresslist
|
||||||
|
|
||||||
|
|
||||||
|
def _format_timetuple_and_zone(timetuple, zone):
|
||||||
|
@@ -251,18 +209,9 @@ def parseaddr(addr):
|
||||||
|
Return a tuple of realname and email address, unless the parse fails, in
|
||||||
|
which case return a 2-tuple of ('', '').
|
||||||
|
"""
|
||||||
|
- 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 ('', '')
|
||||||
|
-
|
||||||
|
+ addrs = _AddressList(addr).addresslist
|
||||||
|
+ if not addrs:
|
||||||
|
+ return '', ''
|
||||||
|
return addrs[0]
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
|
||||||
|
index 8b5c9c1..66aaee1 100644
|
||||||
|
--- a/Lib/test/test_email/test_email.py
|
||||||
|
+++ b/Lib/test/test_email/test_email.py
|
||||||
|
@@ -3213,90 +3213,32 @@ Foo
|
||||||
|
[('Al Person', 'aperson@dom.ain'),
|
||||||
|
('Bud Person', 'bperson@dom.ain')])
|
||||||
|
|
||||||
|
- def test_getaddresses_parsing_errors(self):
|
||||||
|
- """Test for parsing errors from CVE-2023-27043"""
|
||||||
|
- eq = self.assertEqual
|
||||||
|
- eq(utils.getaddresses(['alice@example.org(<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org)<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org<<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org><bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org@<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org,<bob@example.com>']),
|
||||||
|
- [('', 'alice@example.org'), ('', 'bob@example.com')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org;<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org:<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org.<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org"<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org[<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(['alice@example.org]<bob@example.com>']),
|
||||||
|
- [('', '')])
|
||||||
|
-
|
||||||
|
- def test_parseaddr_parsing_errors(self):
|
||||||
|
- """Test for parsing errors from CVE-2023-27043"""
|
||||||
|
- eq = self.assertEqual
|
||||||
|
- eq(utils.parseaddr(['alice@example.org(<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org)<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org<<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org><bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org@<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org,<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org;<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org:<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org.<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org"<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org[<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
- eq(utils.parseaddr(['alice@example.org]<bob@example.com>']),
|
||||||
|
- ('', ''))
|
||||||
|
+ 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_getaddresses_nasty(self):
|
||||||
|
eq = self.assertEqual
|
||||||
|
eq(utils.getaddresses(['foo: ;']), [('', '')])
|
||||||
|
- eq(utils.getaddresses(['[]*-- =~$']), [('', '')])
|
||||||
|
+ eq(utils.getaddresses(
|
||||||
|
+ ['[]*-- =~$']),
|
||||||
|
+ [('', ''), ('', ''), ('', '*--')])
|
||||||
|
eq(utils.getaddresses(
|
||||||
|
['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
|
||||||
|
[('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- [r'Pete(A nice \) chap) <pete(his account)@silly.test(his host)>']),
|
||||||
|
- [('Pete (A nice ) chap his account his host)', 'pete@silly.test')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['(Empty list)(start)Undisclosed recipients :(nobody(I know))']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['Mary <@machine.tld:mary@example.net>, , jdoe@test . example']),
|
||||||
|
- [('Mary', 'mary@example.net'), ('', ''), ('', 'jdoe@test.example')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['John Doe <jdoe@machine(comment). example>']),
|
||||||
|
- [('John Doe (comment)', 'jdoe@machine.example')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['"Mary Smith: Personal Account" <smith@home.example>']),
|
||||||
|
- [('Mary Smith: Personal Account', 'smith@home.example')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- ['Undisclosed recipients:;']),
|
||||||
|
- [('', '')])
|
||||||
|
- eq(utils.getaddresses(
|
||||||
|
- [r'<boss@nil.test>, "Giant; \"Big\" Box" <bob@example.net>']),
|
||||||
|
- [('', 'boss@nil.test'), ('Giant; "Big" Box', 'bob@example.net')])
|
||||||
|
|
||||||
|
def test_getaddresses_embedded_comment(self):
|
||||||
|
"""Test proper handling of a nested comment"""
|
||||||
|
diff --git a/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst b/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
index e0434cc..c67ec45 100644
|
||||||
|
--- a/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
+++ b/Misc/NEWS.d/next/Security/2023-06-13-20-52-24.gh-issue-102988.Kei7Vf.rst
|
||||||
|
@@ -1,4 +1,4 @@
|
||||||
|
-CVE-2023-27043: Prevent :func:`email.utils.parseaddr`
|
||||||
|
-and :func:`email.utils.getaddresses` from returning the realname portion of an
|
||||||
|
-invalid RFC2822 email header in the email address portion of the 2-tuple
|
||||||
|
-returned after being parsed by :class:`email._parseaddr.AddressList`.
|
||||||
|
+Reverted the :mod:`email.utils` security improvement change released in
|
||||||
|
+3.12beta4 that unintentionally caused :mod:`email.utils.getaddresses` to fail
|
||||||
|
+to parse email addresses with a comma in the quoted name field.
|
||||||
|
+See :gh:`106669`.
|
||||||
|
--
|
||||||
|
2.27.0
|
||||||
|
|
||||||
81
python3.spec
81
python3.spec
@ -3,7 +3,7 @@ Summary: Interpreter of the Python3 programming language
|
|||||||
URL: https://www.python.org/
|
URL: https://www.python.org/
|
||||||
|
|
||||||
Version: 3.7.9
|
Version: 3.7.9
|
||||||
Release: 37
|
Release: 43
|
||||||
License: Python-2.0
|
License: Python-2.0
|
||||||
|
|
||||||
%global branchversion 3.7
|
%global branchversion 3.7
|
||||||
@ -73,7 +73,9 @@ BuildRequires: tcl-devel
|
|||||||
BuildRequires: tix-devel
|
BuildRequires: tix-devel
|
||||||
BuildRequires: tk-devel
|
BuildRequires: tk-devel
|
||||||
|
|
||||||
|
%ifnarch loongarch64
|
||||||
BuildRequires: valgrind-devel
|
BuildRequires: valgrind-devel
|
||||||
|
%endif
|
||||||
|
|
||||||
BuildRequires: xz-devel
|
BuildRequires: xz-devel
|
||||||
BuildRequires: zlib-devel
|
BuildRequires: zlib-devel
|
||||||
@ -176,6 +178,22 @@ Patch6065: backport-0003-CVE-2023-40217.patch
|
|||||||
patch9000: Don-t-override-PYTHONPATH-which-is-already-set.patch
|
patch9000: Don-t-override-PYTHONPATH-which-is-already-set.patch
|
||||||
patch9001: add-the-sm3-method-for-obtaining-the-salt-value.patch
|
patch9001: add-the-sm3-method-for-obtaining-the-salt-value.patch
|
||||||
Patch9002: fix-CVE-2023-24329.patch
|
Patch9002: fix-CVE-2023-24329.patch
|
||||||
|
Patch9003: backport-Fix-parsing-errors-in-email-_parseaddr.py.patch
|
||||||
|
Patch9004: backport-Revert-fixes-for-CVE-2023-27043.patch
|
||||||
|
Patch9005: backport-CVE-2023-27043.patch
|
||||||
|
Patch9006: Add-loongarch-support.patch
|
||||||
|
|
||||||
|
# fix CVE-2007-4559
|
||||||
|
Patch9007: backport-3.7-gh-102950-Implement-PEP-706-Filter-for-tarfile.e.patch
|
||||||
|
Patch9008: backport-3.7-gh-107845-Fix-symlink-handling-for-tarfile.data_.patch
|
||||||
|
# fix test error
|
||||||
|
Patch9009: backport-3.7-gh-115133-Fix-test_xml_etree-error-with-expat-ve.patch
|
||||||
|
|
||||||
|
Patch9010: backport-3.7-gh-104049-do-not-expose-on-disk-location-from-Si.patch
|
||||||
|
Patch9011: backport-3.7-gh-99889-Fix-directory-traversal-security-flaw-i.patch
|
||||||
|
Patch9012: 0001-expected_algs-list-to-include-TLS_SM4.patch
|
||||||
|
Patch9013: backport-3.7-gh-124651-CVE-2024-9287.patch
|
||||||
|
|
||||||
|
|
||||||
Provides: python%{branchversion} = %{version}-%{release}
|
Provides: python%{branchversion} = %{version}-%{release}
|
||||||
Provides: python(abi) = %{branchversion}
|
Provides: python(abi) = %{branchversion}
|
||||||
@ -336,6 +354,19 @@ rm Lib/ensurepip/_bundled/*.whl
|
|||||||
%patch9000 -p1
|
%patch9000 -p1
|
||||||
%patch9001 -p1
|
%patch9001 -p1
|
||||||
%patch9002 -p1
|
%patch9002 -p1
|
||||||
|
%patch9003 -p1
|
||||||
|
%patch9004 -p1
|
||||||
|
%patch9005 -p1
|
||||||
|
%patch9006 -p1
|
||||||
|
|
||||||
|
%patch9007 -p1
|
||||||
|
%patch9008 -p1
|
||||||
|
%patch9009 -p1
|
||||||
|
|
||||||
|
%patch9010 -p1
|
||||||
|
%patch9011 -p1
|
||||||
|
%patch9012 -p1
|
||||||
|
%patch9013 -p1
|
||||||
|
|
||||||
sed -i "s/generic_os/%{_vendor}/g" Lib/platform.py
|
sed -i "s/generic_os/%{_vendor}/g" Lib/platform.py
|
||||||
rm configure pyconfig.h.in
|
rm configure pyconfig.h.in
|
||||||
@ -381,7 +412,9 @@ pushd ${DebugBuildDir}
|
|||||||
--enable-loadable-sqlite-extensions \
|
--enable-loadable-sqlite-extensions \
|
||||||
--with-dtrace \
|
--with-dtrace \
|
||||||
--with-ssl-default-suites=openssl \
|
--with-ssl-default-suites=openssl \
|
||||||
|
%ifnarch loongarch64
|
||||||
--with-valgrind \
|
--with-valgrind \
|
||||||
|
%endif
|
||||||
--without-ensurepip \
|
--without-ensurepip \
|
||||||
--with-pydebug
|
--with-pydebug
|
||||||
|
|
||||||
@ -405,7 +438,9 @@ pushd ${OptimizedBuildDir}
|
|||||||
--enable-loadable-sqlite-extensions \
|
--enable-loadable-sqlite-extensions \
|
||||||
--with-dtrace \
|
--with-dtrace \
|
||||||
--with-ssl-default-suites=openssl \
|
--with-ssl-default-suites=openssl \
|
||||||
|
%ifnarch loongarch64
|
||||||
--with-valgrind \
|
--with-valgrind \
|
||||||
|
%endif
|
||||||
--without-ensurepip \
|
--without-ensurepip \
|
||||||
%{optimizations_flag}
|
%{optimizations_flag}
|
||||||
|
|
||||||
@ -937,14 +972,54 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP"
|
|||||||
%{_mandir}/*/*
|
%{_mandir}/*/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Fri Mar 01 GuoCe <guoce@kylinos.cn> - 3.7.9-37
|
* Thu Dec 19 2024 wangshuo <wangshuo@kylinos.cn> - 3.7.9-43
|
||||||
|
- Type:CVE
|
||||||
|
- CVE:CVE-2024-9287
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:fix CVE-2024-9287
|
||||||
|
- gh-124651: Quote template strings in `venv` activation scripts
|
||||||
|
|
||||||
|
* Wed Dec 11 2024 wangshuo <wangshuo@kylinos.cn> - 3.7.9-42
|
||||||
|
- Type:update
|
||||||
|
- ID:NA
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:support TLS_SM4
|
||||||
|
|
||||||
|
* Tue Oct 29 2024 wangshuo <wangshuo@kylinos.cn> - 3.7.9-41
|
||||||
|
- Type:bugfix
|
||||||
|
- ID:NA
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:backport upstream patches
|
||||||
|
- gh-104049: do not expose on-disk location from SimpleHTTPRequestHandler
|
||||||
|
- gh-99889: Fix directory traversal security flaw in uu.decode()
|
||||||
|
|
||||||
|
* Fri Oct 25 2024 wangshuo <wangshuo@kylinos.cn> - 3.7.9-40
|
||||||
|
- Type:CVE
|
||||||
|
- CVE:CVE-2007-4559
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:Patch9007-9008, fix CVE-2007-4559
|
||||||
|
- Patch9009, fix test_xml_etree error with expat versions that fix CVE-2023-52425
|
||||||
|
|
||||||
|
* Wed Mar 06 2024 GuoCe <guoce@kylinos.cn> - 3.7.9-39
|
||||||
|
- Type:bugfix
|
||||||
|
- CVE:NA
|
||||||
|
- SUG:NA
|
||||||
|
- DESC: add loongarch64 support and disable valgrind-devel for loongarch64
|
||||||
|
|
||||||
|
* Tue Mar 05 2024 GuoCe <guoce@kylinos.cn> - 3.7.9-38
|
||||||
|
- Type:CVE
|
||||||
|
- CVE:CVE-2023-27043
|
||||||
|
- SUG:NA
|
||||||
|
- DESC:fix CVE-2023-27043
|
||||||
|
|
||||||
|
* Fri Mar 01 2024 GuoCe <guoce@kylinos.cn> - 3.7.9-37
|
||||||
- Type:bugfix
|
- Type:bugfix
|
||||||
- CVE:NA
|
- CVE:NA
|
||||||
- SUG:NA
|
- SUG:NA
|
||||||
- DESC:Modify the spec file to synchronize the version information of
|
- DESC:Modify the spec file to synchronize the version information of
|
||||||
CHANGLOG and VERSION.
|
CHANGLOG and VERSION.
|
||||||
|
|
||||||
* Tue Sep 19 zhuofeng <zhuofeng2@huawei.com> - 3.7.9-36
|
* Tue Sep 19 2023 zhuofeng <zhuofeng2@huawei.com> - 3.7.9-36
|
||||||
- Type:CVE
|
- Type:CVE
|
||||||
- CVE:CVE-2023-40217
|
- CVE:CVE-2023-40217
|
||||||
- SUG:NA
|
- SUG:NA
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user