From 547544ae921b17f6648a3614525598c251216b83 Mon Sep 17 00:00:00 2001 From: Liwei Ge Date: Wed, 28 Jun 2023 11:16:44 +0800 Subject: [PATCH] Fix CVE-2023-24329 Signed-off-by: Liwei Ge --- ...C0-control-and-space-chars-in-urlspl.patch | 134 ++++++++++++++++++ python39.spec | 7 +- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 10002-start-stripping-C0-control-and-space-chars-in-urlspl.patch diff --git a/10002-start-stripping-C0-control-and-space-chars-in-urlspl.patch b/10002-start-stripping-C0-control-and-space-chars-in-urlspl.patch new file mode 100644 index 0000000..7614a4c --- /dev/null +++ b/10002-start-stripping-C0-control-and-space-chars-in-urlspl.patch @@ -0,0 +1,134 @@ +From 96220c087a89ba5ecc9ee146f49d9bbde0e26835 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 11:11:57 +0800 +Subject: [PATCH] start stripping C0 control and space chars in urlsplit + +backport patch from +https://github.com/python/cpython/pull/104593/commits/ +87819d8478f8ce321ca248ac3549e62390ec33a9 + +Signed-off-by: rpm-build +--- + Lib/test/test_urlparse.py | 61 ++++++++++++++++++++++++++++++++++++++- + Lib/urllib/parse.py | 12 ++++++++ + 2 files changed, 72 insertions(+), 1 deletion(-) + +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index 31943f3..574da5b 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -649,6 +649,65 @@ class UrlParseTestCase(unittest.TestCase): + self.assertEqual(p.scheme, "http") + self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") + ++ def test_urlsplit_strip_url(self): ++ noise = bytes(range(0, 0x20 + 1)) ++ base_url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" ++ ++ url = noise.decode("utf-8") + base_url ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, "http") ++ self.assertEqual(p.netloc, "User:Pass@www.python.org:080") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query=yes") ++ self.assertEqual(p.fragment, "frag") ++ self.assertEqual(p.username, "User") ++ self.assertEqual(p.password, "Pass") ++ self.assertEqual(p.hostname, "www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url) ++ ++ url = noise + base_url.encode("utf-8") ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, b"http") ++ self.assertEqual(p.netloc, b"User:Pass@www.python.org:080") ++ self.assertEqual(p.path, b"/doc/") ++ self.assertEqual(p.query, b"query=yes") ++ self.assertEqual(p.fragment, b"frag") ++ self.assertEqual(p.username, b"User") ++ self.assertEqual(p.password, b"Pass") ++ self.assertEqual(p.hostname, b"www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url.encode("utf-8")) ++ ++ # Test that trailing space is preserved as some applications rely on ++ # this within query strings. ++ query_spaces_url = "https://www.python.org:88/doc/?query= " ++ p = urllib.parse.urlsplit(noise.decode("utf-8") + query_spaces_url) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.netloc, "www.python.org:88") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query= ") ++ self.assertEqual(p.port, 88) ++ self.assertEqual(p.geturl(), query_spaces_url) ++ ++ p = urllib.parse.urlsplit("www.pypi.org ") ++ # That "hostname" gets considered a "path" due to the ++ # trailing space and our existing logic... YUCK... ++ # and re-assembles via geturl aka unurlsplit into the original. ++ # django.core.validators.URLValidator (at least through v3.2) relies on ++ # this, for better or worse, to catch it in a ValidationError via its ++ # regular expressions. ++ # Here we test the basic round trip concept of such a trailing space. ++ self.assertEqual(urllib.parse.urlunsplit(p), "www.pypi.org ") ++ ++ # with scheme as cache-key ++ url = "//www.python.org/" ++ scheme = noise.decode("utf-8") + "https" + noise.decode("utf-8") ++ for _ in range(2): ++ p = urllib.parse.urlsplit(url, scheme=scheme) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.geturl(), "https://www.python.org/") ++ + def test_attributes_bad_port(self): + """Check handling of invalid ports.""" + for bytes in (False, True): +@@ -656,7 +715,7 @@ class UrlParseTestCase(unittest.TestCase): + for port in ("foo", "1.5", "-1", "0x10"): + with self.subTest(bytes=bytes, parse=parse, port=port): + netloc = "www.example.net:" + port +- url = "http://" + netloc ++ url = "http://" + netloc + "/" + if bytes: + netloc = netloc.encode("ascii") + url = url.encode("ascii") +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index b7965fe..5b7193f 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -25,6 +25,10 @@ currently not entirely compliant with this RFC due to defacto + scenarios for parsing, and for backward compatibility purposes, some + parsing quirks from older RFCs are retained. The testcases in + test_urlparse.py provides a good indicator of parsing behavior. ++ ++The WHATWG URL Parser spec should also be considered. We are not compliant with ++it either due to existing user code API behavior expectations (Hyrum's Law). ++It serves as a useful guide when making changes. + """ + + import re +@@ -78,6 +82,10 @@ scheme_chars = ('abcdefghijklmnopqrstuvwxyz' + '0123456789' + '+-.') + ++# Leading and trailing C0 control and space to be stripped per WHATWG spec. ++# == "".join([chr(i) for i in range(0, 0x20 + 1)]) ++_WHATWG_C0_CONTROL_OR_SPACE = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' ++ + # Unsafe bytes to be removed per WHATWG spec + _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n'] + +@@ -456,6 +464,10 @@ def urlsplit(url, scheme='', allow_fragments=True): + """ + + url, scheme, _coerce_result = _coerce_args(url, scheme) ++ # Only lstrip url as some applications rely on preserving trailing space. ++ # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both) ++ url = url.lstrip(_WHATWG_C0_CONTROL_OR_SPACE) ++ scheme = scheme.strip(_WHATWG_C0_CONTROL_OR_SPACE) + + for b in _UNSAFE_URL_BYTES_TO_REMOVE: + url = url.replace(b, "") +-- +2.27.0 + diff --git a/python39.spec b/python39.spec index b6d8d3c..ba7652a 100644 --- a/python39.spec +++ b/python39.spec @@ -1,4 +1,4 @@ -%define anolis_release .0.1 +%define anolis_release .0.2 # ================== # Top-level metadata # ================== @@ -431,6 +431,7 @@ Patch378: 00378-support-expat-2-4-5.patch # Add by Anolis Patch10000: 10000-python-anolis-rebrand.patch Patch10001: 10001-anolis-python-support-loongarch64.patch +Patch10002: 10002-start-stripping-C0-control-and-space-chars-in-urlspl.patch # End # ========================================== @@ -832,6 +833,7 @@ rm Lib/ensurepip/_bundled/*.whl %apply_patch -q %{PATCH378} %apply_patch -q %{PATCH10000} %apply_patch -q %{PATCH10001} +%apply_patch -q %{PATCH10002} # Remove all exe files to ensure we are not shipping prebuilt binaries # note that those are only used to create Microsoft Windows installers @@ -1998,6 +2000,9 @@ fi # ====================================================== %changelog +* Wed Jun 28 2023 Liwei Ge - 3.9.16-2.0.2 +- Fix CVE-2023-24329 + * Mon Jun 12 2023 zhangbinchen - 3.9.16-2.0.1 - Rebrand for Anolis OS - Support loongarch64 platform(Liwei Ge) -- Gitee