Wed Oct 13 21:04:01 2021 UTC ()
Pullup ticket #6513 - requested by gutteridge
lang/python27: security fix
Revisions pulled up:
- lang/python27/Makefile 1.94
- lang/python27/distinfo 1.85
- lang/python27/patches/patch-Doc_library_cgi.rst 1.1
- lang/python27/patches/patch-Doc_library_urlparse.rst 1.1
- lang/python27/patches/patch-Lib_cgi.py 1.1
- lang/python27/patches/patch-Lib_ctypes_test_test__parameters.py 1.1
- lang/python27/patches/patch-Lib_httplib.py 1.4
- lang/python27/patches/patch-Lib_test_multibytecodec__support.py 1.1
- lang/python27/patches/patch-Lib_test_test__cgi.py 1.1
- lang/python27/patches/patch-Lib_test_test__httplib.py 1.4
- lang/python27/patches/patch-Lib_test_test__urlparse.py 1.1
- lang/python27/patches/patch-Lib_urllib2.py 1.3
- lang/python27/patches/patch-Lib_urlparse.py 1.1
- lang/python27/patches/patch-Modules___ctypes_callproc.c 1.2
---
Module Name: pkgsrc
Committed By: gutteridge
Date: Sun Oct 10 03:00:59 UTC 2021
Modified Files:
pkgsrc/lang/python27: Makefile distinfo
pkgsrc/lang/python27/patches: patch-Lib_httplib.py
patch-Lib_test_test__httplib.py patch-Lib_urllib2.py
patch-Modules___ctypes_callproc.c
Added Files:
pkgsrc/lang/python27/patches: patch-Doc_library_cgi.rst
patch-Doc_library_urlparse.rst patch-Lib_cgi.py
patch-Lib_ctypes_test_test__parameters.py
patch-Lib_test_multibytecodec__support.py
patch-Lib_test_test__cgi.py patch-Lib_test_test__urlparse.py
patch-Lib_urlparse.py
Log Message:
python27: fix various security issues
Addresses CVE-2020-27619, CVE-2021-3177, CVE-2021-3733, CVE-2021-3737
and CVE-2021-23336. Patches mostly sourced via Fedora.
(tm)
diff -r1.93 -r1.93.8.1 pkgsrc/lang/python27/Makefile
diff -r1.82 -r1.82.2.1 pkgsrc/lang/python27/distinfo
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Doc_library_cgi.rst
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Doc_library_urlparse.rst
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_cgi.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_ctypes_test_test__parameters.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_multibytecodec__support.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_test__cgi.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_test__urlparse.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_urlparse.py
diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_httplib.py
diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py
diff -r1.2 -r1.2.10.1 pkgsrc/lang/python27/patches/patch-Lib_urllib2.py
diff -r1.1 -r1.1.2.1 pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c
--- pkgsrc/lang/python27/Makefile 2020/12/07 13:14:38 1.93
+++ pkgsrc/lang/python27/Makefile 2021/10/13 21:04:00 1.93.8.1
| @@ -1,19 +1,19 @@ | | | @@ -1,19 +1,19 @@ |
1 | # $NetBSD: Makefile,v 1.93 2020/12/07 13:14:38 nia Exp $ | | 1 | # $NetBSD: Makefile,v 1.93.8.1 2021/10/13 21:04:00 tm Exp $ |
2 | | | 2 | |
3 | .include "dist.mk" | | 3 | .include "dist.mk" |
4 | | | 4 | |
5 | PKGNAME= python27-${PY_DISTVERSION} | | 5 | PKGNAME= python27-${PY_DISTVERSION} |
6 | PKGREVISION= 3 | | 6 | PKGREVISION= 4 |
7 | CATEGORIES= lang python | | 7 | CATEGORIES= lang python |
8 | | | 8 | |
9 | MAINTAINER= pkgsrc-users@NetBSD.org | | 9 | MAINTAINER= pkgsrc-users@NetBSD.org |
10 | HOMEPAGE= https://www.python.org/ | | 10 | HOMEPAGE= https://www.python.org/ |
11 | COMMENT= Interpreted, interactive, object-oriented programming language | | 11 | COMMENT= Interpreted, interactive, object-oriented programming language |
12 | LICENSE= python-software-foundation | | 12 | LICENSE= python-software-foundation |
13 | | | 13 | |
14 | DEPENDS= mozilla-rootcerts>=1.0.20150804nb1:../../security/mozilla-rootcerts | | 14 | DEPENDS= mozilla-rootcerts>=1.0.20150804nb1:../../security/mozilla-rootcerts |
15 | | | 15 | |
16 | CONFLICTS+= python-[0-9]* | | 16 | CONFLICTS+= python-[0-9]* |
17 | | | 17 | |
18 | USE_LANGUAGES= c c++ | | 18 | USE_LANGUAGES= c c++ |
19 | GNU_CONFIGURE= yes | | 19 | GNU_CONFIGURE= yes |
--- pkgsrc/lang/python27/distinfo 2021/06/23 18:30:24 1.82
+++ pkgsrc/lang/python27/distinfo 2021/10/13 21:04:00 1.82.2.1
| @@ -1,47 +1,55 @@ | | | @@ -1,47 +1,55 @@ |
1 | $NetBSD: distinfo,v 1.82 2021/06/23 18:30:24 schmonz Exp $ | | 1 | $NetBSD: distinfo,v 1.82.2.1 2021/10/13 21:04:00 tm Exp $ |
2 | | | 2 | |
3 | SHA1 (Python-2.7.18.tar.xz) = 678d4cf483a1c92efd347ee8e1e79326dc82810b | | 3 | SHA1 (Python-2.7.18.tar.xz) = 678d4cf483a1c92efd347ee8e1e79326dc82810b |
4 | RMD160 (Python-2.7.18.tar.xz) = 40a514bb05c9e631454ea8466e28f5bb229428ad | | 4 | RMD160 (Python-2.7.18.tar.xz) = 40a514bb05c9e631454ea8466e28f5bb229428ad |
5 | SHA512 (Python-2.7.18.tar.xz) = a7bb62b51f48ff0b6df0b18f5b0312a523e3110f49c3237936bfe56ed0e26838c0274ff5401bda6fc21bf24337477ccac49e8026c5d651e4b4cafb5eb5086f6c | | 5 | SHA512 (Python-2.7.18.tar.xz) = a7bb62b51f48ff0b6df0b18f5b0312a523e3110f49c3237936bfe56ed0e26838c0274ff5401bda6fc21bf24337477ccac49e8026c5d651e4b4cafb5eb5086f6c |
6 | Size (Python-2.7.18.tar.xz) = 12854736 bytes | | 6 | Size (Python-2.7.18.tar.xz) = 12854736 bytes |
| | | 7 | SHA1 (patch-Doc_library_cgi.rst) = ed9ac101b0857dc573e9a648694d1ee5fabe61fb |
| | | 8 | SHA1 (patch-Doc_library_urlparse.rst) = f9714b945a2bacb4ec5360c151a42192e00f08ad |
7 | SHA1 (patch-Include_pyerrors.h) = 0d2cd52d18cc719b895fa32ed7e11c6cb15bae54 | | 9 | SHA1 (patch-Include_pyerrors.h) = 0d2cd52d18cc719b895fa32ed7e11c6cb15bae54 |
8 | SHA1 (patch-Include_pyport.h) = f3e4ddbc954425a65301465410911222ca471320 | | 10 | SHA1 (patch-Include_pyport.h) = f3e4ddbc954425a65301465410911222ca471320 |
9 | SHA1 (patch-Lib___osx__support.py) = 4389472565616b3875c699f6e3e74850d5fde712 | | 11 | SHA1 (patch-Lib___osx__support.py) = 4389472565616b3875c699f6e3e74850d5fde712 |
| | | 12 | SHA1 (patch-Lib_cgi.py) = 9653904acfd2dbe03655a7cfa5688c450556671b |
10 | SHA1 (patch-Lib_ctypes_____init____.py) = 31dd0546bbe29ad1b1d481edc525ba43479c06da | | 13 | SHA1 (patch-Lib_ctypes_____init____.py) = 31dd0546bbe29ad1b1d481edc525ba43479c06da |
11 | SHA1 (patch-Lib_ctypes_macholib_dyld.py) = 9b7e972d4c71311742ca8b3501382182a4c9e2fe | | 14 | SHA1 (patch-Lib_ctypes_macholib_dyld.py) = 9b7e972d4c71311742ca8b3501382182a4c9e2fe |
12 | SHA1 (patch-Lib_ctypes_test_test__macholib.py) = 4479d315cd037f4c9138e8f5baa8eb1685932baa | | 15 | SHA1 (patch-Lib_ctypes_test_test__macholib.py) = 4479d315cd037f4c9138e8f5baa8eb1685932baa |
| | | 16 | SHA1 (patch-Lib_ctypes_test_test__parameters.py) = 8f8bb50515bc7e89ab59363b10af4d5391957eb7 |
13 | SHA1 (patch-Lib_ctypes_util.py) = 6fa516c7b43f08992427a0afcbe80c17bcc070f1 | | 17 | SHA1 (patch-Lib_ctypes_util.py) = 6fa516c7b43f08992427a0afcbe80c17bcc070f1 |
14 | SHA1 (patch-Lib_distutils_command_build__ext.py) = ea4feba4e93dbcff07050c82a00d591bb650e934 | | 18 | SHA1 (patch-Lib_distutils_command_build__ext.py) = ea4feba4e93dbcff07050c82a00d591bb650e934 |
15 | SHA1 (patch-Lib_distutils_command_install.py) = e6aef090b444b455fe351308d251e670329b7dc3 | | 19 | SHA1 (patch-Lib_distutils_command_install.py) = e6aef090b444b455fe351308d251e670329b7dc3 |
16 | SHA1 (patch-Lib_distutils_command_install__egg__info.py) = ec7f9e0cd04489b1f6497c44d75bff6864ad1047 | | 20 | SHA1 (patch-Lib_distutils_command_install__egg__info.py) = ec7f9e0cd04489b1f6497c44d75bff6864ad1047 |
17 | SHA1 (patch-Lib_distutils_tests_test__build__ext.py) = 6b3c8c8d1d351836b239c049d34d132953bd4786 | | 21 | SHA1 (patch-Lib_distutils_tests_test__build__ext.py) = 6b3c8c8d1d351836b239c049d34d132953bd4786 |
18 | SHA1 (patch-Lib_distutils_unixccompiler.py) = db16c9aca2f29730945f28247b88b18828739bbb | | 22 | SHA1 (patch-Lib_distutils_unixccompiler.py) = db16c9aca2f29730945f28247b88b18828739bbb |
19 | SHA1 (patch-Lib_distutils_util.py) = 5bcfad96f8e490351160f1a7c1f4ece7706a33fa | | 23 | SHA1 (patch-Lib_distutils_util.py) = 5bcfad96f8e490351160f1a7c1f4ece7706a33fa |
20 | SHA1 (patch-Lib_httplib.py) = 375d80eb79209f53046c62db128d8d3f64d9e765 | | 24 | SHA1 (patch-Lib_httplib.py) = b8eeaa203e2a86ece94148d192b2a7e0c078602a |
21 | SHA1 (patch-Lib_lib2to3_pgen2_driver.py) = 5d6dab14197f27363394ff1aeee22a8ced8026d2 | | 25 | SHA1 (patch-Lib_lib2to3_pgen2_driver.py) = 5d6dab14197f27363394ff1aeee22a8ced8026d2 |
22 | SHA1 (patch-Lib_multiprocessing_process.py) = 15699bd8ec822bf54a0631102e00e0a34f882803 | | 26 | SHA1 (patch-Lib_multiprocessing_process.py) = 15699bd8ec822bf54a0631102e00e0a34f882803 |
23 | SHA1 (patch-Lib_plistlib.py) = 96ae702995d434e2d7ec0ac62e37427a90b61d13 | | 27 | SHA1 (patch-Lib_plistlib.py) = 96ae702995d434e2d7ec0ac62e37427a90b61d13 |
24 | SHA1 (patch-Lib_sysconfig.py) = 8a7a0e5cbfec279a05945dffafea1b1131a76f0e | | 28 | SHA1 (patch-Lib_sysconfig.py) = 8a7a0e5cbfec279a05945dffafea1b1131a76f0e |
25 | SHA1 (patch-Lib_tarfile.py) = df00aa1941367c42dcbbed4b6658b724a22ddcde | | 29 | SHA1 (patch-Lib_tarfile.py) = df00aa1941367c42dcbbed4b6658b724a22ddcde |
26 | SHA1 (patch-Lib_test_test__httplib.py) = 9d37263e36110838e0b5f413ff4747deb3966dfe | | 30 | SHA1 (patch-Lib_test_multibytecodec__support.py) = a18c40e8009f1a8f63e15196d3e751d7dccf8367 |
| | | 31 | SHA1 (patch-Lib_test_test__cgi.py) = 724355e8d2195f8a4b76d7ea61133e9b14fa3a68 |
| | | 32 | SHA1 (patch-Lib_test_test__httplib.py) = f7cfa5501a63eaca539bfa53d38cf931f3a6c3ac |
27 | SHA1 (patch-Lib_test_test__platform.py) = 3a3b8c05f9bf9adf4862b1022ce864127d36b8b0 | | 33 | SHA1 (patch-Lib_test_test__platform.py) = 3a3b8c05f9bf9adf4862b1022ce864127d36b8b0 |
28 | SHA1 (patch-Lib_test_test__unicode.py) = 1bd182bdbd880d0a847f9d8b69277a607f9f0526 | | 34 | SHA1 (patch-Lib_test_test__unicode.py) = 1bd182bdbd880d0a847f9d8b69277a607f9f0526 |
29 | SHA1 (patch-Lib_test_test__urllib2.py) = 89baa57daf2f3282e4fc5009915dbc4910b96ef1 | | 35 | SHA1 (patch-Lib_test_test__urllib2.py) = 89baa57daf2f3282e4fc5009915dbc4910b96ef1 |
30 | SHA1 (patch-Lib_urllib2.py) = 33a85593da702447fa3ea74b4e3d36d0016f70b5 | | 36 | SHA1 (patch-Lib_test_test__urlparse.py) = 257cb3bf7a0e9b5e0dcb204f675959b10953ba7b |
| | | 37 | SHA1 (patch-Lib_urllib2.py) = 0cc0dc811bb9544496962e08b040b5c96fb9073c |
| | | 38 | SHA1 (patch-Lib_urlparse.py) = ec45dd48966eb806a5c0e79af6a7369fb45b9859 |
31 | SHA1 (patch-Mac_Tools_pythonw.c) = 2b9a60d4b349c240471fd305be69c28e0f654cdc | | 39 | SHA1 (patch-Mac_Tools_pythonw.c) = 2b9a60d4b349c240471fd305be69c28e0f654cdc |
32 | SHA1 (patch-Makefile.pre.in) = ceaf34237588b527478ce1f9163c9168382fa201 | | 40 | SHA1 (patch-Makefile.pre.in) = ceaf34237588b527478ce1f9163c9168382fa201 |
33 | SHA1 (patch-Modules___ctypes_callbacks.c) = 8c335edfc9d2ef47988c5bdf1c3dd8473757637b | | 41 | SHA1 (patch-Modules___ctypes_callbacks.c) = 8c335edfc9d2ef47988c5bdf1c3dd8473757637b |
34 | SHA1 (patch-Modules___ctypes_callproc.c) = adac5eb047eb58c14003ea9237d5d34e8b327b2f | | 42 | SHA1 (patch-Modules___ctypes_callproc.c) = 7b669f9c081bbc2b7fce2c827703f52b7389d592 |
35 | SHA1 (patch-Modules___ctypes_ctypes.h) = 07e9d5ecf8309a3ca4bf8382411d56dda08d7b27 | | 43 | SHA1 (patch-Modules___ctypes_ctypes.h) = 07e9d5ecf8309a3ca4bf8382411d56dda08d7b27 |
36 | SHA1 (patch-Modules___ctypes_malloc__closure.c) = 25d470cc66d218446227c7c1bd7ade409c53b8d0 | | 44 | SHA1 (patch-Modules___ctypes_malloc__closure.c) = 25d470cc66d218446227c7c1bd7ade409c53b8d0 |
37 | SHA1 (patch-Modules___multiprocessing_multiprocessing.h) = 7ca8fe22ba4bdcde6d39dd50fe2e86c25994c146 | | 45 | SHA1 (patch-Modules___multiprocessing_multiprocessing.h) = 7ca8fe22ba4bdcde6d39dd50fe2e86c25994c146 |
38 | SHA1 (patch-Modules___multiprocessing_semaphore.c) = 03b9c33ef38da383d5f7c2c84c17fe38cdd2911e | | 46 | SHA1 (patch-Modules___multiprocessing_semaphore.c) = 03b9c33ef38da383d5f7c2c84c17fe38cdd2911e |
39 | SHA1 (patch-Modules__ssl.c) = 6e68f88ad205106691900f091a897ffe0a4c363c | | 47 | SHA1 (patch-Modules__ssl.c) = 6e68f88ad205106691900f091a897ffe0a4c363c |
40 | SHA1 (patch-Modules_getaddrinfo.c) = aa699d257f1bc98b9a3183a21324053e134409d1 | | 48 | SHA1 (patch-Modules_getaddrinfo.c) = aa699d257f1bc98b9a3183a21324053e134409d1 |
41 | SHA1 (patch-Modules_getpath.c) = 4e6445be9da49626800c03eaaab28fb3826be9f9 | | 49 | SHA1 (patch-Modules_getpath.c) = 4e6445be9da49626800c03eaaab28fb3826be9f9 |
42 | SHA1 (patch-Modules_makesetup) = 9aad78714c4fe1a21cf66a6627d97d164ecea196 | | 50 | SHA1 (patch-Modules_makesetup) = 9aad78714c4fe1a21cf66a6627d97d164ecea196 |
43 | SHA1 (patch-Modules_nismodule.c) = 129ef7b32779944c2f1827c6b078a3aafab60729 | | 51 | SHA1 (patch-Modules_nismodule.c) = 129ef7b32779944c2f1827c6b078a3aafab60729 |
44 | SHA1 (patch-Modules_posixmodule.c) = 5105d380cd49bf49b8adbd9aa5ffb245195728ed | | 52 | SHA1 (patch-Modules_posixmodule.c) = 5105d380cd49bf49b8adbd9aa5ffb245195728ed |
45 | SHA1 (patch-Modules_selectmodule.c) = 01e113b0bd251978b555caaaa60b79c372edebce | | 53 | SHA1 (patch-Modules_selectmodule.c) = 01e113b0bd251978b555caaaa60b79c372edebce |
46 | SHA1 (patch-Modules_socketmodule.c) = 16848d90947b3de1f921a0813fa5c317f76961d4 | | 54 | SHA1 (patch-Modules_socketmodule.c) = 16848d90947b3de1f921a0813fa5c317f76961d4 |
47 | SHA1 (patch-Modules_sunaudiodev.c) = d836d77854a2b3d79fa34a06a8e2493bf0a503e6 | | 55 | SHA1 (patch-Modules_sunaudiodev.c) = d836d77854a2b3d79fa34a06a8e2493bf0a503e6 |
$NetBSD: patch-Doc_library_cgi.rst,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Doc/library/cgi.rst.orig 2020-04-19 21:13:39.000000000 +0000
+++ Doc/library/cgi.rst
@@ -285,10 +285,10 @@ These are useful if you want more contro
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,7 +316,6 @@ algorithms implemented in this module in
Note that this does not parse nested multipart parts --- use
:class:`FieldStorage` for that.
-
.. function:: parse_header(string)
Parse a MIME header (such as :mailheader:`Content-Type`) into a main value and a
$NetBSD: patch-Doc_library_urlparse.rst,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Doc/library/urlparse.rst.orig 2020-04-19 21:13:39.000000000 +0000
+++ Doc/library/urlparse.rst
@@ -136,7 +136,7 @@ The :mod:`urlparse` module defines the f
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,15 @@ The :mod:`urlparse` module defines the f
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 is recommended to set it to ``'&'`` or ``';'``.
+ It defaults to ``'&'``; a warning is raised if this default is used.
+ This default may be changed with the following environment variable settings:
+
+ - ``PYTHON_URLLIB_QS_SEPARATOR='&'``: use only ``&`` as separator, without warning (as in Python 3.6.13+ or 3.10)
+ - ``PYTHON_URLLIB_QS_SEPARATOR=';'``: use only ``;`` as separator
+ - ``PYTHON_URLLIB_QS_SEPARATOR=legacy``: use both ``&`` and ``;`` (as in previous versions of Python)
+
Use the :func:`urllib.urlencode` function to convert such dictionaries into
query strings.
@@ -186,6 +195,9 @@ The :mod:`urlparse` module defines the f
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 works as in :py:func:`parse_qs`.
+
Use the :func:`urllib.urlencode` function to convert such lists of pairs into
query strings.
@@ -195,6 +207,7 @@ The :mod:`urlparse` module defines the f
.. versionchanged:: 2.7.16
Added *max_num_fields* parameter.
+
.. function:: urlunparse(parts)
Construct a URL from a tuple as returned by ``urlparse()``. The *parts* argument
$NetBSD: patch-Lib_cgi.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Lib/cgi.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ Lib/cgi.py
@@ -121,7 +121,8 @@ log = initlog # The current lo
# 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=None):
"""Parse a query in the environment or from a file (default stdin)
Arguments, all optional:
@@ -140,6 +141,8 @@ def parse(fp=None, environ=os.environ, k
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.
"""
if fp is None:
fp = sys.stdin
@@ -171,25 +174,26 @@ def parse(fp=None, environ=os.environ, k
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,
# this is done in order to maintain backward compatibility.
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, separator=None):
"""Parse a query given as a string argument."""
warn("cgi.parse_qs is deprecated, use urlparse.parse_qs instead",
PendingDeprecationWarning, 2)
- return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
+ return urlparse.parse_qs(qs, keep_blank_values, strict_parsing,
+ separator=separator)
-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=None):
"""Parse a query given as a string argument."""
warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead",
PendingDeprecationWarning, 2)
return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing,
- max_num_fields)
+ max_num_fields, separator=separator)
def parse_multipart(fp, pdict):
"""Parse multipart input.
@@ -288,7 +292,6 @@ def parse_multipart(fp, pdict):
return partdict
-
def _parseparam(s):
while s[:1] == ';':
s = s[1:]
@@ -395,7 +398,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=None):
"""Constructor. Read multipart/* until last part.
Arguments, all optional:
@@ -430,6 +433,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 +617,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,
+ self.separator)
self.list = [MiniFieldStorage(key, value) for key, value in query]
self.skip_lines()
@@ -629,7 +634,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,
+ self.separator)
self.list.extend(MiniFieldStorage(key, value)
for key, value in query)
FieldStorageClass = None
@@ -649,7 +655,8 @@ 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
@@ -817,10 +824,11 @@ class FormContentDict(UserDict.UserDict)
form.dict == {key: [val, val, ...], ...}
"""
- def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0):
+ def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0, separator=None):
self.dict = self.data = parse(environ=environ,
keep_blank_values=keep_blank_values,
- strict_parsing=strict_parsing)
+ strict_parsing=strict_parsing,
+ separator=separator)
self.query_string = environ['QUERY_STRING']
$NetBSD: patch-Lib_ctypes_test_test__parameters.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00357-CVE-2021-3177.patch
--- Lib/ctypes/test/test_parameters.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ Lib/ctypes/test/test_parameters.py
@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestC
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"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' ('a')>")
+ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
+ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
+ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
+ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
+ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
+ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
+ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
+ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
+ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
+ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
+ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
+ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
+
################################################################
if __name__ == '__main__':
$NetBSD: patch-Lib_test_multibytecodec__support.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2020-27619: No longer call eval() on content received via HTTP in the CJK codec tests
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00355-CVE-2020-27619.patch
--- Lib/test/multibytecodec_support.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ Lib/test/multibytecodec_support.py
@@ -279,30 +279,22 @@ 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(unichr(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: {!r}".format(line))
+ csetch = bytes.fromhex(data[0][2:])
+ if len(csetch) == 1 and 0x80 <= csetch[0]:
continue
unich = unichrs(data[1])
$NetBSD: patch-Lib_test_test__cgi.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Lib/test/test_cgi.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ 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'],
@@ -143,6 +138,60 @@ class CgiTests(unittest.TestCase):
if isinstance(expect, dict):
# test dict interface
self.assertEqual(len(expect), len(fcd))
+ self.assertItemsEqual(expect.keys(), fcd.keys())
+ self.assertItemsEqual(expect.values(), fcd.values())
+ self.assertItemsEqual(expect.items(), fcd.items())
+ self.assertEqual(fcd.get("nonexistent field", "default"), "default")
+ self.assertEqual(len(sd), len(fs))
+ self.assertItemsEqual(sd.keys(), fs.keys())
+ self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
+ # test individual fields
+ for key in expect.keys():
+ expect_val = expect[key]
+ self.assertTrue(fcd.has_key(key))
+ self.assertItemsEqual(fcd[key], expect[key])
+ self.assertEqual(fcd.get(key, "default"), fcd[key])
+ self.assertTrue(fs.has_key(key))
+ if len(expect_val) > 1:
+ single_value = 0
+ else:
+ single_value = 1
+ try:
+ val = sd[key]
+ except IndexError:
+ self.assertFalse(single_value)
+ self.assertEqual(fs.getvalue(key), expect_val)
+ else:
+ self.assertTrue(single_value)
+ self.assertEqual(val, expect_val[0])
+ self.assertEqual(fs.getvalue(key), expect_val[0])
+ self.assertItemsEqual(sd.getlist(key), expect_val)
+ if single_value:
+ self.assertItemsEqual(sd.values(),
+ first_elts(expect.values()))
+ self.assertItemsEqual(sd.items(),
+ first_second_elts(expect.items()))
+
+ 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}
+ fcd = cgi.FormContentDict(env, separator=';')
+ sd = cgi.SvFormContentDict(env, separator=';')
+ fs = cgi.FieldStorage(environ=env, separator=';')
+ if isinstance(expect, dict):
+ # test dict interface
+ self.assertEqual(len(expect), len(fcd))
self.assertItemsEqual(expect.keys(), fcd.keys())
self.assertItemsEqual(expect.values(), fcd.values())
self.assertItemsEqual(expect.items(), fcd.items())
$NetBSD: patch-Lib_test_test__urlparse.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Lib/test/test_urlparse.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ Lib/test/test_urlparse.py
@@ -3,6 +3,12 @@ import sys
import unicodedata
import unittest
import urlparse
+from test.support import EnvironmentVarGuard
+from warnings import catch_warnings, filterwarnings
+import tempfile
+import contextlib
+import os.path
+import shutil
RFC1808_BASE = "http://a/b/c/d;p?q#f"
RFC2396_BASE = "http://a/b/c/d;p?q"
@@ -24,16 +30,29 @@ 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')]),
+]
+
+parse_qsl_test_cases_semicolon = [
(";", []),
(";;", []),
(";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')]),
+]
+
+parse_qsl_test_cases_legacy = [
+ ("a=1;a=2&a=3", [('a', '1'), ('a', '2'), ('a', '3')]),
+ ("a=1;b=2&c=3", [('a', '1'), ('b', '2'), ('c', '3')]),
+ ("a=1&b=2&c=3;", [('a', '1'), ('b', '2'), ('c', '3')]),
+]
+
+parse_qsl_test_cases_warn = [
+ (";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')]),
+ ("a=1;a=2&a=3", [('a', '1;a=2'), ('a', '3')]),
+ (b"a=1;a=2&a=3", [(b'a', b'1;a=2'), (b'a', b'3')]),
]
parse_qs_test_cases = [
@@ -57,6 +76,9 @@ 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']}),
+]
+
+parse_qs_test_cases_semicolon = [
(";", {}),
(";;", {}),
(";a=b", {'a': ['b']}),
@@ -69,6 +91,24 @@ parse_qs_test_cases = [
(b"a=1;a=2", {b'a': [b'1', b'2']}),
]
+parse_qs_test_cases_legacy = [
+ ("a=1;a=2&a=3", {'a': ['1', '2', '3']}),
+ ("a=1;b=2&c=3", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+ ("a=1&b=2&c=3;", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+ (b"a=1;a=2&a=3", {b'a': [b'1', b'2', b'3']}),
+ (b"a=1;b=2&c=3", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+ (b"a=1&b=2&c=3;", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+]
+
+parse_qs_test_cases_warn = [
+ (";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']}),
+ ("a=1;a=2&a=3", {'a': ['1;a=2', '3']}),
+ (b"a=1;a=2&a=3", {b'a': [b'1;a=2', b'3']}),
+]
+
class UrlParseTestCase(unittest.TestCase):
def checkRoundtrips(self, url, parsed, split):
@@ -141,6 +181,40 @@ class UrlParseTestCase(unittest.TestCase
self.assertEqual(result, expect_without_blanks,
"Error parsing %r" % orig)
+ def test_qs_default_warn(self):
+ for orig, expect in parse_qs_test_cases_warn:
+ with catch_warnings(record=True) as w:
+ filterwarnings(action='always',
+ category=urlparse._QueryStringSeparatorWarning)
+ result = urlparse.parse_qs(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
+
+ def test_qsl_default_warn(self):
+ for orig, expect in parse_qsl_test_cases_warn:
+ with catch_warnings(record=True) as w:
+ filterwarnings(action='always',
+ category=urlparse._QueryStringSeparatorWarning)
+ result = urlparse.parse_qsl(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
+
+ def test_default_qs_no_warnings(self):
+ for orig, expect in parse_qs_test_cases:
+ with catch_warnings(record=True) as w:
+ result = urlparse.parse_qs(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_default_qsl_no_warnings(self):
+ for orig, expect in parse_qsl_test_cases:
+ with catch_warnings(record=True) as w:
+ result = urlparse.parse_qsl(orig, keep_blank_values=True)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
def test_roundtrips(self):
testcases = [
('file:///tmp/junk.txt',
@@ -626,6 +700,132 @@ class UrlParseTestCase(unittest.TestCase
self.assertEqual(urlparse.urlparse("http://www.python.org:80"),
('http','www.python.org:80','','','',''))
+ def test_parse_qs_separator_bytes(self):
+ expected = {b'a': [b'1'], b'b': [b'2']}
+
+ result = urlparse.parse_qs(b'a=1;b=2', separator=b';')
+ self.assertEqual(result, expected)
+ result = urlparse.parse_qs(b'a=1;b=2', separator=';')
+ self.assertEqual(result, expected)
+ result = urlparse.parse_qs('a=1;b=2', separator=';')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2']})
+
+ @contextlib.contextmanager
+ def _qsl_sep_config(self, sep):
+ """Context for the given parse_qsl default separator configured in config file"""
+ old_filename = urlparse._QS_SEPARATOR_CONFIG_FILENAME
+ urlparse._default_qs_separator = None
+ try:
+ tmpdirname = tempfile.mkdtemp()
+ filename = os.path.join(tmpdirname, 'conf.cfg')
+ with open(filename, 'w') as file:
+ file.write('[parse_qs]\n')
+ file.write('PYTHON_URLLIB_QS_SEPARATOR = {}'.format(sep))
+ urlparse._QS_SEPARATOR_CONFIG_FILENAME = filename
+ yield
+ finally:
+ urlparse._QS_SEPARATOR_CONFIG_FILENAME = old_filename
+ urlparse._default_qs_separator = None
+ shutil.rmtree(tmpdirname)
+
+ def test_parse_qs_separator_semicolon(self):
+ for orig, expect in parse_qs_test_cases_semicolon:
+ result = urlparse.parse_qs(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+ result = urlparse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+ result = urlparse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qsl_separator_semicolon(self):
+ for orig, expect in parse_qsl_test_cases_semicolon:
+ result = urlparse.parse_qsl(orig, separator=';')
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+ result = urlparse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+ result = urlparse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qs_separator_legacy(self):
+ for orig, expect in parse_qs_test_cases_legacy:
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+ result = urlparse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+ result = urlparse.parse_qs(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qsl_separator_legacy(self):
+ for orig, expect in parse_qsl_test_cases_legacy:
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+ result = urlparse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+ with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+ result = urlparse.parse_qsl(orig)
+ self.assertEqual(result, expect, "Error parsing %r" % orig)
+ self.assertEqual(len(w), 0)
+
+ def test_parse_qs_separator_bad_value_env_or_config(self):
+ for bad_sep in '', 'abc', 'safe', '&;', 'SEP':
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = bad_sep
+ with self.assertRaises(ValueError):
+ urlparse.parse_qsl('a=1;b=2')
+ with self._qsl_sep_config('bad_sep'), catch_warnings(record=True) as w:
+ with self.assertRaises(ValueError):
+ urlparse.parse_qsl('a=1;b=2')
+
+ def test_parse_qs_separator_bad_value_arg(self):
+ for bad_sep in True, {}, '':
+ with self.assertRaises(ValueError):
+ urlparse.parse_qsl('a=1;b=2', separator=bad_sep)
+
+ def test_parse_qs_separator_num_fields(self):
+ for qs, sep in (
+ ('a&b&c', '&'),
+ ('a;b;c', ';'),
+ ('a&b;c', 'legacy'),
+ ):
+ with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+ if sep != 'legacy':
+ with self.assertRaises(ValueError):
+ urlparse.parse_qsl(qs, separator=sep, max_num_fields=2)
+ if sep:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = sep
+ with self.assertRaises(ValueError):
+ urlparse.parse_qsl(qs, max_num_fields=2)
+
+ def test_parse_qs_separator_priority(self):
+ # env variable trumps config file
+ with self._qsl_sep_config('~'), EnvironmentVarGuard() as environ:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '!'
+ result = urlparse.parse_qs('a=1!b=2~c=3')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+ # argument trumps config file
+ with self._qsl_sep_config('~'):
+ result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+ # argument trumps env variable
+ with EnvironmentVarGuard() as environ:
+ environ['PYTHON_URLLIB_QS_SEPARATOR'] = '~'
+ result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
+ self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+
def test_urlsplit_normalization(self):
# Certain characters should never occur in the netloc,
# including under normalization.
$NetBSD: patch-Lib_urlparse.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $
Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch
--- Lib/urlparse.py.orig 2020-04-19 21:13:39.000000000 +0000
+++ Lib/urlparse.py
@@ -29,6 +29,7 @@ test_urlparse.py provides a good indicat
"""
import re
+import os
__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
"urlsplit", "urlunsplit", "parse_qs", "parse_qsl"]
@@ -382,7 +383,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=None):
"""Parse a query given as a string argument.
Arguments:
@@ -405,14 +407,23 @@ def parse_qs(qs, keep_blank_values=0, st
"""
dict = {}
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing,
- max_num_fields):
+ max_num_fields, 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):
+class _QueryStringSeparatorWarning(RuntimeWarning):
+ """Warning for using default `separator` in parse_qs or parse_qsl"""
+
+# The default "separator" for parse_qsl can be specified in a config file.
+# It's cached after first read.
+#_QS_SEPARATOR_CONFIG_FILENAME = '/etc/python/urllib.cfg'
+_default_qs_separator = None
+
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None,
+ separator=None):
"""Parse a query given as a string argument.
Arguments:
@@ -434,15 +445,72 @@ def parse_qsl(qs, keep_blank_values=0, s
Returns a list, as G-d intended.
"""
+
+ if (not separator or (not isinstance(separator, (str, bytes)))) and separator is not None:
+ raise ValueError("Separator must be of type string or bytes.")
+
+ # Used when both "&" and ";" act as separators. (Need a non-string value.)
+ _legacy = object()
+
+ if separator is None:
+ global _default_qs_separator
+ separator = _default_qs_separator
+ envvar_name = 'PYTHON_URLLIB_QS_SEPARATOR'
+ if separator is None:
+ # Set default separator from environment variable
+ separator = os.environ.get(envvar_name)
+ config_source = 'environment variable'
+ if separator is None:
+ # Set default separator from the configuration file
+ try:
+ file = open(_QS_SEPARATOR_CONFIG_FILENAME)
+ except EnvironmentError:
+ pass
+ else:
+ with file:
+ import ConfigParser
+ config = ConfigParser.ConfigParser()
+ config.readfp(file)
+ separator = config.get('parse_qs', envvar_name)
+ _default_qs_separator = separator
+ config_source = _QS_SEPARATOR_CONFIG_FILENAME
+ if separator is None:
+ # The default is '&', but warn if not specified explicitly
+ if ';' in qs:
+ from warnings import warn
+ warn("The default separator of urlparse.parse_qsl and "
+ + "parse_qs was changed to '&' to avoid a web cache "
+ + "poisoning issue (CVE-2021-23336). "
+ + "By default, semicolons no longer act as query field "
+ + "separators. "
+ + "See https://access.redhat.com/articles/5860431 for "
+ + "more details.",
+ _QueryStringSeparatorWarning, stacklevel=2)
+ separator = '&'
+ elif separator == 'legacy':
+ separator = _legacy
+ elif len(separator) != 1:
+ raise ValueError(
+ '{} (from {}) must contain '.format(envvar_name, config_source)
+ + '1 character, or "legacy". See '
+ + 'https://access.redhat.com/articles/5860431 for more details.'
+ )
+
# 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(';')
+ if separator is _legacy:
+ num_fields = 1 + qs.count('&') + qs.count(';')
+ else:
+ 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(';')]
+ if separator is _legacy:
+ pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+ else:
+ pairs = [s1 for s1 in qs.split(separator)]
r = []
for name_value in pairs:
if not name_value and not strict_parsing:
--- pkgsrc/lang/python27/patches/patch-Lib_httplib.py 2020/09/27 14:57:22 1.3
+++ pkgsrc/lang/python27/patches/patch-Lib_httplib.py 2021/10/13 21:04:01 1.3.8.1
| @@ -1,43 +1,93 @@ | | | @@ -1,43 +1,93 @@ |
1 | $NetBSD: patch-Lib_httplib.py,v 1.3 2020/09/27 14:57:22 leot Exp $ | | 1 | $NetBSD: patch-Lib_httplib.py,v 1.3.8.1 2021/10/13 21:04:01 tm Exp $ |
2 | | | 2 | |
3 | bpo-39603 (CVE-2020-26116): header injection via HTTP method | | 3 | bpo-39603 (CVE-2020-26116): header injection via HTTP method |
4 | | | 4 | |
5 | taken from: | | 5 | Taken from: |
6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 | | 6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 |
7 | | | 7 | |
| | | 8 | Fix CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue |
| | | 9 | Via Fedora: |
| | | 10 | https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00368-CVE-2021-3737.patch |
| | | 11 | |
8 | --- Lib/httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 | | 12 | --- Lib/httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 |
9 | +++ Lib/httplib.py | | 13 | +++ Lib/httplib.py |
10 | @@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.c | | 14 | @@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.c |
11 | # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") | | 15 | # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") |
12 | # We are more lenient for assumed real world compatibility purposes. | | 16 | # We are more lenient for assumed real world compatibility purposes. |
13 | | | 17 | |
14 | +# These characters are not allowed within HTTP method names | | 18 | +# These characters are not allowed within HTTP method names |
15 | +# to prevent http header injection. | | 19 | +# to prevent http header injection. |
16 | +_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]') | | 20 | +_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]') |
17 | + | | 21 | + |
18 | # We always set the Content-Length header for these methods because some | | 22 | # We always set the Content-Length header for these methods because some |
19 | # servers will otherwise respond with a 411 | | 23 | # servers will otherwise respond with a 411 |
20 | _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} | | 24 | _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} |
21 | @@ -935,6 +939,8 @@ class HTTPConnection: | | 25 | @@ -361,6 +365,25 @@ class HTTPMessage(mimetools.Message): |
| | | 26 | # It's not a header line; skip it and try the next line. |
| | | 27 | self.status = 'Non-header line where header expected' |
| | | 28 | |
| | | 29 | + |
| | | 30 | +def _read_headers(fp): |
| | | 31 | + """Reads potential header lines into a list from a file pointer. |
| | | 32 | + Length of line is limited by _MAXLINE, and number of |
| | | 33 | + headers is limited by _MAXHEADERS. |
| | | 34 | + """ |
| | | 35 | + headers = [] |
| | | 36 | + while True: |
| | | 37 | + line = fp.readline(_MAXLINE + 1) |
| | | 38 | + if len(line) > _MAXLINE: |
| | | 39 | + raise LineTooLong("header line") |
| | | 40 | + headers.append(line) |
| | | 41 | + if len(headers) > _MAXHEADERS: |
| | | 42 | + raise HTTPException("got more than %d headers" % _MAXHEADERS) |
| | | 43 | + if line in (b'\r\n', b'\n', b''): |
| | | 44 | + break |
| | | 45 | + return headers |
| | | 46 | + |
| | | 47 | + |
| | | 48 | class HTTPResponse: |
| | | 49 | |
| | | 50 | # strict: If true, raise BadStatusLine if the status line can't be |
| | | 51 | @@ -449,15 +472,10 @@ class HTTPResponse: |
| | | 52 | if status != CONTINUE: |
| | | 53 | break |
| | | 54 | # skip the header from the 100 response |
| | | 55 | - while True: |
| | | 56 | - skip = self.fp.readline(_MAXLINE + 1) |
| | | 57 | - if len(skip) > _MAXLINE: |
| | | 58 | - raise LineTooLong("header line") |
| | | 59 | - skip = skip.strip() |
| | | 60 | - if not skip: |
| | | 61 | - break |
| | | 62 | - if self.debuglevel > 0: |
| | | 63 | - print "header:", skip |
| | | 64 | + skipped_headers = _read_headers(self.fp) |
| | | 65 | + if self.debuglevel > 0: |
| | | 66 | + print("headers:", skipped_headers) |
| | | 67 | + del skipped_headers |
| | | 68 | |
| | | 69 | self.status = status |
| | | 70 | self.reason = reason.strip() |
| | | 71 | @@ -935,6 +953,8 @@ class HTTPConnection: |
22 | else: | | 72 | else: |
23 | raise CannotSendRequest() | | 73 | raise CannotSendRequest() |
24 | | | 74 | |
25 | + self._validate_method(method) | | 75 | + self._validate_method(method) |
26 | + | | 76 | + |
27 | # Save the method for use later in the response phase | | 77 | # Save the method for use later in the response phase |
28 | self._method = method | | 78 | self._method = method |
29 | | | 79 | |
30 | @@ -1020,6 +1026,17 @@ class HTTPConnection: | | 80 | @@ -1020,6 +1040,17 @@ class HTTPConnection: |
31 | # On Python 2, request is already encoded (default) | | 81 | # On Python 2, request is already encoded (default) |
32 | return request | | 82 | return request |
33 | | | 83 | |
34 | + def _validate_method(self, method): | | 84 | + def _validate_method(self, method): |
35 | + """Validate a method name for putrequest.""" | | 85 | + """Validate a method name for putrequest.""" |
36 | + # prevent http header injection | | 86 | + # prevent http header injection |
37 | + match = _contains_disallowed_method_pchar_re.search(method) | | 87 | + match = _contains_disallowed_method_pchar_re.search(method) |
38 | + if match: | | 88 | + if match: |
39 | + msg = ( | | 89 | + msg = ( |
40 | + "method can't contain control characters. {method!r} " | | 90 | + "method can't contain control characters. {method!r} " |
41 | + "(found at least {matched!r})" | | 91 | + "(found at least {matched!r})" |
42 | + ).format(matched=match.group(), method=method) | | 92 | + ).format(matched=match.group(), method=method) |
43 | + raise ValueError(msg) | | 93 | + raise ValueError(msg) |
--- pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py 2020/09/27 14:57:22 1.3
+++ pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py 2021/10/13 21:04:01 1.3.8.1
| @@ -1,20 +1,24 @@ | | | @@ -1,20 +1,24 @@ |
1 | $NetBSD: patch-Lib_test_test__httplib.py,v 1.3 2020/09/27 14:57:22 leot Exp $ | | 1 | $NetBSD: patch-Lib_test_test__httplib.py,v 1.3.8.1 2021/10/13 21:04:01 tm Exp $ |
2 | | | 2 | |
3 | bpo-39603 (CVE-2020-26116): header injection via HTTP method | | 3 | bpo-39603 (CVE-2020-26116): header injection via HTTP method |
4 | | | 4 | |
5 | taken from: | | 5 | Taken from: |
6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 | | 6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 |
7 | | | 7 | |
| | | 8 | Fix CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue |
| | | 9 | Via Fedora: |
| | | 10 | https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00368-CVE-2021-3737.patch |
| | | 11 | |
8 | --- Lib/test/test_httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 | | 12 | --- Lib/test/test_httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 |
9 | +++ Lib/test/test_httplib.py | | 13 | +++ Lib/test/test_httplib.py |
10 | @@ -384,6 +384,26 @@ class HeaderTests(TestCase): | | 14 | @@ -384,6 +384,26 @@ class HeaderTests(TestCase): |
11 | with self.assertRaisesRegexp(ValueError, 'Invalid header'): | | 15 | with self.assertRaisesRegexp(ValueError, 'Invalid header'): |
12 | conn.putheader(name, value) | | 16 | conn.putheader(name, value) |
13 | | | 17 | |
14 | + def test_invalid_method_names(self): | | 18 | + def test_invalid_method_names(self): |
15 | + methods = ( | | 19 | + methods = ( |
16 | + 'GET\r', | | 20 | + 'GET\r', |
17 | + 'POST\n', | | 21 | + 'POST\n', |
18 | + 'PUT\n\r', | | 22 | + 'PUT\n\r', |
19 | + 'POST\nValue', | | 23 | + 'POST\nValue', |
20 | + 'POST\nHOST:abc', | | 24 | + 'POST\nHOST:abc', |
| @@ -24,13 +28,28 @@ https://gitweb.gentoo.org/fork/cpython.g | | | @@ -24,13 +28,28 @@ https://gitweb.gentoo.org/fork/cpython.g |
24 | + '\nPUT' | | 28 | + '\nPUT' |
25 | + ) | | 29 | + ) |
26 | + | | 30 | + |
27 | + for method in methods: | | 31 | + for method in methods: |
28 | + with self.assertRaisesRegexp( | | 32 | + with self.assertRaisesRegexp( |
29 | + ValueError, "method can't contain control characters"): | | 33 | + ValueError, "method can't contain control characters"): |
30 | + conn = httplib.HTTPConnection('example.com') | | 34 | + conn = httplib.HTTPConnection('example.com') |
31 | + conn.sock = FakeSocket(None) | | 35 | + conn.sock = FakeSocket(None) |
32 | + conn.request(method=method, url="/") | | 36 | + conn.request(method=method, url="/") |
33 | + | | 37 | + |
34 | | | 38 | |
35 | class BasicTest(TestCase): | | 39 | class BasicTest(TestCase): |
36 | def test_status_lines(self): | | 40 | def test_status_lines(self): |
| | | 41 | @@ -655,6 +675,14 @@ class BasicTest(TestCase): |
| | | 42 | resp = httplib.HTTPResponse(FakeSocket(body)) |
| | | 43 | self.assertRaises(httplib.LineTooLong, resp.begin) |
| | | 44 | |
| | | 45 | + def test_overflowing_header_limit_after_100(self): |
| | | 46 | + body = ( |
| | | 47 | + 'HTTP/1.1 100 OK\r\n' |
| | | 48 | + 'r\n' * 32768 |
| | | 49 | + ) |
| | | 50 | + resp = httplib.HTTPResponse(FakeSocket(body)) |
| | | 51 | + self.assertRaises(httplib.HTTPException, resp.begin) |
| | | 52 | + |
| | | 53 | def test_overflowing_chunked_line(self): |
| | | 54 | body = ( |
| | | 55 | 'HTTP/1.1 200 OK\r\n' |
--- pkgsrc/lang/python27/patches/patch-Lib_urllib2.py 2020/09/20 12:10:27 1.2
+++ pkgsrc/lang/python27/patches/patch-Lib_urllib2.py 2021/10/13 21:04:01 1.2.10.1
| @@ -1,31 +1,36 @@ | | | @@ -1,31 +1,36 @@ |
1 | $NetBSD: patch-Lib_urllib2.py,v 1.2 2020/09/20 12:10:27 mgorny Exp $ | | 1 | $NetBSD: patch-Lib_urllib2.py,v 1.2.10.1 2021/10/13 21:04:01 tm Exp $ |
2 | | | 2 | |
3 | bpo-39503 (CVE-2020-8492): ReDoS on AbstractBasicAuthHandler | | 3 | bpo-39503 (CVE-2020-8492): ReDoS on AbstractBasicAuthHandler |
4 | | | 4 | |
5 | taken from: | | 5 | Taken from: |
6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=2273e65e11dd0234f2f51ebaef61fc6e848d4059 | | 6 | https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=2273e65e11dd0234f2f51ebaef61fc6e848d4059 |
7 | | | 7 | |
| | | 8 | bpo-43075 (CVE-2021-3733): Fix ReDoS in request |
| | | 9 | |
| | | 10 | Taken from: |
| | | 11 | https://github.com/python/cpython/pull/24391/ |
| | | 12 | |
8 | --- Lib/urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000 | | 13 | --- Lib/urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000 |
9 | +++ Lib/urllib2.py | | 14 | +++ Lib/urllib2.py |
10 | @@ -856,8 +856,15 @@ class AbstractBasicAuthHandler: | | 15 | @@ -856,8 +856,15 @@ class AbstractBasicAuthHandler: |
11 | | | 16 | |
12 | # allow for double- and single-quoted realm values | | 17 | # allow for double- and single-quoted realm values |
13 | # (single quotes are a violation of the RFC, but appear in the wild) | | 18 | # (single quotes are a violation of the RFC, but appear in the wild) |
14 | - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' | | 19 | - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' |
15 | - 'realm=(["\']?)([^"\']*)\\2', re.I) | | 20 | - 'realm=(["\']?)([^"\']*)\\2', re.I) |
16 | + rx = re.compile('(?:^|,)' # start of the string or ',' | | 21 | + rx = re.compile('(?:^|,)' # start of the string or ',' |
17 | + '[ \t]*' # optional whitespaces | | 22 | + '[ \t]*' # optional whitespaces |
18 | + '([^ \t]+)' # scheme like "Basic" | | 23 | + '([^ \t,]+)' # scheme like "Basic" |
19 | + '[ \t]+' # mandatory whitespaces | | 24 | + '[ \t]+' # mandatory whitespaces |
20 | + # realm=xxx | | 25 | + # realm=xxx |
21 | + # realm='xxx' | | 26 | + # realm='xxx' |
22 | + # realm="xxx" | | 27 | + # realm="xxx" |
23 | + 'realm=(["\']?)([^"\']*)\\2', | | 28 | + 'realm=(["\']?)([^"\']*)\\2', |
24 | + re.I) | | 29 | + re.I) |
25 | | | 30 | |
26 | # XXX could pre-emptively send auth info already accepted (RFC 2617, | | 31 | # XXX could pre-emptively send auth info already accepted (RFC 2617, |
27 | # end of section 2, and section 1.2 immediately after "credentials" | | 32 | # end of section 2, and section 1.2 immediately after "credentials" |
28 | @@ -869,23 +876,52 @@ class AbstractBasicAuthHandler: | | 33 | @@ -869,23 +876,52 @@ class AbstractBasicAuthHandler: |
29 | self.passwd = password_mgr | | 34 | self.passwd = password_mgr |
30 | self.add_password = self.passwd.add_password | | 35 | self.add_password = self.passwd.add_password |
31 | | | 36 | |
--- pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c 2021/06/23 18:30:24 1.1
+++ pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c 2021/10/13 21:04:01 1.1.2.1
| @@ -1,41 +1,150 @@ | | | @@ -1,41 +1,150 @@ |
1 | $NetBSD: patch-Modules___ctypes_callproc.c,v 1.1 2021/06/23 18:30:24 schmonz Exp $ | | 1 | $NetBSD: patch-Modules___ctypes_callproc.c,v 1.1.2.1 2021/10/13 21:04:01 tm Exp $ |
2 | | | 2 | |
3 | macOS arm64 support, via MacPorts. | | 3 | macOS arm64 support, via MacPorts. |
4 | | | 4 | |
5 | --- Modules/_ctypes/callproc.c.orig 2021-06-22 19:20:28.000000000 +0000 | | 5 | Fix CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs |
| | | 6 | Via Fedora: |
| | | 7 | https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00357-CVE-2021-3177.patch |
| | | 8 | |
| | | 9 | --- Modules/_ctypes/callproc.c.orig 2020-04-19 21:13:39.000000000 +0000 |
6 | +++ Modules/_ctypes/callproc.c | | 10 | +++ Modules/_ctypes/callproc.c |
7 | @@ -74,6 +74,10 @@ | | 11 | @@ -74,6 +74,10 @@ |
8 | #include <malloc.h> | | 12 | #include <malloc.h> |
9 | #endif | | 13 | #endif |
10 | | | 14 | |
11 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH | | 15 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH |
12 | +#include <mach-o/dyld.h> | | 16 | +#include <mach-o/dyld.h> |
13 | +#endif | | 17 | +#endif |
14 | + | | 18 | + |
15 | #include <ffi.h> | | 19 | #include <ffi.h> |
16 | #include "ctypes.h" | | 20 | #include "ctypes.h" |
17 | #ifdef HAVE_ALLOCA_H | | 21 | #ifdef HAVE_ALLOCA_H |
18 | @@ -773,7 +777,8 @@ static int _call_function_pointer(int fl | | 22 | @@ -460,50 +464,62 @@ PyCArg_dealloc(PyCArgObject *self) |
| | | 23 | static PyObject * |
| | | 24 | PyCArg_repr(PyCArgObject *self) |
| | | 25 | { |
| | | 26 | - char buffer[256]; |
| | | 27 | switch(self->tag) { |
| | | 28 | case 'b': |
| | | 29 | case 'B': |
| | | 30 | - sprintf(buffer, "<cparam '%c' (%d)>", |
| | | 31 | + return PyString_FromFormat("<cparam '%c' (%d)>", |
| | | 32 | self->tag, self->value.b); |
| | | 33 | - break; |
| | | 34 | case 'h': |
| | | 35 | case 'H': |
| | | 36 | - sprintf(buffer, "<cparam '%c' (%d)>", |
| | | 37 | + return PyString_FromFormat("<cparam '%c' (%d)>", |
| | | 38 | self->tag, self->value.h); |
| | | 39 | - break; |
| | | 40 | case 'i': |
| | | 41 | case 'I': |
| | | 42 | - sprintf(buffer, "<cparam '%c' (%d)>", |
| | | 43 | + return PyString_FromFormat("<cparam '%c' (%d)>", |
| | | 44 | self->tag, self->value.i); |
| | | 45 | - break; |
| | | 46 | case 'l': |
| | | 47 | case 'L': |
| | | 48 | - sprintf(buffer, "<cparam '%c' (%ld)>", |
| | | 49 | + return PyString_FromFormat("<cparam '%c' (%ld)>", |
| | | 50 | self->tag, self->value.l); |
| | | 51 | - break; |
| | | 52 | |
| | | 53 | #ifdef HAVE_LONG_LONG |
| | | 54 | case 'q': |
| | | 55 | case 'Q': |
| | | 56 | - sprintf(buffer, |
| | | 57 | - "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>", |
| | | 58 | + return PyString_FromFormat("<cparam '%c' (%lld)>", |
| | | 59 | self->tag, self->value.q); |
| | | 60 | - break; |
| | | 61 | #endif |
| | | 62 | case 'd': |
| | | 63 | - sprintf(buffer, "<cparam '%c' (%f)>", |
| | | 64 | - self->tag, self->value.d); |
| | | 65 | - break; |
| | | 66 | - case 'f': |
| | | 67 | - sprintf(buffer, "<cparam '%c' (%f)>", |
| | | 68 | - self->tag, self->value.f); |
| | | 69 | - break; |
| | | 70 | - |
| | | 71 | + case 'f': { |
| | | 72 | + PyObject *s = PyString_FromFormat("<cparam '%c' (", self->tag); |
| | | 73 | + if (s == NULL) { |
| | | 74 | + return NULL; |
| | | 75 | + } |
| | | 76 | + PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); |
| | | 77 | + if (f == NULL) { |
| | | 78 | + Py_DECREF(s); |
| | | 79 | + return NULL; |
| | | 80 | + } |
| | | 81 | + PyObject *r = PyObject_Repr(f); |
| | | 82 | + Py_DECREF(f); |
| | | 83 | + if (r == NULL) { |
| | | 84 | + Py_DECREF(s); |
| | | 85 | + return NULL; |
| | | 86 | + } |
| | | 87 | + PyString_ConcatAndDel(&s, r); |
| | | 88 | + if (s == NULL) { |
| | | 89 | + return NULL; |
| | | 90 | + } |
| | | 91 | + r = PyString_FromString(")>"); |
| | | 92 | + if (r == NULL) { |
| | | 93 | + Py_DECREF(s); |
| | | 94 | + return NULL; |
| | | 95 | + } |
| | | 96 | + PyString_ConcatAndDel(&s, r); |
| | | 97 | + return s; |
| | | 98 | + } |
| | | 99 | case 'c': |
| | | 100 | - sprintf(buffer, "<cparam '%c' (%c)>", |
| | | 101 | + return PyString_FromFormat("<cparam '%c' ('%c')>", |
| | | 102 | self->tag, self->value.c); |
| | | 103 | - break; |
| | | 104 | |
| | | 105 | /* Hm, are these 'z' and 'Z' codes useful at all? |
| | | 106 | Shouldn't they be replaced by the functionality of c_string |
| | | 107 | @@ -512,16 +528,13 @@ PyCArg_repr(PyCArgObject *self) |
| | | 108 | case 'z': |
| | | 109 | case 'Z': |
| | | 110 | case 'P': |
| | | 111 | - sprintf(buffer, "<cparam '%c' (%p)>", |
| | | 112 | + return PyUnicode_FromFormat("<cparam '%c' (%p)>", |
| | | 113 | self->tag, self->value.p); |
| | | 114 | - break; |
| | | 115 | |
| | | 116 | default: |
| | | 117 | - sprintf(buffer, "<cparam '%c' at %p>", |
| | | 118 | - self->tag, self); |
| | | 119 | - break; |
| | | 120 | + return PyString_FromFormat("<cparam '%c' at %p>", |
| | | 121 | + (unsigned char)self->tag, (void *)self); |
| | | 122 | } |
| | | 123 | - return PyString_FromString(buffer); |
| | | 124 | } |
| | | 125 | |
| | | 126 | static PyMemberDef PyCArgType_members[] = { |
| | | 127 | @@ -773,7 +786,8 @@ static int _call_function_pointer(int fl |
19 | ffi_type **atypes, | | 128 | ffi_type **atypes, |
20 | ffi_type *restype, | | 129 | ffi_type *restype, |
21 | void *resmem, | | 130 | void *resmem, |
22 | - int argcount) | | 131 | - int argcount) |
23 | + int argcount, | | 132 | + int argcount, |
24 | + int argtypecount) | | 133 | + int argtypecount) |
25 | { | | 134 | { |
26 | #ifdef WITH_THREAD | | 135 | #ifdef WITH_THREAD |
27 | PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */ | | 136 | PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */ |
28 | @@ -801,6 +806,37 @@ static int _call_function_pointer(int fl | | 137 | @@ -801,6 +815,37 @@ static int _call_function_pointer(int fl |
29 | if ((flags & FUNCFLAG_CDECL) == 0) | | 138 | if ((flags & FUNCFLAG_CDECL) == 0) |
30 | cc = FFI_STDCALL; | | 139 | cc = FFI_STDCALL; |
31 | #endif | | 140 | #endif |
32 | + | | 141 | + |
33 | + /* Even on Apple-arm64 the calling convention for variadic functions conincides | | 142 | + /* Even on Apple-arm64 the calling convention for variadic functions conincides |
34 | + * with the standard calling convention in the case that the function called | | 143 | + * with the standard calling convention in the case that the function called |
35 | + * only with its fixed arguments. Thus, we do not need a special flag to be | | 144 | + * only with its fixed arguments. Thus, we do not need a special flag to be |
36 | + * set on variadic functions. We treat a function as variadic if it is called | | 145 | + * set on variadic functions. We treat a function as variadic if it is called |
37 | + * with a nonzero number of variadic arguments */ | | 146 | + * with a nonzero number of variadic arguments */ |
38 | + int is_variadic = (argtypecount != 0 && argcount > argtypecount); | | 147 | + int is_variadic = (argtypecount != 0 && argcount > argtypecount); |
39 | + (void) is_variadic; | | 148 | + (void) is_variadic; |
40 | + | | 149 | + |
41 | +#if defined(__APPLE__) && defined(__arm64__) && !defined(HAVE_FFI_PREP_CIF_VAR) | | 150 | +#if defined(__APPLE__) && defined(__arm64__) && !defined(HAVE_FFI_PREP_CIF_VAR) |
| @@ -53,67 +162,67 @@ macOS arm64 support, via MacPorts. | | | @@ -53,67 +162,67 @@ macOS arm64 support, via MacPorts. |
53 | + argcount, | | 162 | + argcount, |
54 | + restype, | | 163 | + restype, |
55 | + atypes)) { | | 164 | + atypes)) { |
56 | + PyErr_SetString(PyExc_RuntimeError, | | 165 | + PyErr_SetString(PyExc_RuntimeError, |
57 | + "ffi_prep_cif_var failed"); | | 166 | + "ffi_prep_cif_var failed"); |
58 | + return -1; | | 167 | + return -1; |
59 | + } | | 168 | + } |
60 | + } else | | 169 | + } else |
61 | + #endif | | 170 | + #endif |
62 | + { | | 171 | + { |
63 | if (FFI_OK != ffi_prep_cif(&cif, | | 172 | if (FFI_OK != ffi_prep_cif(&cif, |
64 | cc, | | 173 | cc, |
65 | argcount, | | 174 | argcount, |
66 | @@ -810,6 +846,7 @@ static int _call_function_pointer(int fl | | 175 | @@ -810,6 +855,7 @@ static int _call_function_pointer(int fl |
67 | "ffi_prep_cif failed"); | | 176 | "ffi_prep_cif failed"); |
68 | return -1; | | 177 | return -1; |
69 | } | | 178 | } |
70 | + } | | 179 | + } |
71 | | | 180 | |
72 | if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { | | 181 | if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { |
73 | error_object = _ctypes_get_errobj(&space); | | 182 | error_object = _ctypes_get_errobj(&space); |
74 | @@ -1183,6 +1220,9 @@ PyObject *_ctypes_callproc(PPROC pProc, | | 183 | @@ -1183,6 +1229,9 @@ PyObject *_ctypes_callproc(PPROC pProc, |
75 | rtype, resbuf, | | 184 | rtype, resbuf, |
76 | Py_SAFE_DOWNCAST(argcount, | | 185 | Py_SAFE_DOWNCAST(argcount, |
77 | Py_ssize_t, | | 186 | Py_ssize_t, |
78 | + int), | | 187 | + int), |
79 | + Py_SAFE_DOWNCAST(argtype_count, | | 188 | + Py_SAFE_DOWNCAST(argtype_count, |
80 | + Py_ssize_t, | | 189 | + Py_ssize_t, |
81 | int))) | | 190 | int))) |
82 | goto cleanup; | | 191 | goto cleanup; |
83 | | | 192 | |
84 | @@ -1416,6 +1456,25 @@ copy_com_pointer(PyObject *self, PyObjec | | 193 | @@ -1416,6 +1465,25 @@ copy_com_pointer(PyObject *self, PyObjec |
85 | } | | 194 | } |
86 | #else | | 195 | #else |
87 | | | 196 | |
88 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH | | 197 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH |
89 | +static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) | | 198 | +static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) |
90 | +{ | | 199 | +{ |
91 | + if (__builtin_available(macos 11, ios 14, watchos 7, tvos 14, *)) { | | 200 | + if (__builtin_available(macos 11, ios 14, watchos 7, tvos 14, *)) { |
92 | + char *name_str; | | 201 | + char *name_str; |
93 | + if (!PyArg_ParseTuple(args, "z", &name_str)) | | 202 | + if (!PyArg_ParseTuple(args, "z", &name_str)) |
94 | + return NULL; | | 203 | + return NULL; |
95 | + | | 204 | + |
96 | + if(_dyld_shared_cache_contains_path(name_str)) | | 205 | + if(_dyld_shared_cache_contains_path(name_str)) |
97 | + Py_RETURN_TRUE; | | 206 | + Py_RETURN_TRUE; |
98 | + else | | 207 | + else |
99 | + Py_RETURN_FALSE; | | 208 | + Py_RETURN_FALSE; |
100 | + } else { | | 209 | + } else { |
101 | + PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing"); | | 210 | + PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing"); |
102 | + return NULL; | | 211 | + return NULL; |
103 | + } | | 212 | + } |
104 | +} | | 213 | +} |
105 | +#endif | | 214 | +#endif |
106 | + | | 215 | + |
107 | static PyObject *py_dl_open(PyObject *self, PyObject *args) | | 216 | static PyObject *py_dl_open(PyObject *self, PyObject *args) |
108 | { | | 217 | { |
109 | char *name; | | 218 | char *name; |
110 | @@ -1940,6 +1999,9 @@ PyMethodDef _ctypes_module_methods[] = { | | 219 | @@ -1940,6 +2008,9 @@ PyMethodDef _ctypes_module_methods[] = { |
111 | "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"}, | | 220 | "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"}, |
112 | {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"}, | | 221 | {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"}, |
113 | {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"}, | | 222 | {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"}, |
114 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH | | 223 | +#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH |
115 | + {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, | | 224 | + {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, |
116 | +#endif | | 225 | +#endif |
117 | #endif | | 226 | #endif |
118 | {"alignment", align_func, METH_O, alignment_doc}, | | 227 | {"alignment", align_func, METH_O, alignment_doc}, |
119 | {"sizeof", sizeof_func, METH_O, sizeof_doc}, | | 228 | {"sizeof", sizeof_func, METH_O, sizeof_doc}, |