From e9291e95e786bfd388c6aaed9a4b848ac4d061339e2c93919ea27f43b36f31ee Mon Sep 17 00:00:00 2001 From: Marco Strigl Date: Wed, 6 Nov 2024 10:14:01 +0000 Subject: [PATCH] Set link to python310.36315 via maintenance_release request --- CVE-2024-6923-email-hdr-inject.patch | 335 ------------------------- CVE-2024-9287-venv_path_unquoted.patch | 296 ++++++++++++++++++++++ python310.changes | 19 ++ python310.spec | 9 + 4 files changed, 324 insertions(+), 335 deletions(-) delete mode 100644 CVE-2024-6923-email-hdr-inject.patch create mode 100644 CVE-2024-9287-venv_path_unquoted.patch diff --git a/CVE-2024-6923-email-hdr-inject.patch b/CVE-2024-6923-email-hdr-inject.patch deleted file mode 100644 index fe6cde5..0000000 --- a/CVE-2024-6923-email-hdr-inject.patch +++ /dev/null @@ -1,335 +0,0 @@ -From ff3629b3cedf5e50a7a5f567283778fe5539a11e Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Wed, 31 Jul 2024 00:19:48 +0200 -Subject: [PATCH] [3.10] gh-121650: Encode newlines in headers, and verify - headers are sound (GH-122233) - -Per RFC 2047: - -> [...] these encoding schemes allow the -> encoding of arbitrary octet values, mail readers that implement this -> decoding should also ensure that display of the decoded data on the -> recipient's terminal will not cause unwanted side-effects - -It seems that the "quoted-word" scheme is a valid way to include -a newline character in a header value, just like we already allow -undecodable bytes or control characters. -They do need to be properly quoted when serialized to text, though. - -This should fail for custom fold() implementations that aren't careful -about newlines. - -(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384) - -Co-authored-by: Petr Viktorin -Co-authored-by: Bas Bloemsaat -Co-authored-by: Serhiy Storchaka ---- - Doc/library/email.errors.rst | 6 - Doc/library/email.policy.rst | 18 ++ - Doc/whatsnew/3.10.rst | 12 + - Lib/email/_header_value_parser.py | 12 + - Lib/email/_policybase.py | 8 + - Lib/email/errors.py | 4 - Lib/email/generator.py | 13 +- - Lib/test/test_email/test_generator.py | 62 ++++++++++ - Lib/test/test_email/test_policy.py | 26 ++++ - Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5 - 10 files changed, 162 insertions(+), 4 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst - ---- a/Doc/library/email.errors.rst -+++ b/Doc/library/email.errors.rst -@@ -59,6 +59,12 @@ The following exception classes are defi - :class:`~email.mime.image.MIMEImage`). - - -+.. exception:: HeaderWriteError() -+ -+ Raised when an error occurs when the :mod:`~email.generator` outputs -+ headers. -+ -+ - Here is the list of the defects that the :class:`~email.parser.FeedParser` - can find while parsing messages. Note that the defects are added to the message - where the problem was found, so for example, if a message nested inside a ---- a/Doc/library/email.policy.rst -+++ b/Doc/library/email.policy.rst -@@ -229,6 +229,24 @@ added matters. To illustrate:: - - .. versionadded:: 3.6 - -+ -+ .. attribute:: verify_generated_headers -+ -+ If ``True`` (the default), the generator will raise -+ :exc:`~email.errors.HeaderWriteError` instead of writing a header -+ that is improperly folded or delimited, such that it would -+ be parsed as multiple headers or joined with adjacent data. -+ Such headers can be generated by custom header classes or bugs -+ in the ``email`` module. -+ -+ As it's a security feature, this defaults to ``True`` even in the -+ :class:`~email.policy.Compat32` policy. -+ For backwards compatible, but unsafe, behavior, it must be set to -+ ``False`` explicitly. -+ -+ .. versionadded:: 3.10.15 -+ -+ - The following :class:`Policy` method is intended to be called by code using - the email library to create policy instances with custom settings: - ---- a/Doc/whatsnew/3.10.rst -+++ b/Doc/whatsnew/3.10.rst -@@ -2357,3 +2357,15 @@ ipaddress - - * Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, - ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. -+ -+email -+----- -+ -+* Headers with embedded newlines are now quoted on output. -+ -+ The :mod:`~email.generator` will now refuse to serialize (write) headers -+ that are improperly folded or delimited, such that they would be parsed as -+ multiple headers or joined with adjacent data. -+ If you need to turn this safety feature off, -+ set :attr:`~email.policy.Policy.verify_generated_headers`. -+ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.) ---- a/Lib/email/_header_value_parser.py -+++ b/Lib/email/_header_value_parser.py -@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP - ASPECIALS = TSPECIALS | set("*'%") - ATTRIBUTE_ENDS = ASPECIALS | WSP - EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') -+NLSET = {'\n', '\r'} -+SPECIALSNL = SPECIALS | NLSET - - def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' -@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po - wrap_as_ew_blocked -= 1 - continue - tstr = str(part) -- if part.token_type == 'ptext' and set(tstr) & SPECIALS: -- # Encode if tstr contains special characters. -- want_encoding = True -+ if not want_encoding: -+ if part.token_type == 'ptext': -+ # Encode if tstr contains special characters. -+ want_encoding = not SPECIALSNL.isdisjoint(tstr) -+ else: -+ # Encode if tstr contains newlines. -+ want_encoding = not NLSET.isdisjoint(tstr) - try: - tstr.encode(encoding) - charset = encoding ---- a/Lib/email/_policybase.py -+++ b/Lib/email/_policybase.py -@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc. - message_factory -- the class to use to create new message objects. - If the value is None, the default is Message. - -+ verify_generated_headers -+ -- if true, the generator verifies that each header -+ they are properly folded, so that a parser won't -+ treat it as multiple headers, start-of-body, or -+ part of another header. -+ This is a check against custom Header & fold() -+ implementations. - """ - - raise_on_defect = False -@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc. - max_line_length = 78 - mangle_from_ = False - message_factory = None -+ verify_generated_headers = True - - def handle_defect(self, obj, defect): - """Based on policy, either raise defect or call register_defect. ---- a/Lib/email/errors.py -+++ b/Lib/email/errors.py -@@ -29,6 +29,10 @@ class CharsetError(MessageError): - """An illegal charset was given.""" - - -+class HeaderWriteError(MessageError): -+ """Error while writing headers.""" -+ -+ - # These are parsing defects which the parser was able to work around. - class MessageDefect(ValueError): - """Base class for a message defect.""" ---- a/Lib/email/generator.py -+++ b/Lib/email/generator.py -@@ -14,12 +14,14 @@ import random - from copy import deepcopy - from io import StringIO, BytesIO - from email.utils import _has_surrogates -+from email.errors import HeaderWriteError - - UNDERSCORE = '_' - NL = '\n' # XXX: no longer used by the code below. - - NLCRE = re.compile(r'\r\n|\r|\n') - fcre = re.compile(r'^From ', re.MULTILINE) -+NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') - - - -@@ -223,7 +225,16 @@ class Generator: - - def _write_headers(self, msg): - for h, v in msg.raw_items(): -- self.write(self.policy.fold(h, v)) -+ folded = self.policy.fold(h, v) -+ if self.policy.verify_generated_headers: -+ linesep = self.policy.linesep -+ if not folded.endswith(self.policy.linesep): -+ raise HeaderWriteError( -+ f'folded header does not end with {linesep!r}: {folded!r}') -+ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)): -+ raise HeaderWriteError( -+ f'folded header contains newline: {folded!r}') -+ self.write(folded) - # A blank line always separates headers from body - self.write(self._NL) - ---- a/Lib/test/test_email/test_generator.py -+++ b/Lib/test/test_email/test_generator.py -@@ -6,6 +6,7 @@ from email.message import EmailMessage - from email.generator import Generator, BytesGenerator - from email.headerregistry import Address - from email import policy -+import email.errors - from test.test_email import TestEmailBase, parameterize - - -@@ -216,6 +217,44 @@ class TestGeneratorBase: - g.flatten(msg) - self.assertEqual(s.getvalue(), self.typ(expected)) - -+ def test_keep_encoded_newlines(self): -+ msg = self.msgmaker(self.typ(textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """))) -+ expected = textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """) -+ s = self.ioclass() -+ g = self.genclass(s, policy=self.policy.clone(max_line_length=80)) -+ g.flatten(msg) -+ self.assertEqual(s.getvalue(), self.typ(expected)) -+ -+ def test_keep_long_encoded_newlines(self): -+ msg = self.msgmaker(self.typ(textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com -+ -+ None -+ """))) -+ expected = textwrap.dedent("""\ -+ To: nobody -+ Subject: Bad subject -+ =?utf-8?q?=0A?=Bcc: -+ injection@example.com -+ -+ None -+ """) -+ s = self.ioclass() -+ g = self.genclass(s, policy=self.policy.clone(max_line_length=30)) -+ g.flatten(msg) -+ self.assertEqual(s.getvalue(), self.typ(expected)) -+ - - class TestGenerator(TestGeneratorBase, TestEmailBase): - -@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T - ioclass = io.StringIO - typ = str - -+ def test_verify_generated_headers(self): -+ """gh-121650: by default the generator prevents header injection""" -+ class LiteralHeader(str): -+ name = 'Header' -+ def fold(self, **kwargs): -+ return self -+ -+ for text in ( -+ 'Value\r\nBad Injection\r\n', -+ 'NoNewLine' -+ ): -+ with self.subTest(text=text): -+ message = message_from_string( -+ "Header: Value\r\n\r\nBody", -+ policy=self.policy, -+ ) -+ -+ del message['Header'] -+ message['Header'] = LiteralHeader(text) -+ -+ with self.assertRaises(email.errors.HeaderWriteError): -+ message.as_string() -+ - - class TestBytesGenerator(TestGeneratorBase, TestEmailBase): - ---- a/Lib/test/test_email/test_policy.py -+++ b/Lib/test/test_email/test_policy.py -@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase): - 'raise_on_defect': False, - 'mangle_from_': True, - 'message_factory': None, -+ 'verify_generated_headers': True, - } - # These default values are the ones set on email.policy.default. - # If any of these defaults change, the docs must be updated. -@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase): - with self.assertRaises(email.errors.HeaderParseError): - policy.fold("Subject", subject) - -+ def test_verify_generated_headers(self): -+ """Turning protection off allows header injection""" -+ policy = email.policy.default.clone(verify_generated_headers=False) -+ for text in ( -+ 'Header: Value\r\nBad: Injection\r\n', -+ 'Header: NoNewLine' -+ ): -+ with self.subTest(text=text): -+ message = email.message_from_string( -+ "Header: Value\r\n\r\nBody", -+ policy=policy, -+ ) -+ class LiteralHeader(str): -+ name = 'Header' -+ def fold(self, **kwargs): -+ return self -+ -+ del message['Header'] -+ message['Header'] = LiteralHeader(text) -+ -+ self.assertEqual( -+ message.as_string(), -+ f"{text}\nBody", -+ ) -+ - # XXX: Need subclassing tests. - # For adding subclassed objects, make sure the usual rules apply (subclass - # wins), but that the order still works (right overrides left). ---- /dev/null -+++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst -@@ -0,0 +1,5 @@ -+:mod:`email` headers with embedded newlines are now quoted on output. The -+:mod:`~email.generator` will now refuse to serialize (write) headers that -+are unsafely folded or delimited; see -+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas -+Bloemsaat and Petr Viktorin in :gh:`121650`.) diff --git a/CVE-2024-9287-venv_path_unquoted.patch b/CVE-2024-9287-venv_path_unquoted.patch new file mode 100644 index 0000000..93f30e1 --- /dev/null +++ b/CVE-2024-9287-venv_path_unquoted.patch @@ -0,0 +1,296 @@ +From 21139b45039a72e8346bdc32d498345ef174ba92 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Fri, 1 Nov 2024 14:11:47 +0100 +Subject: [PATCH] [3.11] gh-124651: Quote template strings in `venv` activation + scripts (GH-124712) (GH-126185) (#126269) + +(cherry picked from commit ae961ae94bf19c8f8c7fbea3d1c25cc55ce8ae97) +--- + Lib/test/test_venv.py | 81 ++++++++++ + Lib/venv/__init__.py | 42 ++++- + Lib/venv/scripts/common/activate | 8 + Lib/venv/scripts/nt/activate.bat | 6 + Lib/venv/scripts/posix/activate.csh | 8 + Lib/venv/scripts/posix/activate.fish | 8 + Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst | 1 + 7 files changed, 134 insertions(+), 20 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst + +--- a/Lib/test/test_venv.py ++++ b/Lib/test/test_venv.py +@@ -15,6 +15,7 @@ import struct + import subprocess + import sys + import tempfile ++import shlex + from test.support import (captured_stdout, captured_stderr, requires_zlib, + skip_if_broken_multiprocessing_synchronize) + from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) +@@ -85,6 +86,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.""" + +@@ -342,6 +347,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): + """ +--- a/Lib/venv/__init__.py ++++ b/Lib/venv/__init__.py +@@ -11,6 +11,7 @@ import subprocess + import sys + import sysconfig + import types ++import shlex + + + CORE_VENV_DEPS = ('pip', 'setuptools') +@@ -364,11 +365,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): +@@ -408,6 +439,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) +--- a/Lib/venv/scripts/common/activate ++++ b/Lib/venv/scripts/common/activate +@@ -38,11 +38,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 +@@ -55,9 +55,9 @@ fi + + if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" +- PS1="__VENV_PROMPT__${PS1:-}" ++ PS1=__VENV_PROMPT__"${PS1:-}" + export PS1 +- VIRTUAL_ENV_PROMPT="__VENV_PROMPT__" ++ VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ + export VIRTUAL_ENV_PROMPT + fi + +--- 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,8 +24,8 @@ 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 VIRTUAL_ENV_PROMPT=__VENV_PROMPT__ ++set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" ++set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__" + + :END + if defined _OLD_CODEPAGE ( +--- a/Lib/venv/scripts/posix/activate.csh ++++ b/Lib/venv/scripts/posix/activate.csh +@@ -8,17 +8,17 @@ alias deactivate 'test $?_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" + + if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then +- set prompt = "__VENV_PROMPT__$prompt" +- setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" ++ set prompt = __VENV_PROMPT__"$prompt" ++ setenv VIRTUAL_ENV_PROMPT __VENV_PROMPT__ + endif + + alias pydoc python -m pydoc +--- a/Lib/venv/scripts/posix/activate.fish ++++ b/Lib/venv/scripts/posix/activate.fish +@@ -33,10 +33,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 +@@ -56,7 +56,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. +- printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal) ++ printf "%s%s%s" (set_color 4B8BBE) __VENV_PROMPT__ (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . +@@ -65,5 +65,5 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +- set -gx VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" ++ set -gx VIRTUAL_ENV_PROMPT __VENV_PROMPT__ + end +--- /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. diff --git a/python310.changes b/python310.changes index 20e1f0c..385f82c 100644 --- a/python310.changes +++ b/python310.changes @@ -1,3 +1,22 @@ +------------------------------------------------------------------- +Fri Nov 1 21:38:45 UTC 2024 - Matej Cepl + +- Update CVE-2024-9287-venv_path_unquoted.patch according to the + upstream PR gh#python/cpython!126301. + +------------------------------------------------------------------- +Thu Oct 24 16:09:00 UTC 2024 - Matej Cepl + +- Add CVE-2024-9287-venv_path_unquoted.patch to properly quote + path names provided when creating a virtual environment + (bsc#1232241, CVE-2024-9287) + +------------------------------------------------------------------- +Wed Oct 2 16:18:29 UTC 2024 - Matej Cepl + +- Drop .pyc files from docdir for reproducible builds + (bsc#1230906). + ------------------------------------------------------------------- Mon Sep 9 13:41:07 UTC 2024 - Matej Cepl diff --git a/python310.spec b/python310.spec index 48d2192..fe74b4e 100644 --- a/python310.spec +++ b/python310.spec @@ -204,6 +204,9 @@ Patch27: gh120226-fix-sendfile-test-kernel-610.patch # PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com # status_iterator method moved between the Sphinx versions Patch28: sphinx-802.patch +# PATCH-FIX-UPSTREAM CVE-2024-9287-venv_path_unquoted.patch gh#python/cpython#124651 mcepl@suse.com +# venv should properly quote path names provided when creating a venv +Patch29: CVE-2024-9287-venv_path_unquoted.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -483,6 +486,7 @@ other applications. %patch -p1 -P 24 %patch -p1 -P 27 %patch -p1 -P 28 +%patch -p1 -P 29 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac @@ -819,6 +823,11 @@ LD_LIBRARY_PATH=. ./python -O -c "from py_compile import compile; compile('$FAIL echo %{sitedir}/_import_failed > %{buildroot}/%{sitedir}/site-packages/zzzz-import-failed-hooks.pth %endif +# For the purposes of reproducibility, it is necessary to eliminate any *.pyc files inside documentation dirs +if [ -d %{buildroot}%{_defaultdocdir} ] ; then +find %{buildroot}%{_defaultdocdir} -type f -name \*.pyc -ls -exec rm -vf '{}' \; +fi + %if %{with general} %files -n %{python_pkg_name}-tk %{sitedir}/tkinter