Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion python-3.10.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package:
name: python-3.10
version: 3.10.13
epoch: 4
epoch: 5
description: "the Python programming language"
copyright:
- license: PSF-2.0
Expand Down Expand Up @@ -41,6 +41,14 @@ pipeline:
Modules/_ctypes/darwin* \
Modules/_ctypes/libffi*

- uses: patch
with:
patches: CVE-2023-27043.patch

- uses: patch
with:
patches: CVE-2023-27043-enable-disable.patch

- name: Configure
runs: |
./configure \
Expand Down Expand Up @@ -113,3 +121,5 @@ test:
pipeline:
- runs: |
python3 --version
- runs: |
python3.10 CVE-2023-27043-unittest.py
149 changes: 149 additions & 0 deletions python-3.10/CVE-2023-27043-enable-disable.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
From 004e2014b1ecef428530857f57e82668ed2947bd Mon Sep 17 00:00:00 2001
From: Scott Moser <smoser@chainguard.dev>
Date: Tue, 27 Feb 2024 17:13:23 +0000
Subject: [PATCH 2/2] Change default value for strict to be dependent on
environment var

This follows the general solution described at:
https://access.redhat.com/articles/7051467

The differences are:
1. it does not support /etc/python/email.cfg
2. environment variable is named PYTHON_EMAIL_STRICT_PARSING_DEFAULT
It loosely controls the default 'strict' value of getaddresses and
parseaddr.

If the variable is unset or set to any value other than 'false'
or '0', then strict=True is used.

To opt out of this security fix, set the environment variable

PYTHON_EMAIL_STRICT_PARSING_DEFAULT=false
---
Lib/email/utils.py | 29 ++++++++++++++--
Lib/test/test_email/test_email_notstrict.py | 38 +++++++++++++++++++++
2 files changed, 65 insertions(+), 2 deletions(-)
create mode 100644 Lib/test/test_email/test_email_notstrict.py

diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index 9522341fab..687edaba78 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -48,6 +48,8 @@
specialsre = re.compile(r'[][\\()<>@,:;".]')
escapesre = re.compile(r'[\\"]')

+_parseaddr_strict_default = None
+

def _has_surrogates(s):
"""Return True if s contains surrogate-escaped binary data."""
@@ -149,7 +151,7 @@ def _strip_quoted_realnames(addr):

supports_strict_parsing = True

-def getaddresses(fieldvalues, *, strict=True):
+def getaddresses(fieldvalues, *, strict=None):
"""Return a list of (REALNAME, EMAIL) or ('','') for each fieldvalue.

When parsing fails for a fieldvalue, a 2-tuple of ('', '') is returned in
@@ -157,6 +159,7 @@ def getaddresses(fieldvalues, *, strict=True):

If strict is true, use a strict parser which rejects malformed inputs.
"""
+ strict = _get_default_parseaddr_strict(strict)

# 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
@@ -321,7 +324,7 @@ def parsedate_to_datetime(data):
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))


-def parseaddr(addr, *, strict=True):
+def parseaddr(addr, *, strict=None):
"""
Parse addr into its constituent realname and email address parts.

@@ -330,6 +333,8 @@ def parseaddr(addr, *, strict=True):

If strict is True, use a strict parser which rejects malformed inputs.
"""
+ strict = _get_default_parseaddr_strict(strict)
+
if not strict:
addrs = _AddressList(addr).addresslist
if not addrs:
@@ -351,6 +356,26 @@ def parseaddr(addr, *, strict=True):
return addrs[0]


+# get default value for strict parameter in parseaddr and getaddresses
+def _get_default_parseaddr_strict(val):
+ # non-None value passed into function, use it.
+ if val is not None:
+ return val
+
+ # consult or update the cached global.
+ global _parseaddr_strict_default
+
+ if _parseaddr_strict_default is None:
+ val = os.environ.get("PYTHON_EMAIL_STRICT_PARSING_DEFAULT", "true")
+ # env var with 'false' explicitly disables the disabling (meaning strict=true)
+ if val in ("false", "0"):
+ _parseaddr_strict_default = False
+ else:
+ _parseaddr_strict_default = True
+
+ return _parseaddr_strict_default
+
+
# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3.
def unquote(str):
"""Remove quotes from a string."""
diff --git a/Lib/test/test_email/test_email_notstrict.py b/Lib/test/test_email/test_email_notstrict.py
new file mode 100644
index 0000000000..fe8617cfcb
--- /dev/null
+++ b/Lib/test/test_email/test_email_notstrict.py
@@ -0,0 +1,38 @@
+"""
+This is the test_getaddresses_nasty function with the triggering
+test case that was added for this fix. We test that
+setting PYTHON_EMAIL_STRICT_PARSING_DEFAULT to false gives
+the old behavior and to true gives new behavior
+"""
+
+import unittest
+
+from unittest.mock import patch
+
+from email import utils
+
+expected_strict = [('', '')]
+expected_nonstrict = [('', ''), ('', ''), ('', '*--')]
+
+@patch('os.environ', {"PYTHON_EMAIL_STRICT_PARSING_DEFAULT": "false"})
+@patch('email.utils._parseaddr_strict_default', None)
+class TestNonstrictParsing(unittest.TestCase):
+ def test_getaddresses_nasty(self):
+ self.assertEqual(utils.getaddresses(
+ ['[]*-- =~$']), expected_nonstrict)
+
+@patch('os.environ', {"PYTHON_EMAIL_STRICT_PARSING_DEFAULT": "true"})
+@patch('email.utils._parseaddr_strict_default', None)
+class TestStrictParsing(unittest.TestCase):
+ def test_getaddresses_nasty(self):
+ self.assertEqual(utils.getaddresses(
+ ['[]*-- =~$']), expected_strict)
+
+"""This test would fail if env had PYTHON_EMAIL_STRICT_PARSING_DEFAULT=false"""
+class TestStrictNoEnvParsing(unittest.TestCase):
+ def test_getaddresses_nasty(self):
+ self.assertEqual(utils.getaddresses(
+ ['[]*-- =~$']), expected_strict)
+
+if __name__ == '__main__':
+ unittest.main()
--
2.44.0

38 changes: 38 additions & 0 deletions python-3.10/CVE-2023-27043-unittest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
This is the test_getaddresses_nasty function with the triggering
test case that was added for this fix. We test that
setting PYTHON_EMAIL_STRICT_PARSING_DEFAULT to false gives
the old behavior and to true gives new behavior
"""

import unittest

from unittest.mock import patch

from email import utils

expected_strict = [('', '')]
expected_nonstrict = [('', ''), ('', ''), ('', '*--')]

@patch('os.environ', {"PYTHON_EMAIL_STRICT_PARSING_DEFAULT": "false"})
@patch('email.utils._parseaddr_strict_default', None)
class TestNonstrictParsing(unittest.TestCase):
def test_getaddresses_nasty(self):
self.assertEqual(utils.getaddresses(
['[]*-- =~$']), expected_nonstrict)

@patch('os.environ', {"PYTHON_EMAIL_STRICT_PARSING_DEFAULT": "true"})
@patch('email.utils._parseaddr_strict_default', None)
class TestStrictParsing(unittest.TestCase):
def test_getaddresses_nasty(self):
self.assertEqual(utils.getaddresses(
['[]*-- =~$']), expected_strict)

"""This test would fail if env had PYTHON_EMAIL_STRICT_PARSING_DEFAULT=false"""
class TestStrictNoEnvParsing(unittest.TestCase):
def test_getaddresses_nasty(self):
self.assertEqual(utils.getaddresses(
['[]*-- =~$']), expected_strict)

if __name__ == '__main__':
unittest.main()
Loading