From 14334cb68316f32552b750a6afaf7cb92bfbf4fc Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Mon, 28 Nov 2022 09:36:45 +0000 Subject: [PATCH 1/3] python27: switch to ActiveState's fork for Python 2 ActiveState is a company that is maintaining a fork of Python 2 to fixes its security issues. Their support is paid, however the code is open-source. See the details here: https://www.activestate.com/products/python/python-2-end-of-life-security-updates/ This enable us to drop a bunch of CVE's patches for Python 2.7 and also it should be easier to maintain, since we can just bump the version once ActiveState tags a new version. --- .../python/cpython/2.7/CVE-2019-20907.patch | 24 -- .../python/cpython/2.7/CVE-2020-26116.patch | 93 ----- .../python/cpython/2.7/CVE-2020-27619.patch | 73 ---- .../python/cpython/2.7/CVE-2020-8492.patch | 213 ---------- .../python/cpython/2.7/CVE-2021-23336.patch | 390 ------------------ .../python/cpython/2.7/CVE-2021-3177.patch | 181 -------- .../python/cpython/2.7/default.nix | 16 +- .../interpreters/python/default.nix | 4 +- 8 files changed, 7 insertions(+), 987 deletions(-) delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2019-20907.patch delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2020-26116.patch delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2020-27619.patch delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2020-8492.patch delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2021-23336.patch delete mode 100644 pkgs/development/interpreters/python/cpython/2.7/CVE-2021-3177.patch diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2019-20907.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2019-20907.patch deleted file mode 100644 index cf67ae2b51be..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2019-20907.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 5a8d121a1f3ef5ad7c105ee378cc79a3eac0c7d4 Mon Sep 17 00:00:00 2001 -From: Rishi -Date: Wed, 15 Jul 2020 13:51:00 +0200 -Subject: [PATCH] bpo-39017: Avoid infinite loop in the tarfile module - (GH-21454) - -Avoid infinite loop when reading specially crafted TAR files using the tarfile module -(CVE-2019-20907). ---- - Lib/tarfile.py | 2 ++ - -diff --git a/Lib/tarfile.py b/Lib/tarfile.py -index e2b60532f6..6769066cab 100755 ---- a/Lib/tarfile.py -+++ b/Lib/tarfile.py -@@ -1249,6 +1249,8 @@ class TarInfo(object): - - length, keyword = match.groups() - length = int(length) -+ if length == 0: -+ raise InvalidHeaderError("invalid header") - value = buf[match.end(2) + 1:match.start(1) + length - 1] - - # Normally, we could just use "utf-8" as the encoding and "strict" diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-26116.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-26116.patch deleted file mode 100644 index 8f57119601ab..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-26116.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= -Date: Thu, 10 Sep 2020 13:39:48 +0200 -Subject: [PATCH 03/36] bpo-39603: Prevent header injection in http methods - (GH-18485) (GH-21539) - -reject control chars in http method in http.client.putrequest to prevent http header injection -(cherry picked from commit 8ca8a2e8fb068863c1138f07e3098478ef8be12e) - -Co-authored-by: AMIR <31338382+amiremohamadi@users.noreply.github.com> - -[rebased for py2.7] ---- - Lib/httplib.py | 17 +++++++++++++++++ - Lib/test/test_httplib.py | 20 ++++++++++++++++++++ - 2 files changed, 37 insertions(+) - -diff --git a/Lib/httplib.py b/Lib/httplib.py -index fcc4152aaf..81a08d5d71 100644 ---- a/Lib/httplib.py -+++ b/Lib/httplib.py -@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]') - # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") - # We are more lenient for assumed real world compatibility purposes. - -+# These characters are not allowed within HTTP method names -+# to prevent http header injection. -+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]') -+ - # We always set the Content-Length header for these methods because some - # servers will otherwise respond with a 411 - _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} -@@ -935,6 +939,8 @@ class HTTPConnection: - else: - raise CannotSendRequest() - -+ self._validate_method(method) -+ - # Save the method for use later in the response phase - self._method = method - -@@ -1020,6 +1026,17 @@ class HTTPConnection: - # On Python 2, request is already encoded (default) - return request - -+ def _validate_method(self, method): -+ """Validate a method name for putrequest.""" -+ # prevent http header injection -+ match = _contains_disallowed_method_pchar_re.search(method) -+ if match: -+ msg = ( -+ "method can't contain control characters. {method!r} " -+ "(found at least {matched!r})" -+ ).format(matched=match.group(), method=method) -+ raise ValueError(msg) -+ - def _validate_path(self, url): - """Validate a url for putrequest.""" - # Prevent CVE-2019-9740. -diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py -index d8a57f7353..e20a0986dc 100644 ---- a/Lib/test/test_httplib.py -+++ b/Lib/test/test_httplib.py -@@ -384,6 +384,26 @@ class HeaderTests(TestCase): - with self.assertRaisesRegexp(ValueError, 'Invalid header'): - conn.putheader(name, value) - -+ def test_invalid_method_names(self): -+ methods = ( -+ 'GET\r', -+ 'POST\n', -+ 'PUT\n\r', -+ 'POST\nValue', -+ 'POST\nHOST:abc', -+ 'GET\nrHost:abc\n', -+ 'POST\rRemainder:\r', -+ 'GET\rHOST:\n', -+ '\nPUT' -+ ) -+ -+ for method in methods: -+ with self.assertRaisesRegexp( -+ ValueError, "method can't contain control characters"): -+ conn = httplib.HTTPConnection('example.com') -+ conn.sock = FakeSocket(None) -+ conn.request(method=method, url="/") -+ - - class BasicTest(TestCase): - def test_status_lines(self): --- -2.38.1 - diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-27619.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-27619.patch deleted file mode 100644 index 9705b59c6b4e..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-27619.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 6a6c4240fa1e628dbcca09fdde39aea4d8eb6138 Mon Sep 17 00:00:00 2001 -From: "Miss Skeleton (bot)" <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 19 Oct 2020 21:46:10 -0700 -Subject: [PATCH 05/36] bpo-41944: No longer call eval() on content received - via HTTP in the CJK codec tests (GH-22566) (GH-22579) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -(cherry picked from commit 2ef5caa58febc8968e670e39e3d37cf8eef3cab8) - -Co-authored-by: Serhiy Storchaka - -Rebased for Python 2.7 by Michał Górny ---- - Lib/test/multibytecodec_support.py | 23 +++++++------------ - .../2020-10-05-17-43-46.bpo-41944.rf1dYb.rst | 1 + - 2 files changed, 9 insertions(+), 15 deletions(-) - create mode 100644 Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst - -diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py -index 5b2329b6d8..b7d7a3aba7 100644 ---- a/Lib/test/multibytecodec_support.py -+++ b/Lib/test/multibytecodec_support.py -@@ -279,30 +279,23 @@ class TestBase_Mapping(unittest.TestCase): - self._test_mapping_file_plain() - - def _test_mapping_file_plain(self): -- _unichr = lambda c: eval("u'\\U%08x'" % int(c, 16)) -- unichrs = lambda s: u''.join(_unichr(c) for c in s.split('+')) -+ def unichrs(s): -+ return ''.join(chr(int(x, 16)) for x in s.split('+')) -+ - urt_wa = {} - - with self.open_mapping_file() as f: - for line in f: - if not line: - break -- data = line.split('#')[0].strip().split() -+ data = line.split('#')[0].split() - if len(data) != 2: - continue - -- csetval = eval(data[0]) -- if csetval <= 0x7F: -- csetch = chr(csetval & 0xff) -- elif csetval >= 0x1000000: -- csetch = chr(csetval >> 24) + chr((csetval >> 16) & 0xff) + \ -- chr((csetval >> 8) & 0xff) + chr(csetval & 0xff) -- elif csetval >= 0x10000: -- csetch = chr(csetval >> 16) + \ -- chr((csetval >> 8) & 0xff) + chr(csetval & 0xff) -- elif csetval >= 0x100: -- csetch = chr(csetval >> 8) + chr(csetval & 0xff) -- else: -+ if data[0][:2] != '0x': -+ self.fail("Invalid line: {line!r}".format(line=line)) -+ csetch = bytes.fromhex(data[0][2:]) -+ if len(csetch) == 1 and 0x80 <= csetch[0]: - continue - - unich = unichrs(data[1]) -diff --git a/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst -new file mode 100644 -index 0000000000..4f9782f1c8 ---- /dev/null -+++ b/Misc/NEWS.d/next/Tests/2020-10-05-17-43-46.bpo-41944.rf1dYb.rst -@@ -0,0 +1 @@ -+Tests for CJK codecs no longer call ``eval()`` on content received via HTTP. --- -2.38.1 - diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-8492.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-8492.patch deleted file mode 100644 index de21686b4edf..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2020-8492.patch +++ /dev/null @@ -1,213 +0,0 @@ -From 2273e65e11dd0234f2f51ebaef61fc6e848d4059 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= -Date: Thu, 10 Sep 2020 13:35:39 +0200 -Subject: [PATCH 02/36] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler - (GH-18284) (GH-19304) - -The AbstractBasicAuthHandler class of the urllib.request module uses -an inefficient regular expression which can be exploited by an -attacker to cause a denial of service. Fix the regex to prevent the -catastrophic backtracking. Vulnerability reported by Ben Caller -and Matt Schwager. - -AbstractBasicAuthHandler of urllib.request now parses all -WWW-Authenticate HTTP headers and accepts multiple challenges per -header: use the realm of the first Basic challenge. - -Co-Authored-By: Serhiy Storchaka -(cherry picked from commit 0b297d4ff1c0e4480ad33acae793fbaf4bf015b4) - -[rebased for py2.7] ---- - Lib/test/test_urllib2.py | 81 ++++++++++++++++++++++++++-------------- - Lib/urllib2.py | 60 +++++++++++++++++++++++------ - 2 files changed, 101 insertions(+), 40 deletions(-) - -diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py -index 20a0f58143..0adbb13c43 100644 ---- a/Lib/test/test_urllib2.py -+++ b/Lib/test/test_urllib2.py -@@ -1128,42 +1128,67 @@ class HandlerTests(unittest.TestCase): - self.assertEqual(req.get_host(), "proxy.example.com:3128") - self.assertEqual(req.get_header("Proxy-authorization"),"FooBar") - -- def test_basic_auth(self, quote_char='"'): -+ def check_basic_auth(self, headers, realm): - opener = OpenerDirector() - password_manager = MockPasswordManager() - auth_handler = urllib2.HTTPBasicAuthHandler(password_manager) -- realm = "ACME Widget Store" -- http_handler = MockHTTPHandler( -- 401, 'WWW-Authenticate: Basic realm=%s%s%s\r\n\r\n' % -- (quote_char, realm, quote_char) ) -+ body = '\r\n'.join(headers) + '\r\n\r\n' -+ http_handler = MockHTTPHandler(401, body) - opener.add_handler(auth_handler) - opener.add_handler(http_handler) - self._test_basic_auth(opener, auth_handler, "Authorization", - realm, http_handler, password_manager, - "http://acme.example.com/protected", -- "http://acme.example.com/protected" -- ) -- -- def test_basic_auth_with_single_quoted_realm(self): -- self.test_basic_auth(quote_char="'") -- -- def test_basic_auth_with_unquoted_realm(self): -- opener = OpenerDirector() -- password_manager = MockPasswordManager() -- auth_handler = urllib2.HTTPBasicAuthHandler(password_manager) -- realm = "ACME Widget Store" -- http_handler = MockHTTPHandler( -- 401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm) -- opener.add_handler(auth_handler) -- opener.add_handler(http_handler) -- msg = "Basic Auth Realm was unquoted" -- with test_support.check_warnings((msg, UserWarning)): -- self._test_basic_auth(opener, auth_handler, "Authorization", -- realm, http_handler, password_manager, -- "http://acme.example.com/protected", -- "http://acme.example.com/protected" -- ) -- -+ "http://acme.example.com/protected") -+ -+ def test_basic_auth(self): -+ realm = "realm2@example.com" -+ realm2 = "realm2@example.com" -+ basic = 'Basic realm="{realm}"'.format(realm=realm) -+ basic2 = 'Basic realm="{realm2}"'.format(realm2=realm2) -+ other_no_realm = 'Otherscheme xxx' -+ digest = ('Digest realm="{realm2}", ' -+ 'qop="auth, auth-int", ' -+ 'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ' -+ 'opaque="5ccc069c403ebaf9f0171e9517f40e41"' -+ .format(realm2=realm2)) -+ for realm_str in ( -+ # test "quote" and 'quote' -+ 'Basic realm="{realm}"'.format(realm=realm), -+ "Basic realm='{realm}'".format(realm=realm), -+ -+ # charset is ignored -+ 'Basic realm="{realm}", charset="UTF-8"'.format(realm=realm), -+ -+ # Multiple challenges per header -+ ', '.join((basic, basic2)), -+ ', '.join((basic, other_no_realm)), -+ ', '.join((other_no_realm, basic)), -+ ', '.join((basic, digest)), -+ ', '.join((digest, basic)), -+ ): -+ headers = ['WWW-Authenticate: {realm_str}' -+ .format(realm_str=realm_str)] -+ self.check_basic_auth(headers, realm) -+ -+ # no quote: expect a warning -+ with test_support.check_warnings(("Basic Auth Realm was unquoted", -+ UserWarning)): -+ headers = ['WWW-Authenticate: Basic realm={realm}' -+ .format(realm=realm)] -+ self.check_basic_auth(headers, realm) -+ -+ # Multiple headers: one challenge per header. -+ # Use the first Basic realm. -+ for challenges in ( -+ [basic, basic2], -+ [basic, digest], -+ [digest, basic], -+ ): -+ headers = ['WWW-Authenticate: {challenge}' -+ .format(challenge=challenge) -+ for challenge in challenges] -+ self.check_basic_auth(headers, realm) - - def test_proxy_basic_auth(self): - opener = OpenerDirector() -diff --git a/Lib/urllib2.py b/Lib/urllib2.py -index 8b634ada37..b2d1fad6f2 100644 ---- a/Lib/urllib2.py -+++ b/Lib/urllib2.py -@@ -856,8 +856,15 @@ class AbstractBasicAuthHandler: - - # allow for double- and single-quoted realm values - # (single quotes are a violation of the RFC, but appear in the wild) -- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' -- 'realm=(["\']?)([^"\']*)\\2', re.I) -+ rx = re.compile('(?:^|,)' # start of the string or ',' -+ '[ \t]*' # optional whitespaces -+ '([^ \t]+)' # scheme like "Basic" -+ '[ \t]+' # mandatory whitespaces -+ # realm=xxx -+ # realm='xxx' -+ # realm="xxx" -+ 'realm=(["\']?)([^"\']*)\\2', -+ re.I) - - # XXX could pre-emptively send auth info already accepted (RFC 2617, - # end of section 2, and section 1.2 immediately after "credentials" -@@ -869,23 +876,52 @@ class AbstractBasicAuthHandler: - self.passwd = password_mgr - self.add_password = self.passwd.add_password - -+ def _parse_realm(self, header): -+ # parse WWW-Authenticate header: accept multiple challenges per header -+ found_challenge = False -+ for mo in AbstractBasicAuthHandler.rx.finditer(header): -+ scheme, quote, realm = mo.groups() -+ if quote not in ['"', "'"]: -+ warnings.warn("Basic Auth Realm was unquoted", -+ UserWarning, 3) -+ -+ yield (scheme, realm) -+ -+ found_challenge = True -+ -+ if not found_challenge: -+ if header: -+ scheme = header.split()[0] -+ else: -+ scheme = '' -+ yield (scheme, None) - - def http_error_auth_reqed(self, authreq, host, req, headers): - # host may be an authority (without userinfo) or a URL with an - # authority -- # XXX could be multiple headers -- authreq = headers.get(authreq, None) -+ headers = headers.getheaders(authreq) -+ if not headers: -+ # no header found -+ return - -- if authreq: -- mo = AbstractBasicAuthHandler.rx.search(authreq) -- if mo: -- scheme, quote, realm = mo.groups() -- if quote not in ['"', "'"]: -- warnings.warn("Basic Auth Realm was unquoted", -- UserWarning, 2) -- if scheme.lower() == 'basic': -+ unsupported = None -+ for header in headers: -+ for scheme, realm in self._parse_realm(header): -+ if scheme.lower() != 'basic': -+ unsupported = scheme -+ continue -+ -+ if realm is not None: -+ # Use the first matching Basic challenge. -+ # Ignore following challenges even if they use the Basic -+ # scheme. - return self.retry_http_basic_auth(host, req, realm) - -+ if unsupported is not None: -+ raise ValueError("AbstractBasicAuthHandler does not " -+ "support the following scheme: %r" -+ % (scheme,)) -+ - def retry_http_basic_auth(self, host, req, realm): - user, pw = self.passwd.find_user_password(realm, host) - if pw is not None: --- -2.38.1 - diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-23336.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-23336.patch deleted file mode 100644 index 760d0e7eed8e..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-23336.patch +++ /dev/null @@ -1,390 +0,0 @@ -From e7b005c05dbdbce967a409abd71641281a8604bf Mon Sep 17 00:00:00 2001 -From: Senthil Kumaran -Date: Mon, 15 Feb 2021 11:16:43 -0800 -Subject: [PATCH 24/26] [3.6] bpo-42967: only use '&' as a query string - separator (GH-24297) (GH-24532) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -bpo-42967: [security] Address a web cache-poisoning issue reported in -urllib.parse.parse_qsl(). - -urllib.parse will only us "&" as query string separator by default -instead of both ";" and "&" as allowed in earlier versions. An optional -argument seperator with default value "&" is added to specify the -separator. - -Co-authored-by: Éric Araujo -Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> -Co-authored-by: Adam Goldschmidt - -Rebased for Python 2.7 by Michał Górny ---- - Doc/library/cgi.rst | 7 +++- - Doc/library/urlparse.rst | 23 ++++++++++- - Lib/cgi.py | 20 +++++++--- - Lib/test/test_cgi.py | 29 +++++++++++--- - Lib/test/test_urlparse.py | 38 +++++++++---------- - Lib/urlparse.py | 22 ++++++++--- - .../2021-02-14-15-59-16.bpo-42967.YApqDS.rst | 1 + - 7 files changed, 100 insertions(+), 40 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst - -diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst -index ecd62c8c01..b85cdd8b61 100644 ---- a/Doc/library/cgi.rst -+++ b/Doc/library/cgi.rst -@@ -285,10 +285,10 @@ These are useful if you want more control, or if you want to employ some of the - algorithms implemented in this module in other circumstances. - - --.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing]]]) -+.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing]]], separator="&") - - Parse a query in the environment or from a file (the file defaults to -- ``sys.stdin`` and environment defaults to ``os.environ``). The *keep_blank_values* and *strict_parsing* parameters are -+ ``sys.stdin`` and environment defaults to ``os.environ``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are - passed to :func:`urlparse.parse_qs` unchanged. - - -@@ -316,6 +316,9 @@ algorithms implemented in this module in other circumstances. - Note that this does not parse nested multipart parts --- use - :class:`FieldStorage` for that. - -+ .. versionchanged:: 3.6.13 -+ Added the *separator* parameter. -+ - - .. function:: parse_header(string) - -diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst -index 0989c88c30..2f8e4c5a44 100644 ---- a/Doc/library/urlparse.rst -+++ b/Doc/library/urlparse.rst -@@ -136,7 +136,7 @@ The :mod:`urlparse` module defines the following functions: - now raise :exc:`ValueError`. - - --.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]]) -+.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]], separator='&') - - Parse a query string given as a string argument (data of type - :mimetype:`application/x-www-form-urlencoded`). Data are returned as a -@@ -157,6 +157,9 @@ The :mod:`urlparse` module defines the following functions: - read. If set, then throws a :exc:`ValueError` if there are more than - *max_num_fields* fields read. - -+ The optional argument *separator* is the symbol to use for separating the -+ query arguments. It defaults to ``&``. -+ - Use the :func:`urllib.urlencode` function to convert such dictionaries into - query strings. - -@@ -166,7 +169,14 @@ The :mod:`urlparse` module defines the following functions: - .. versionchanged:: 2.7.16 - Added *max_num_fields* parameter. - --.. function:: parse_qsl(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]]) -+ .. versionchanged:: 2.7.18-gentoo -+ Added *separator* parameter with the default value of ``&``. Earlier -+ Python versions allowed using both ``;`` and ``&`` as query parameter -+ separator. This has been changed to allow only a single separator key, -+ with ``&`` as the default separator. -+ -+ -+.. function:: parse_qsl(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]], separator='&') - - Parse a query string given as a string argument (data of type - :mimetype:`application/x-www-form-urlencoded`). Data are returned as a list of -@@ -186,6 +196,9 @@ The :mod:`urlparse` module defines the following functions: - read. If set, then throws a :exc:`ValueError` if there are more than - *max_num_fields* fields read. - -+ The optional argument *separator* is the symbol to use for separating the -+ query arguments. It defaults to ``&``. -+ - Use the :func:`urllib.urlencode` function to convert such lists of pairs into - query strings. - -@@ -195,6 +208,12 @@ The :mod:`urlparse` module defines the following functions: - .. versionchanged:: 2.7.16 - Added *max_num_fields* parameter. - -+ .. versionchanged:: 2.7.18-gentoo -+ Added *separator* parameter with the default value of ``&``. Earlier -+ Python versions allowed using both ``;`` and ``&`` as query parameter -+ separator. This has been changed to allow only a single separator key, -+ with ``&`` as the default separator. -+ - .. function:: urlunparse(parts) - - Construct a URL from a tuple as returned by ``urlparse()``. The *parts* argument -diff --git a/Lib/cgi.py b/Lib/cgi.py -index 5b903e0347..9d0848b6b1 100755 ---- a/Lib/cgi.py -+++ b/Lib/cgi.py -@@ -121,7 +121,8 @@ log = initlog # The current logging function - # 0 ==> unlimited input - maxlen = 0 - --def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): -+def parse(fp=None, environ=os.environ, keep_blank_values=0, -+ strict_parsing=0, separator='&'): - """Parse a query in the environment or from a file (default stdin) - - Arguments, all optional: -@@ -140,6 +141,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. -+ -+ separator: str. The symbol to use for separating the query arguments. -+ Defaults to &. - """ - if fp is None: - fp = sys.stdin -@@ -171,7 +175,8 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): - else: - qs = "" - environ['QUERY_STRING'] = qs # XXX Shouldn't, really -- return urlparse.parse_qs(qs, keep_blank_values, strict_parsing) -+ return urlparse.parse_qs(qs, keep_blank_values, strict_parsing, -+ separator=separator) - - - # parse query string function called from urlparse, -@@ -395,7 +400,7 @@ class FieldStorage: - - def __init__(self, fp=None, headers=None, outerboundary="", - environ=os.environ, keep_blank_values=0, strict_parsing=0, -- max_num_fields=None): -+ max_num_fields=None, separator='&'): - """Constructor. Read multipart/* until last part. - - Arguments, all optional: -@@ -430,6 +435,7 @@ class FieldStorage: - self.keep_blank_values = keep_blank_values - self.strict_parsing = strict_parsing - self.max_num_fields = max_num_fields -+ self.separator = separator - if 'REQUEST_METHOD' in environ: - method = environ['REQUEST_METHOD'].upper() - self.qs_on_post = None -@@ -613,7 +619,8 @@ class FieldStorage: - if self.qs_on_post: - qs += '&' + self.qs_on_post - query = urlparse.parse_qsl(qs, self.keep_blank_values, -- self.strict_parsing, self.max_num_fields) -+ self.strict_parsing, self.max_num_fields, -+ separator=self.separator) - self.list = [MiniFieldStorage(key, value) for key, value in query] - self.skip_lines() - -@@ -629,7 +636,8 @@ class FieldStorage: - query = urlparse.parse_qsl(self.qs_on_post, - self.keep_blank_values, - self.strict_parsing, -- self.max_num_fields) -+ self.max_num_fields, -+ separator=self.separator) - self.list.extend(MiniFieldStorage(key, value) - for key, value in query) - FieldStorageClass = None -@@ -649,7 +657,7 @@ class FieldStorage: - headers = rfc822.Message(self.fp) - part = klass(self.fp, headers, ib, - environ, keep_blank_values, strict_parsing, -- max_num_fields) -+ max_num_fields, separator=self.separator) - - if max_num_fields is not None: - max_num_fields -= 1 -diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py -index 743c2afbd4..f414faa23b 100644 ---- a/Lib/test/test_cgi.py -+++ b/Lib/test/test_cgi.py -@@ -61,12 +61,9 @@ parse_strict_test_cases = [ - ("", ValueError("bad query field: ''")), - ("&", ValueError("bad query field: ''")), - ("&&", ValueError("bad query field: ''")), -- (";", ValueError("bad query field: ''")), -- (";&;", ValueError("bad query field: ''")), - # Should the next few really be valid? - ("=", {}), - ("=&=", {}), -- ("=;=", {}), - # This rest seem to make sense - ("=a", {'': ['a']}), - ("&=a", ValueError("bad query field: ''")), -@@ -81,8 +78,6 @@ parse_strict_test_cases = [ - ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), - ("a=a+b&a=b+a", {'a': ['a b', 'b a']}), - ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), -- ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), -- ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), - ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env", - {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'], - 'cuyer': ['r'], -@@ -188,6 +183,30 @@ class CgiTests(unittest.TestCase): - self.assertEqual(expect[k], v) - self.assertItemsEqual(expect.values(), d.values()) - -+ def test_separator(self): -+ parse_semicolon = [ -+ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}), -+ ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), -+ (";", ValueError("bad query field: ''")), -+ (";;", ValueError("bad query field: ''")), -+ ("=;a", ValueError("bad query field: 'a'")), -+ (";b=a", ValueError("bad query field: ''")), -+ ("b;=a", ValueError("bad query field: 'b'")), -+ ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), -+ ("a=a+b;a=b+a", {'a': ['a b', 'b a']}), -+ ] -+ for orig, expect in parse_semicolon: -+ env = {'QUERY_STRING': orig} -+ fs = cgi.FieldStorage(separator=';', environ=env) -+ if isinstance(expect, dict): -+ for key in expect.keys(): -+ expect_val = expect[key] -+ self.assertIn(key, fs) -+ if len(expect_val) > 1: -+ self.assertEqual(fs.getvalue(key), expect_val) -+ else: -+ self.assertEqual(fs.getvalue(key), expect_val[0]) -+ - def test_log(self): - cgi.log("Testing") - -diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -index 86c4a0595c..0b2107339a 100644 ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -24,16 +24,20 @@ parse_qsl_test_cases = [ - ("&a=b", [('a', 'b')]), - ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]), - ("a=1&a=2", [('a', '1'), ('a', '2')]), -- (";", []), -- (";;", []), -- (";a=b", [('a', 'b')]), -- ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), -- ("a=1;a=2", [('a', '1'), ('a', '2')]), -- (b";", []), -- (b";;", []), -- (b";a=b", [(b'a', b'b')]), -- (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), -- (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), -+ (b"", []), -+ (b"&", []), -+ (b"&&", []), -+ (b"=", [(b'', b'')]), -+ (b"=a", [(b'', b'a')]), -+ (b"a", [(b'a', b'')]), -+ (b"a=", [(b'a', b'')]), -+ (b"&a=b", [(b'a', b'b')]), -+ (b"a=a+b&b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), -+ (b"a=1&a=2", [(b'a', b'1'), (b'a', b'2')]), -+ (";a=b", [(';a', 'b')]), -+ ("a=a+b;b=b+c", [('a', 'a b;b=b c')]), -+ (b";a=b", [(b';a', b'b')]), -+ (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]), - ] - - parse_qs_test_cases = [ -@@ -57,16 +61,10 @@ parse_qs_test_cases = [ - (b"&a=b", {b'a': [b'b']}), - (b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), - (b"a=1&a=2", {b'a': [b'1', b'2']}), -- (";", {}), -- (";;", {}), -- (";a=b", {'a': ['b']}), -- ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), -- ("a=1;a=2", {'a': ['1', '2']}), -- (b";", {}), -- (b";;", {}), -- (b";a=b", {b'a': [b'b']}), -- (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), -- (b"a=1;a=2", {b'a': [b'1', b'2']}), -+ (";a=b", {';a': ['b']}), -+ ("a=a+b;b=b+c", {'a': ['a b;b=b c']}), -+ (b";a=b", {b';a': [b'b']}), -+ (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}), - ] - - class UrlParseTestCase(unittest.TestCase): -diff --git a/Lib/urlparse.py b/Lib/urlparse.py -index 798b467b60..6c32727fce 100644 ---- a/Lib/urlparse.py -+++ b/Lib/urlparse.py -@@ -382,7 +382,8 @@ def unquote(s): - append(item) - return ''.join(res) - --def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None): -+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None, -+ separator='&'): - """Parse a query given as a string argument. - - Arguments: -@@ -402,17 +403,22 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None): - - max_num_fields: int. If set, then throws a ValueError if there - are more than n fields read by parse_qsl(). -+ -+ separator: str. The symbol to use for separating the query arguments. -+ Defaults to &. -+ - """ - dict = {} - for name, value in parse_qsl(qs, keep_blank_values, strict_parsing, -- max_num_fields): -+ max_num_fields, separator=separator): - if name in dict: - dict[name].append(value) - else: - dict[name] = [value] - return dict - --def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None): -+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None, -+ separator='&'): - """Parse a query given as a string argument. - - Arguments: -@@ -432,17 +438,23 @@ def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None): - max_num_fields: int. If set, then throws a ValueError if there - are more than n fields read by parse_qsl(). - -+ separator: str. The symbol to use for separating the query arguments. -+ Defaults to &. -+ - Returns a list, as G-d intended. - """ -+ if not separator or (not isinstance(separator, (str, bytes))): -+ raise ValueError("Separator must be of type string or bytes.") -+ - # If max_num_fields is defined then check that the number of fields - # is less than max_num_fields. This prevents a memory exhaustion DOS - # attack via post bodies with many fields. - if max_num_fields is not None: -- num_fields = 1 + qs.count('&') + qs.count(';') -+ num_fields = 1 + qs.count(separator) - if max_num_fields < num_fields: - raise ValueError('Max number of fields exceeded') - -- pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] -+ pairs = [s1 for s1 in qs.split(separator)] - r = [] - for name_value in pairs: - if not name_value and not strict_parsing: -diff --git a/Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst b/Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst -new file mode 100644 -index 0000000000..f08489b414 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst -@@ -0,0 +1 @@ -+Fix web cache poisoning vulnerability by defaulting the query args separator to ``&``, and allowing the user to choose a custom separator. --- -2.31.1 - diff --git a/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-3177.patch b/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-3177.patch deleted file mode 100644 index 6c0ae468461f..000000000000 --- a/pkgs/development/interpreters/python/cpython/2.7/CVE-2021-3177.patch +++ /dev/null @@ -1,181 +0,0 @@ -From fab838b2ee7cfb9037c24f0f18dfe01aa379b3f7 Mon Sep 17 00:00:00 2001 -From: Benjamin Peterson -Date: Mon, 18 Jan 2021 15:11:46 -0600 -Subject: [3.6] closes bpo-42938: Replace snprintf with Python unicode - formatting in ctypes param reprs. (GH-24250) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -(cherry picked from commit 916610ef90a0d0761f08747f7b0905541f0977c7) - -Co-authored-by: Benjamin Peterson -Rebased for Python 2.7 by Michał Górny ---- - Lib/ctypes/test/test_parameters.py | 43 +++++++++++++++++++ - .../2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst | 2 + - Modules/_ctypes/callproc.c | 49 +++++++++++----------- - 3 files changed, 69 insertions(+), 25 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst - -diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py -index 23c1b6e225..3456882ccb 100644 ---- a/Lib/ctypes/test/test_parameters.py -+++ b/Lib/ctypes/test/test_parameters.py -@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestCase): - with self.assertRaises(ZeroDivisionError): - WorseStruct().__setstate__({}, b'foo') - -+ def test_parameter_repr(self): -+ from ctypes import ( -+ c_bool, -+ c_char, -+ c_wchar, -+ c_byte, -+ c_ubyte, -+ c_short, -+ c_ushort, -+ c_int, -+ c_uint, -+ c_long, -+ c_ulong, -+ c_longlong, -+ c_ulonglong, -+ c_float, -+ c_double, -+ c_longdouble, -+ c_char_p, -+ c_wchar_p, -+ c_void_p, -+ ) -+ self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^$") -+ self.assertEqual(repr(c_char.from_param('a')), "") -+ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^$") -+ self.assertEqual(repr(c_byte.from_param(98)), "") -+ self.assertEqual(repr(c_ubyte.from_param(98)), "") -+ self.assertEqual(repr(c_short.from_param(511)), "") -+ self.assertEqual(repr(c_ushort.from_param(511)), "") -+ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^$") -+ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^$") -+ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^$") -+ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^$") -+ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^$") -+ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^$") -+ self.assertEqual(repr(c_float.from_param(1.5)), "") -+ self.assertEqual(repr(c_double.from_param(1.5)), "") -+ self.assertEqual(repr(c_double.from_param(1e300)), "") -+ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^$") -+ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^$") -+ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^$") -+ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^$") -+ - ################################################################ - - if __name__ == '__main__': -diff --git a/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst -new file mode 100644 -index 0000000000..7df65a156f ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst -@@ -0,0 +1,2 @@ -+Avoid static buffers when computing the repr of :class:`ctypes.c_double` and -+:class:`ctypes.c_longdouble` values. -diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c -index 066fefc0cc..421addf353 100644 ---- a/Modules/_ctypes/callproc.c -+++ b/Modules/_ctypes/callproc.c -@@ -460,50 +460,51 @@ PyCArg_dealloc(PyCArgObject *self) - static PyObject * - PyCArg_repr(PyCArgObject *self) - { -- char buffer[256]; - switch(self->tag) { - case 'b': - case 'B': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.b); -- break; - case 'h': - case 'H': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.h); -- break; - case 'i': - case 'I': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.i); -- break; - case 'l': - case 'L': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.l); -- break; - - #ifdef HAVE_LONG_LONG - case 'q': - case 'Q': -- sprintf(buffer, -+ return PyString_FromFormat( - "", - self->tag, self->value.q); -- break; - #endif - case 'd': -- sprintf(buffer, "", -- self->tag, self->value.d); -- break; -- case 'f': -- sprintf(buffer, "", -- self->tag, self->value.f); -- break; -- -+ case 'f': { -+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); -+ if (f == NULL) { -+ return NULL; -+ } -+ PyObject *r = PyObject_Repr(f); -+ if (r == NULL) { -+ Py_DECREF(f); -+ return NULL; -+ } -+ PyObject *result = PyString_FromFormat( -+ "", self->tag, PyString_AsString(r)); -+ Py_DECREF(r); -+ Py_DECREF(f); -+ return result; -+ } - case 'c': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.c); -- break; - - /* Hm, are these 'z' and 'Z' codes useful at all? - Shouldn't they be replaced by the functionality of c_string -@@ -512,16 +513,14 @@ PyCArg_repr(PyCArgObject *self) - case 'z': - case 'Z': - case 'P': -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self->value.p); - break; - - default: -- sprintf(buffer, "", -+ return PyString_FromFormat("", - self->tag, self); -- break; - } -- return PyString_FromString(buffer); - } - - static PyMemberDef PyCArgType_members[] = { --- -cgit v1.2.3 - diff --git a/pkgs/development/interpreters/python/cpython/2.7/default.nix b/pkgs/development/interpreters/python/cpython/2.7/default.nix index 85995327fa8e..6e231c928eca 100644 --- a/pkgs/development/interpreters/python/cpython/2.7/default.nix +++ b/pkgs/development/interpreters/python/cpython/2.7/default.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, fetchurl, fetchpatch +{ lib, stdenv, fetchFromGitHub, fetchpatch , bzip2 , expat , libffi @@ -79,8 +79,10 @@ let version = with sourceVersion; "${major}.${minor}.${patch}${suffix}"; - src = fetchurl { - url = with sourceVersion; "https://www.python.org/ftp/python/${major}.${minor}.${patch}/Python-${version}.tar.xz"; + src = fetchFromGitHub { + owner = "ActiveState"; + repo = "cpython"; + rev = "v${version}"; inherit sha256; }; @@ -119,14 +121,6 @@ let # Backport from CPython 3.8 of a good list of tests to run for PGO. ./profile-task.patch - # https://www.activestate.com/products/python/python-2-end-of-life-security-updates/ - ./CVE-2019-20907.patch - ./CVE-2020-8492.patch - ./CVE-2020-26116.patch - ./CVE-2020-27619.patch - ./CVE-2021-3177.patch - ./CVE-2021-23336.patch - # The workaround is for unittests on Win64, which we don't support. # It does break aarch64-darwin, which we do support. See: # * https://bugs.python.org/issue35523 diff --git a/pkgs/development/interpreters/python/default.nix b/pkgs/development/interpreters/python/default.nix index 12f9c50535d0..903dae38ac12 100644 --- a/pkgs/development/interpreters/python/default.nix +++ b/pkgs/development/interpreters/python/default.nix @@ -145,9 +145,9 @@ in { major = "2"; minor = "7"; patch = "18"; - suffix = ""; + suffix = ".5"; # ActiveState's Python 2 extended support }; - sha256 = "0hzgxl94hnflis0d6m4szjx0b52gah7wpmcg5g00q7am6xwhwb5n"; + sha256 = "sha256-f5A0go0mUEv8cXuXo0ZRNfGwNPjnDhP7KqhkETOoqsw="; inherit (darwin) configd; inherit passthruFun; }; From b3d02fb8b5c6b822faaeb22afbb9fefabf38982f Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Mon, 28 Nov 2022 09:40:52 +0000 Subject: [PATCH 2/3] python27: add thiagokokada as maintainer --- pkgs/development/interpreters/python/cpython/2.7/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/development/interpreters/python/cpython/2.7/default.nix b/pkgs/development/interpreters/python/cpython/2.7/default.nix index 6e231c928eca..ebbae7381b01 100644 --- a/pkgs/development/interpreters/python/cpython/2.7/default.nix +++ b/pkgs/development/interpreters/python/cpython/2.7/default.nix @@ -329,7 +329,7 @@ in with passthru; stdenv.mkDerivation ({ ''; license = lib.licenses.psfl; platforms = lib.platforms.all; - maintainers = with lib.maintainers; [ fridh ]; + maintainers = with lib.maintainers; [ fridh thiagokokada ]; # Higher priority than Python 3.x so that `/bin/python` points to `/bin/python2` # in case both 2 and 3 are installed. priority = -100; From d345fb25003313c0af43f867c9b5660fdfcae59a Mon Sep 17 00:00:00 2001 From: Thiago Kenji Okada Date: Mon, 28 Nov 2022 11:42:27 +0000 Subject: [PATCH 3/3] python27: fix CVE-2021-3733 --- .../interpreters/python/cpython/2.7/default.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkgs/development/interpreters/python/cpython/2.7/default.nix b/pkgs/development/interpreters/python/cpython/2.7/default.nix index ebbae7381b01..684088186a0a 100644 --- a/pkgs/development/interpreters/python/cpython/2.7/default.nix +++ b/pkgs/development/interpreters/python/cpython/2.7/default.nix @@ -79,6 +79,8 @@ let version = with sourceVersion; "${major}.${minor}.${patch}${suffix}"; + # ActiveState is a fork of cpython that includes fixes for security + # issues after its EOL src = fetchFromGitHub { owner = "ActiveState"; repo = "cpython"; @@ -121,6 +123,13 @@ let # Backport from CPython 3.8 of a good list of tests to run for PGO. ./profile-task.patch + # remove once 2.7.18.6 is released + (fetchpatch { + name = "CVE-2021-3733.patch"; + url = "https://github.com/ActiveState/cpython/commit/eeb7fe50450f08a782921f3229abed2f23e7b2d7.patch"; + sha256 = "sha256-ch4cMoFythDmyvlVxOAVw3Ow4PPWVDq5o9c1qox2824="; + }) + # The workaround is for unittests on Win64, which we don't support. # It does break aarch64-darwin, which we do support. See: # * https://bugs.python.org/issue35523