diff options
| author | Steve Sakoman <steve@sakoman.com> | 2021-06-19 13:11:58 -1000 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-06-20 09:44:11 +0100 |
| commit | 43060f59ba60a0257864f1f7b25b51fac3f2d2cf (patch) | |
| tree | 3dcaf9d30ee6b58898cf962ee766435eb46ac2bd | |
| parent | f46f6af1f255544e4399e0ce4aee6ce9061a259d (diff) | |
| download | poky-43060f59ba60a0257864f1f7b25b51fac3f2d2cf.tar.gz | |
Revert "python3: fix CVE-2021-23336"yocto-3.1.9dunfell-23.0.9
Causes build failures on autobuilder
This reverts commit 8a59c47ce4c101b2470a06ecf101ca5ab7d1f82e.
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-devtools/python/python3/CVE-2021-23336.patch | 530 | ||||
| -rw-r--r-- | meta/recipes-devtools/python/python3_3.8.2.bb | 1 |
2 files changed, 0 insertions, 531 deletions
diff --git a/meta/recipes-devtools/python/python3/CVE-2021-23336.patch b/meta/recipes-devtools/python/python3/CVE-2021-23336.patch deleted file mode 100644 index 2a885b9d37..0000000000 --- a/meta/recipes-devtools/python/python3/CVE-2021-23336.patch +++ /dev/null | |||
| @@ -1,530 +0,0 @@ | |||
| 1 | From 3ab6f812653e79d008d5eba31dc25d34f3ca7170 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Senthil Kumaran <senthil@uthcode.com> | ||
| 3 | Date: Mon, 15 Feb 2021 10:15:02 -0800 | ||
| 4 | Subject: [PATCH] bpo-42967: only use '&' as a query string separator | ||
| 5 | MIME-Version: 1.0 | ||
| 6 | Content-Type: text/plain; charset=UTF-8 | ||
| 7 | Content-Transfer-Encoding: 8bit | ||
| 8 | |||
| 9 | (GH-24297) (#24529) | ||
| 10 | MIME-Version: 1.0 | ||
| 11 | Content-Type: text/plain; charset=UTF-8 | ||
| 12 | Content-Transfer-Encoding: 8bit | ||
| 13 | |||
| 14 | * bpo-42967: only use '&' as a query string separator (#24297) | ||
| 15 | |||
| 16 | bpo-42967: [security] Address a web cache-poisoning issue reported in | ||
| 17 | urllib.parse.parse_qsl(). | ||
| 18 | |||
| 19 | urllib.parse will only us "&" as query string separator by default | ||
| 20 | instead of both ";" and "&" as allowed in earlier versions. An optional | ||
| 21 | argument seperator with default value "&" is added to specify the | ||
| 22 | separator. | ||
| 23 | |||
| 24 | Co-authored-by: Éric Araujo <merwok@netwok.org> | ||
| 25 | Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> | ||
| 26 | Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> | ||
| 27 | Co-authored-by: Éric Araujo <merwok@netwok.org> | ||
| 28 | (cherry picked from commit fcbe0cb04d35189401c0c880ebfb4311e952d776) | ||
| 29 | |||
| 30 | * [3.8] bpo-42967: only use '&' as a query string separator (GH-24297) | ||
| 31 | |||
| 32 | bpo-42967: [security] Address a web cache-poisoning issue reported in urllib.parse.parse_qsl(). | ||
| 33 | |||
| 34 | 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. | ||
| 35 | |||
| 36 | Co-authored-by: Éric Araujo <merwok@netwok.org> | ||
| 37 | Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> | ||
| 38 | Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> | ||
| 39 | Co-authored-by: Éric Araujo <merwok@netwok.org>. | ||
| 40 | (cherry picked from commit fcbe0cb04d35189401c0c880ebfb4311e952d776) | ||
| 41 | |||
| 42 | Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com> | ||
| 43 | |||
| 44 | * Update correct version information. | ||
| 45 | |||
| 46 | * fix docs and make logic clearer | ||
| 47 | |||
| 48 | Co-authored-by: Adam Goldschmidt <adamgold7@gmail.com> | ||
| 49 | Co-authored-by: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> | ||
| 50 | |||
| 51 | Upstream-Status: Backport [https://github.com/python/cpython/commit/e3110c3cfbb7daa690d54d0eff6c264c870a71bf] | ||
| 52 | CVE: CVE-2020-23336 | ||
| 53 | Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com> | ||
| 54 | |||
| 55 | --- | ||
| 56 | Doc/library/cgi.rst | 11 ++- | ||
| 57 | Doc/library/urllib.parse.rst | 22 +++++- | ||
| 58 | Doc/whatsnew/3.6.rst | 13 ++++ | ||
| 59 | Doc/whatsnew/3.7.rst | 13 ++++ | ||
| 60 | Lib/cgi.py | 23 ++++--- | ||
| 61 | Lib/test/test_cgi.py | 29 ++++++-- | ||
| 62 | Lib/test/test_urlparse.py | 68 +++++++++++++------ | ||
| 63 | Lib/urllib/parse.py | 19 ++++-- | ||
| 64 | .../2021-02-14-15-59-16.bpo-42967.YApqDS.rst | 1 + | ||
| 65 | 9 files changed, 153 insertions(+), 46 deletions(-) | ||
| 66 | create mode 100644 Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst | ||
| 67 | |||
| 68 | diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst | ||
| 69 | index 4048592..880074b 100644 | ||
| 70 | --- a/Doc/library/cgi.rst | ||
| 71 | +++ b/Doc/library/cgi.rst | ||
| 72 | @@ -277,14 +277,16 @@ These are useful if you want more control, or if you want to employ some of the | ||
| 73 | algorithms implemented in this module in other circumstances. | ||
| 74 | |||
| 75 | |||
| 76 | -.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False) | ||
| 77 | +.. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&") | ||
| 78 | |||
| 79 | Parse a query in the environment or from a file (the file defaults to | ||
| 80 | - ``sys.stdin``). The *keep_blank_values* and *strict_parsing* parameters are | ||
| 81 | + ``sys.stdin``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are | ||
| 82 | passed to :func:`urllib.parse.parse_qs` unchanged. | ||
| 83 | |||
| 84 | + .. versionchanged:: 3.8.8 | ||
| 85 | + Added the *separator* parameter. | ||
| 86 | |||
| 87 | -.. function:: parse_multipart(fp, pdict, encoding="utf-8", errors="replace") | ||
| 88 | +.. function:: parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator="&") | ||
| 89 | |||
| 90 | Parse input of type :mimetype:`multipart/form-data` (for file uploads). | ||
| 91 | Arguments are *fp* for the input file, *pdict* for a dictionary containing | ||
| 92 | @@ -303,6 +305,9 @@ algorithms implemented in this module in other circumstances. | ||
| 93 | Added the *encoding* and *errors* parameters. For non-file fields, the | ||
| 94 | value is now a list of strings, not bytes. | ||
| 95 | |||
| 96 | + .. versionchanged:: 3.8.8 | ||
| 97 | + Added the *separator* parameter. | ||
| 98 | + | ||
| 99 | |||
| 100 | .. function:: parse_header(string) | ||
| 101 | |||
| 102 | diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst | ||
| 103 | index 52f98ef..45ca03a 100644 | ||
| 104 | --- a/Doc/library/urllib.parse.rst | ||
| 105 | +++ b/Doc/library/urllib.parse.rst | ||
| 106 | @@ -165,7 +165,7 @@ or on combining URL components into a URL string. | ||
| 107 | now raise :exc:`ValueError`. | ||
| 108 | |||
| 109 | |||
| 110 | -.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None) | ||
| 111 | +.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') | ||
| 112 | |||
| 113 | Parse a query string given as a string argument (data of type | ||
| 114 | :mimetype:`application/x-www-form-urlencoded`). Data are returned as a | ||
| 115 | @@ -190,6 +190,9 @@ or on combining URL components into a URL string. | ||
| 116 | read. If set, then throws a :exc:`ValueError` if there are more than | ||
| 117 | *max_num_fields* fields read. | ||
| 118 | |||
| 119 | + The optional argument *separator* is the symbol to use for separating the | ||
| 120 | + query arguments. It defaults to ``&``. | ||
| 121 | + | ||
| 122 | Use the :func:`urllib.parse.urlencode` function (with the ``doseq`` | ||
| 123 | parameter set to ``True``) to convert such dictionaries into query | ||
| 124 | strings. | ||
| 125 | @@ -201,8 +204,14 @@ or on combining URL components into a URL string. | ||
| 126 | .. versionchanged:: 3.8 | ||
| 127 | Added *max_num_fields* parameter. | ||
| 128 | |||
| 129 | + .. versionchanged:: 3.8.8 | ||
| 130 | + Added *separator* parameter with the default value of ``&``. Python | ||
| 131 | + versions earlier than Python 3.8.8 allowed using both ``;`` and ``&`` as | ||
| 132 | + query parameter separator. This has been changed to allow only a single | ||
| 133 | + separator key, with ``&`` as the default separator. | ||
| 134 | + | ||
| 135 | |||
| 136 | -.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None) | ||
| 137 | +.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') | ||
| 138 | |||
| 139 | Parse a query string given as a string argument (data of type | ||
| 140 | :mimetype:`application/x-www-form-urlencoded`). Data are returned as a list of | ||
| 141 | @@ -226,6 +235,9 @@ or on combining URL components into a URL string. | ||
| 142 | read. If set, then throws a :exc:`ValueError` if there are more than | ||
| 143 | *max_num_fields* fields read. | ||
| 144 | |||
| 145 | + The optional argument *separator* is the symbol to use for separating the | ||
| 146 | + query arguments. It defaults to ``&``. | ||
| 147 | + | ||
| 148 | Use the :func:`urllib.parse.urlencode` function to convert such lists of pairs into | ||
| 149 | query strings. | ||
| 150 | |||
| 151 | @@ -235,6 +247,12 @@ or on combining URL components into a URL string. | ||
| 152 | .. versionchanged:: 3.8 | ||
| 153 | Added *max_num_fields* parameter. | ||
| 154 | |||
| 155 | + .. versionchanged:: 3.8.8 | ||
| 156 | + Added *separator* parameter with the default value of ``&``. Python | ||
| 157 | + versions earlier than Python 3.8.8 allowed using both ``;`` and ``&`` as | ||
| 158 | + query parameter separator. This has been changed to allow only a single | ||
| 159 | + separator key, with ``&`` as the default separator. | ||
| 160 | + | ||
| 161 | |||
| 162 | .. function:: urlunparse(parts) | ||
| 163 | |||
| 164 | diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst | ||
| 165 | index 04c1f7e..4409a3a 100644 | ||
| 166 | --- a/Doc/whatsnew/3.6.rst | ||
| 167 | +++ b/Doc/whatsnew/3.6.rst | ||
| 168 | @@ -2443,3 +2443,16 @@ because of the behavior of the socket option ``SO_REUSEADDR`` in UDP. For more | ||
| 169 | details, see the documentation for ``loop.create_datagram_endpoint()``. | ||
| 170 | (Contributed by Kyle Stanley, Antoine Pitrou, and Yury Selivanov in | ||
| 171 | :issue:`37228`.) | ||
| 172 | + | ||
| 173 | +Notable changes in Python 3.6.13 | ||
| 174 | +================================ | ||
| 175 | + | ||
| 176 | +Earlier Python versions allowed using both ``;`` and ``&`` as | ||
| 177 | +query parameter separators in :func:`urllib.parse.parse_qs` and | ||
| 178 | +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with | ||
| 179 | +newer W3C recommendations, this has been changed to allow only a single | ||
| 180 | +separator key, with ``&`` as the default. This change also affects | ||
| 181 | +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected | ||
| 182 | +functions internally. For more details, please see their respective | ||
| 183 | +documentation. | ||
| 184 | +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) | ||
| 185 | diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst | ||
| 186 | index b9b5021..8f47a9f 100644 | ||
| 187 | --- a/Doc/whatsnew/3.7.rst | ||
| 188 | +++ b/Doc/whatsnew/3.7.rst | ||
| 189 | @@ -2556,3 +2556,16 @@ because of the behavior of the socket option ``SO_REUSEADDR`` in UDP. For more | ||
| 190 | details, see the documentation for ``loop.create_datagram_endpoint()``. | ||
| 191 | (Contributed by Kyle Stanley, Antoine Pitrou, and Yury Selivanov in | ||
| 192 | :issue:`37228`.) | ||
| 193 | + | ||
| 194 | +Notable changes in Python 3.7.10 | ||
| 195 | +================================ | ||
| 196 | + | ||
| 197 | +Earlier Python versions allowed using both ``;`` and ``&`` as | ||
| 198 | +query parameter separators in :func:`urllib.parse.parse_qs` and | ||
| 199 | +:func:`urllib.parse.parse_qsl`. Due to security concerns, and to conform with | ||
| 200 | +newer W3C recommendations, this has been changed to allow only a single | ||
| 201 | +separator key, with ``&`` as the default. This change also affects | ||
| 202 | +:func:`cgi.parse` and :func:`cgi.parse_multipart` as they use the affected | ||
| 203 | +functions internally. For more details, please see their respective | ||
| 204 | +documentation. | ||
| 205 | +(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.) | ||
| 206 | diff --git a/Lib/cgi.py b/Lib/cgi.py | ||
| 207 | index 5ace46a..13255a9 100755 | ||
| 208 | --- a/Lib/cgi.py | ||
| 209 | +++ b/Lib/cgi.py | ||
| 210 | @@ -106,7 +106,8 @@ log = initlog # The current logging function | ||
| 211 | # 0 ==> unlimited input | ||
| 212 | maxlen = 0 | ||
| 213 | |||
| 214 | -def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | ||
| 215 | +def parse(fp=None, environ=os.environ, keep_blank_values=0, | ||
| 216 | + strict_parsing=0, separator='&'): | ||
| 217 | """Parse a query in the environment or from a file (default stdin) | ||
| 218 | |||
| 219 | Arguments, all optional: | ||
| 220 | @@ -125,6 +126,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | ||
| 221 | strict_parsing: flag indicating what to do with parsing errors. | ||
| 222 | If false (the default), errors are silently ignored. | ||
| 223 | If true, errors raise a ValueError exception. | ||
| 224 | + | ||
| 225 | + separator: str. The symbol to use for separating the query arguments. | ||
| 226 | + Defaults to &. | ||
| 227 | """ | ||
| 228 | if fp is None: | ||
| 229 | fp = sys.stdin | ||
| 230 | @@ -145,7 +149,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | ||
| 231 | if environ['REQUEST_METHOD'] == 'POST': | ||
| 232 | ctype, pdict = parse_header(environ['CONTENT_TYPE']) | ||
| 233 | if ctype == 'multipart/form-data': | ||
| 234 | - return parse_multipart(fp, pdict) | ||
| 235 | + return parse_multipart(fp, pdict, separator=separator) | ||
| 236 | elif ctype == 'application/x-www-form-urlencoded': | ||
| 237 | clength = int(environ['CONTENT_LENGTH']) | ||
| 238 | if maxlen and clength > maxlen: | ||
| 239 | @@ -169,10 +173,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): | ||
| 240 | qs = "" | ||
| 241 | environ['QUERY_STRING'] = qs # XXX Shouldn't, really | ||
| 242 | return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, | ||
| 243 | - encoding=encoding) | ||
| 244 | + encoding=encoding, separator=separator) | ||
| 245 | |||
| 246 | |||
| 247 | -def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): | ||
| 248 | +def parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator='&'): | ||
| 249 | """Parse multipart input. | ||
| 250 | |||
| 251 | Arguments: | ||
| 252 | @@ -193,7 +197,7 @@ def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"): | ||
| 253 | headers.set_type(ctype) | ||
| 254 | headers['Content-Length'] = pdict['CONTENT-LENGTH'] | ||
| 255 | fs = FieldStorage(fp, headers=headers, encoding=encoding, errors=errors, | ||
| 256 | - environ={'REQUEST_METHOD': 'POST'}) | ||
| 257 | + environ={'REQUEST_METHOD': 'POST'}, separator=separator) | ||
| 258 | return {k: fs.getlist(k) for k in fs} | ||
| 259 | |||
| 260 | def _parseparam(s): | ||
| 261 | @@ -303,7 +307,7 @@ class FieldStorage: | ||
| 262 | def __init__(self, fp=None, headers=None, outerboundary=b'', | ||
| 263 | environ=os.environ, keep_blank_values=0, strict_parsing=0, | ||
| 264 | limit=None, encoding='utf-8', errors='replace', | ||
| 265 | - max_num_fields=None): | ||
| 266 | + max_num_fields=None, separator='&'): | ||
| 267 | """Constructor. Read multipart/* until last part. | ||
| 268 | |||
| 269 | Arguments, all optional: | ||
| 270 | @@ -351,6 +355,7 @@ class FieldStorage: | ||
| 271 | self.keep_blank_values = keep_blank_values | ||
| 272 | self.strict_parsing = strict_parsing | ||
| 273 | self.max_num_fields = max_num_fields | ||
| 274 | + self.separator = separator | ||
| 275 | if 'REQUEST_METHOD' in environ: | ||
| 276 | method = environ['REQUEST_METHOD'].upper() | ||
| 277 | self.qs_on_post = None | ||
| 278 | @@ -577,7 +582,7 @@ class FieldStorage: | ||
| 279 | query = urllib.parse.parse_qsl( | ||
| 280 | qs, self.keep_blank_values, self.strict_parsing, | ||
| 281 | encoding=self.encoding, errors=self.errors, | ||
| 282 | - max_num_fields=self.max_num_fields) | ||
| 283 | + max_num_fields=self.max_num_fields, separator=self.separator) | ||
| 284 | self.list = [MiniFieldStorage(key, value) for key, value in query] | ||
| 285 | self.skip_lines() | ||
| 286 | |||
| 287 | @@ -593,7 +598,7 @@ class FieldStorage: | ||
| 288 | query = urllib.parse.parse_qsl( | ||
| 289 | self.qs_on_post, self.keep_blank_values, self.strict_parsing, | ||
| 290 | encoding=self.encoding, errors=self.errors, | ||
| 291 | - max_num_fields=self.max_num_fields) | ||
| 292 | + max_num_fields=self.max_num_fields, separator=self.separator) | ||
| 293 | self.list.extend(MiniFieldStorage(key, value) for key, value in query) | ||
| 294 | |||
| 295 | klass = self.FieldStorageClass or self.__class__ | ||
| 296 | @@ -637,7 +642,7 @@ class FieldStorage: | ||
| 297 | else self.limit - self.bytes_read | ||
| 298 | part = klass(self.fp, headers, ib, environ, keep_blank_values, | ||
| 299 | strict_parsing, limit, | ||
| 300 | - self.encoding, self.errors, max_num_fields) | ||
| 301 | + self.encoding, self.errors, max_num_fields, self.separator) | ||
| 302 | |||
| 303 | if max_num_fields is not None: | ||
| 304 | max_num_fields -= 1 | ||
| 305 | diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py | ||
| 306 | index ab86771..bda03ee 100644 | ||
| 307 | --- a/Lib/test/test_cgi.py | ||
| 308 | +++ b/Lib/test/test_cgi.py | ||
| 309 | @@ -53,12 +53,9 @@ parse_strict_test_cases = [ | ||
| 310 | ("", ValueError("bad query field: ''")), | ||
| 311 | ("&", ValueError("bad query field: ''")), | ||
| 312 | ("&&", ValueError("bad query field: ''")), | ||
| 313 | - (";", ValueError("bad query field: ''")), | ||
| 314 | - (";&;", ValueError("bad query field: ''")), | ||
| 315 | # Should the next few really be valid? | ||
| 316 | ("=", {}), | ||
| 317 | ("=&=", {}), | ||
| 318 | - ("=;=", {}), | ||
| 319 | # This rest seem to make sense | ||
| 320 | ("=a", {'': ['a']}), | ||
| 321 | ("&=a", ValueError("bad query field: ''")), | ||
| 322 | @@ -73,8 +70,6 @@ parse_strict_test_cases = [ | ||
| 323 | ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), | ||
| 324 | ("a=a+b&a=b+a", {'a': ['a b', 'b a']}), | ||
| 325 | ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), | ||
| 326 | - ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), | ||
| 327 | - ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), | ||
| 328 | ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env", | ||
| 329 | {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'], | ||
| 330 | 'cuyer': ['r'], | ||
| 331 | @@ -187,6 +182,30 @@ Content-Length: 3 | ||
| 332 | else: | ||
| 333 | self.assertEqual(fs.getvalue(key), expect_val[0]) | ||
| 334 | |||
| 335 | + def test_separator(self): | ||
| 336 | + parse_semicolon = [ | ||
| 337 | + ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}), | ||
| 338 | + ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), | ||
| 339 | + (";", ValueError("bad query field: ''")), | ||
| 340 | + (";;", ValueError("bad query field: ''")), | ||
| 341 | + ("=;a", ValueError("bad query field: 'a'")), | ||
| 342 | + (";b=a", ValueError("bad query field: ''")), | ||
| 343 | + ("b;=a", ValueError("bad query field: 'b'")), | ||
| 344 | + ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), | ||
| 345 | + ("a=a+b;a=b+a", {'a': ['a b', 'b a']}), | ||
| 346 | + ] | ||
| 347 | + for orig, expect in parse_semicolon: | ||
| 348 | + env = {'QUERY_STRING': orig} | ||
| 349 | + fs = cgi.FieldStorage(separator=';', environ=env) | ||
| 350 | + if isinstance(expect, dict): | ||
| 351 | + for key in expect.keys(): | ||
| 352 | + expect_val = expect[key] | ||
| 353 | + self.assertIn(key, fs) | ||
| 354 | + if len(expect_val) > 1: | ||
| 355 | + self.assertEqual(fs.getvalue(key), expect_val) | ||
| 356 | + else: | ||
| 357 | + self.assertEqual(fs.getvalue(key), expect_val[0]) | ||
| 358 | + | ||
| 359 | def test_log(self): | ||
| 360 | cgi.log("Testing") | ||
| 361 | |||
| 362 | diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py | ||
| 363 | index 4ae6ed3..90c8d69 100644 | ||
| 364 | --- a/Lib/test/test_urlparse.py | ||
| 365 | +++ b/Lib/test/test_urlparse.py | ||
| 366 | @@ -32,16 +32,10 @@ parse_qsl_test_cases = [ | ||
| 367 | (b"&a=b", [(b'a', b'b')]), | ||
| 368 | (b"a=a+b&b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), | ||
| 369 | (b"a=1&a=2", [(b'a', b'1'), (b'a', b'2')]), | ||
| 370 | - (";", []), | ||
| 371 | - (";;", []), | ||
| 372 | - (";a=b", [('a', 'b')]), | ||
| 373 | - ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), | ||
| 374 | - ("a=1;a=2", [('a', '1'), ('a', '2')]), | ||
| 375 | - (b";", []), | ||
| 376 | - (b";;", []), | ||
| 377 | - (b";a=b", [(b'a', b'b')]), | ||
| 378 | - (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), | ||
| 379 | - (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), | ||
| 380 | + (";a=b", [(';a', 'b')]), | ||
| 381 | + ("a=a+b;b=b+c", [('a', 'a b;b=b c')]), | ||
| 382 | + (b";a=b", [(b';a', b'b')]), | ||
| 383 | + (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]), | ||
| 384 | ] | ||
| 385 | |||
| 386 | # Each parse_qs testcase is a two-tuple that contains | ||
| 387 | @@ -68,16 +62,10 @@ parse_qs_test_cases = [ | ||
| 388 | (b"&a=b", {b'a': [b'b']}), | ||
| 389 | (b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), | ||
| 390 | (b"a=1&a=2", {b'a': [b'1', b'2']}), | ||
| 391 | - (";", {}), | ||
| 392 | - (";;", {}), | ||
| 393 | - (";a=b", {'a': ['b']}), | ||
| 394 | - ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), | ||
| 395 | - ("a=1;a=2", {'a': ['1', '2']}), | ||
| 396 | - (b";", {}), | ||
| 397 | - (b";;", {}), | ||
| 398 | - (b";a=b", {b'a': [b'b']}), | ||
| 399 | - (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), | ||
| 400 | - (b"a=1;a=2", {b'a': [b'1', b'2']}), | ||
| 401 | + (";a=b", {';a': ['b']}), | ||
| 402 | + ("a=a+b;b=b+c", {'a': ['a b;b=b c']}), | ||
| 403 | + (b";a=b", {b';a': [b'b']}), | ||
| 404 | + (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}), | ||
| 405 | ] | ||
| 406 | |||
| 407 | class UrlParseTestCase(unittest.TestCase): | ||
| 408 | @@ -884,10 +872,46 @@ class UrlParseTestCase(unittest.TestCase): | ||
| 409 | def test_parse_qsl_max_num_fields(self): | ||
| 410 | with self.assertRaises(ValueError): | ||
| 411 | urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10) | ||
| 412 | - with self.assertRaises(ValueError): | ||
| 413 | - urllib.parse.parse_qs(';'.join(['a=a']*11), max_num_fields=10) | ||
| 414 | urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10) | ||
| 415 | |||
| 416 | + def test_parse_qs_separator(self): | ||
| 417 | + parse_qs_semicolon_cases = [ | ||
| 418 | + (";", {}), | ||
| 419 | + (";;", {}), | ||
| 420 | + (";a=b", {'a': ['b']}), | ||
| 421 | + ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), | ||
| 422 | + ("a=1;a=2", {'a': ['1', '2']}), | ||
| 423 | + (b";", {}), | ||
| 424 | + (b";;", {}), | ||
| 425 | + (b";a=b", {b'a': [b'b']}), | ||
| 426 | + (b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}), | ||
| 427 | + (b"a=1;a=2", {b'a': [b'1', b'2']}), | ||
| 428 | + ] | ||
| 429 | + for orig, expect in parse_qs_semicolon_cases: | ||
| 430 | + with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): | ||
| 431 | + result = urllib.parse.parse_qs(orig, separator=';') | ||
| 432 | + self.assertEqual(result, expect, "Error parsing %r" % orig) | ||
| 433 | + | ||
| 434 | + | ||
| 435 | + def test_parse_qsl_separator(self): | ||
| 436 | + parse_qsl_semicolon_cases = [ | ||
| 437 | + (";", []), | ||
| 438 | + (";;", []), | ||
| 439 | + (";a=b", [('a', 'b')]), | ||
| 440 | + ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]), | ||
| 441 | + ("a=1;a=2", [('a', '1'), ('a', '2')]), | ||
| 442 | + (b";", []), | ||
| 443 | + (b";;", []), | ||
| 444 | + (b";a=b", [(b'a', b'b')]), | ||
| 445 | + (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]), | ||
| 446 | + (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]), | ||
| 447 | + ] | ||
| 448 | + for orig, expect in parse_qsl_semicolon_cases: | ||
| 449 | + with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"): | ||
| 450 | + result = urllib.parse.parse_qsl(orig, separator=';') | ||
| 451 | + self.assertEqual(result, expect, "Error parsing %r" % orig) | ||
| 452 | + | ||
| 453 | + | ||
| 454 | def test_urlencode_sequences(self): | ||
| 455 | # Other tests incidentally urlencode things; test non-covered cases: | ||
| 456 | # Sequence and object values. | ||
| 457 | diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py | ||
| 458 | index e2b6f13..5a3e847 100644 | ||
| 459 | --- a/Lib/urllib/parse.py | ||
| 460 | +++ b/Lib/urllib/parse.py | ||
| 461 | @@ -648,7 +648,7 @@ def unquote(string, encoding='utf-8', errors='replace'): | ||
| 462 | |||
| 463 | |||
| 464 | def parse_qs(qs, keep_blank_values=False, strict_parsing=False, | ||
| 465 | - encoding='utf-8', errors='replace', max_num_fields=None): | ||
| 466 | + encoding='utf-8', errors='replace', max_num_fields=None, separator='&'): | ||
| 467 | """Parse a query given as a string argument. | ||
| 468 | |||
| 469 | Arguments: | ||
| 470 | @@ -672,12 +672,15 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False, | ||
| 471 | max_num_fields: int. If set, then throws a ValueError if there | ||
| 472 | are more than n fields read by parse_qsl(). | ||
| 473 | |||
| 474 | + separator: str. The symbol to use for separating the query arguments. | ||
| 475 | + Defaults to &. | ||
| 476 | + | ||
| 477 | Returns a dictionary. | ||
| 478 | """ | ||
| 479 | parsed_result = {} | ||
| 480 | pairs = parse_qsl(qs, keep_blank_values, strict_parsing, | ||
| 481 | encoding=encoding, errors=errors, | ||
| 482 | - max_num_fields=max_num_fields) | ||
| 483 | + max_num_fields=max_num_fields, separator=separator) | ||
| 484 | for name, value in pairs: | ||
| 485 | if name in parsed_result: | ||
| 486 | parsed_result[name].append(value) | ||
| 487 | @@ -687,7 +690,7 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False, | ||
| 488 | |||
| 489 | |||
| 490 | def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, | ||
| 491 | - encoding='utf-8', errors='replace', max_num_fields=None): | ||
| 492 | + encoding='utf-8', errors='replace', max_num_fields=None, separator='&'): | ||
| 493 | """Parse a query given as a string argument. | ||
| 494 | |||
| 495 | Arguments: | ||
| 496 | @@ -710,19 +713,25 @@ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, | ||
| 497 | max_num_fields: int. If set, then throws a ValueError | ||
| 498 | if there are more than n fields read by parse_qsl(). | ||
| 499 | |||
| 500 | + separator: str. The symbol to use for separating the query arguments. | ||
| 501 | + Defaults to &. | ||
| 502 | + | ||
| 503 | Returns a list, as G-d intended. | ||
| 504 | """ | ||
| 505 | qs, _coerce_result = _coerce_args(qs) | ||
| 506 | |||
| 507 | + if not separator or (not isinstance(separator, (str, bytes))): | ||
| 508 | + raise ValueError("Separator must be of type string or bytes.") | ||
| 509 | + | ||
| 510 | # If max_num_fields is defined then check that the number of fields | ||
| 511 | # is less than max_num_fields. This prevents a memory exhaustion DOS | ||
| 512 | # attack via post bodies with many fields. | ||
| 513 | if max_num_fields is not None: | ||
| 514 | - num_fields = 1 + qs.count('&') + qs.count(';') | ||
| 515 | + num_fields = 1 + qs.count(separator) | ||
| 516 | if max_num_fields < num_fields: | ||
| 517 | raise ValueError('Max number of fields exceeded') | ||
| 518 | |||
| 519 | - pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] | ||
| 520 | + pairs = [s1 for s1 in qs.split(separator)] | ||
| 521 | r = [] | ||
| 522 | for name_value in pairs: | ||
| 523 | if not name_value and not strict_parsing: | ||
| 524 | 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 | ||
| 525 | new file mode 100644 | ||
| 526 | index 0000000..f08489b | ||
| 527 | --- /dev/null | ||
| 528 | +++ b/Misc/NEWS.d/next/Security/2021-02-14-15-59-16.bpo-42967.YApqDS.rst | ||
| 529 | @@ -0,0 +1 @@ | ||
| 530 | +Fix web cache poisoning vulnerability by defaulting the query args separator to ``&``, and allowing the user to choose a custom separator. | ||
diff --git a/meta/recipes-devtools/python/python3_3.8.2.bb b/meta/recipes-devtools/python/python3_3.8.2.bb index 762e9444b8..072ce97472 100644 --- a/meta/recipes-devtools/python/python3_3.8.2.bb +++ b/meta/recipes-devtools/python/python3_3.8.2.bb | |||
| @@ -39,7 +39,6 @@ SRC_URI = "http://www.python.org/ftp/python/${PV}/Python-${PV}.tar.xz \ | |||
| 39 | file://CVE-2020-26116.patch \ | 39 | file://CVE-2020-26116.patch \ |
| 40 | file://CVE-2020-27619.patch \ | 40 | file://CVE-2020-27619.patch \ |
| 41 | file://CVE-2021-3177.patch \ | 41 | file://CVE-2021-3177.patch \ |
| 42 | file://CVE-2021-23336.patch \ | ||
| 43 | " | 42 | " |
| 44 | 43 | ||
| 45 | SRC_URI_append_class-native = " \ | 44 | SRC_URI_append_class-native = " \ |
