mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
bpo-42967: only use '&' as a query string separator (#24297)
bpo-42967: [security] Address a web cache-poisoning issue reported in urllib.parse.parse_qsl(). urllib.parse will only us "&" as query string separator by default instead of both ";" and "&" as allowed in earlier versions. An optional argument seperator with default value "&" is added to specify the separator. Co-authored-by: Éric Araujo <merwok@netwok.org> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Co-authored-by: Éric Araujo <merwok@netwok.org>
This commit is contained in:
parent
1b57426e3a
commit
fcbe0cb04d
12 changed files with 186 additions and 47 deletions
|
@ -32,16 +32,10 @@ parse_qsl_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'a', b'2')]),
|
||||
(";", []),
|
||||
(";;", []),
|
||||
(";a=b", [('a', 'b')]),
|
||||
("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
|
||||
("a=1;a=2", [('a', '1'), ('a', '2')]),
|
||||
(b";", []),
|
||||
(b";;", []),
|
||||
(b";a=b", [(b'a', b'b')]),
|
||||
(b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
|
||||
(b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
|
||||
(";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')]),
|
||||
]
|
||||
|
||||
# Each parse_qs testcase is a two-tuple that contains
|
||||
|
@ -68,16 +62,10 @@ parse_qs_test_cases = [
|
|||
(b"&a=b", {b'a': [b'b']}),
|
||||
(b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
|
||||
(b"a=1&a=2", {b'a': [b'1', b'2']}),
|
||||
(";", {}),
|
||||
(";;", {}),
|
||||
(";a=b", {'a': ['b']}),
|
||||
("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
|
||||
("a=1;a=2", {'a': ['1', '2']}),
|
||||
(b";", {}),
|
||||
(b";;", {}),
|
||||
(b";a=b", {b'a': [b'b']}),
|
||||
(b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
|
||||
(b"a=1;a=2", {b'a': [b'1', b'2']}),
|
||||
(";a=b", {';a': ['b']}),
|
||||
("a=a+b;b=b+c", {'a': ['a b;b=b c']}),
|
||||
(b";a=b", {b';a': [b'b']}),
|
||||
(b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}),
|
||||
]
|
||||
|
||||
class UrlParseTestCase(unittest.TestCase):
|
||||
|
@ -886,10 +874,46 @@ class UrlParseTestCase(unittest.TestCase):
|
|||
def test_parse_qsl_max_num_fields(self):
|
||||
with self.assertRaises(ValueError):
|
||||
urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10)
|
||||
with self.assertRaises(ValueError):
|
||||
urllib.parse.parse_qs(';'.join(['a=a']*11), max_num_fields=10)
|
||||
urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10)
|
||||
|
||||
def test_parse_qs_separator(self):
|
||||
parse_qs_semicolon_cases = [
|
||||
(";", {}),
|
||||
(";;", {}),
|
||||
(";a=b", {'a': ['b']}),
|
||||
("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
|
||||
("a=1;a=2", {'a': ['1', '2']}),
|
||||
(b";", {}),
|
||||
(b";;", {}),
|
||||
(b";a=b", {b'a': [b'b']}),
|
||||
(b"a=a+b;b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
|
||||
(b"a=1;a=2", {b'a': [b'1', b'2']}),
|
||||
]
|
||||
for orig, expect in parse_qs_semicolon_cases:
|
||||
with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"):
|
||||
result = urllib.parse.parse_qs(orig, separator=';')
|
||||
self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
|
||||
|
||||
def test_parse_qsl_separator(self):
|
||||
parse_qsl_semicolon_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')]),
|
||||
(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')]),
|
||||
]
|
||||
for orig, expect in parse_qsl_semicolon_cases:
|
||||
with self.subTest(f"Original: {orig!r}, Expected: {expect!r}"):
|
||||
result = urllib.parse.parse_qsl(orig, separator=';')
|
||||
self.assertEqual(result, expect, "Error parsing %r" % orig)
|
||||
|
||||
|
||||
def test_urlencode_sequences(self):
|
||||
# Other tests incidentally urlencode things; test non-covered cases:
|
||||
# Sequence and object values.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue