Commit 577fc4e8 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar.

Patch by Demian Brecht.
parent 79fbeee2
...@@ -472,26 +472,42 @@ def parse_ns_headers(ns_headers): ...@@ -472,26 +472,42 @@ def parse_ns_headers(ns_headers):
for ns_header in ns_headers: for ns_header in ns_headers:
pairs = [] pairs = []
version_set = False version_set = False
for ii, param in enumerate(re.split(r";\s*", ns_header)):
param = param.rstrip() # XXX: The following does not strictly adhere to RFCs in that empty
if param == "": continue # names and values are legal (the former will only appear once and will
if "=" not in param: # be overwritten if multiple occurrences are present). This is
k, v = param, None # mostly to deal with backwards compatibility.
for ii, param in enumerate(ns_header.split(';')):
param = param.strip()
key, sep, val = param.partition('=')
key = key.strip()
if not key:
if ii == 0:
break
else: else:
k, v = re.split(r"\s*=\s*", param, 1) continue
k = k.lstrip()
# allow for a distinction between present and empty and missing
# altogether
val = val.strip() if sep else None
if ii != 0: if ii != 0:
lc = k.lower() lc = key.lower()
if lc in known_attrs: if lc in known_attrs:
k = lc key = lc
if k == "version":
if key == "version":
# This is an RFC 2109 cookie. # This is an RFC 2109 cookie.
v = strip_quotes(v) if val is not None:
val = strip_quotes(val)
version_set = True version_set = True
if k == "expires": elif key == "expires":
# convert expires date to seconds since epoch # convert expires date to seconds since epoch
v = http2time(strip_quotes(v)) # None if invalid if val is not None:
pairs.append((k, v)) val = http2time(strip_quotes(val)) # None if invalid
pairs.append((key, val))
if pairs: if pairs:
if not version_set: if not version_set:
......
...@@ -479,6 +479,9 @@ class CookieTests(unittest.TestCase): ...@@ -479,6 +479,9 @@ class CookieTests(unittest.TestCase):
interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=') interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=')
interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; ' interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; '
'expires="Foo Bar 25 33:22:11 3022"') 'expires="Foo Bar 25 33:22:11 3022"')
interact_netscape(c, 'http://www.acme.com/', 'fortytwo=')
interact_netscape(c, 'http://www.acme.com/', '=unladenswallow')
interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade')
cookie = c._cookies[".acme.com"]["/"]["spam"] cookie = c._cookies[".acme.com"]["/"]["spam"]
self.assertEqual(cookie.domain, ".acme.com") self.assertEqual(cookie.domain, ".acme.com")
...@@ -505,6 +508,16 @@ class CookieTests(unittest.TestCase): ...@@ -505,6 +508,16 @@ class CookieTests(unittest.TestCase):
self.assertIsNone(foo.expires) self.assertIsNone(foo.expires)
self.assertIsNone(spam.expires) self.assertIsNone(spam.expires)
cookie = c._cookies['www.acme.com']['/']['fortytwo']
self.assertIsNotNone(cookie.value)
self.assertEqual(cookie.value, '')
# there should be a distinction between a present but empty value
# (above) and a value that's entirely missing (below)
cookie = c._cookies['www.acme.com']['/']['holyhandgrenade']
self.assertIsNone(cookie.value)
def test_ns_parser_special_names(self): def test_ns_parser_special_names(self):
# names such as 'expires' are not special in first name=value pair # names such as 'expires' are not special in first name=value pair
# of Set-Cookie: header # of Set-Cookie: header
...@@ -1080,6 +1093,13 @@ class CookieTests(unittest.TestCase): ...@@ -1080,6 +1093,13 @@ class CookieTests(unittest.TestCase):
parse_ns_headers(["foo"]), parse_ns_headers(["foo"]),
[[("foo", None), ("version", "0")]] [[("foo", None), ("version", "0")]]
) )
# missing cookie values for parsed attributes
self.assertEqual(
parse_ns_headers(['foo=bar; expires']),
[[('foo', 'bar'), ('expires', None), ('version', '0')]])
self.assertEqual(
parse_ns_headers(['foo=bar; version']),
[[('foo', 'bar'), ('version', None)]])
# shouldn't add version if header is empty # shouldn't add version if header is empty
self.assertEqual(parse_ns_headers([""]), []) self.assertEqual(parse_ns_headers([""]), [])
...@@ -1092,6 +1112,8 @@ class CookieTests(unittest.TestCase): ...@@ -1092,6 +1112,8 @@ class CookieTests(unittest.TestCase):
c.extract_cookies(r, req) c.extract_cookies(r, req)
return c return c
future = time2netscape(time.time()+3600)
# none of these bad headers should cause an exception to be raised # none of these bad headers should cause an exception to be raised
for headers in [ for headers in [
["Set-Cookie: "], # actually, nothing wrong with this ["Set-Cookie: "], # actually, nothing wrong with this
...@@ -1102,6 +1124,7 @@ class CookieTests(unittest.TestCase): ...@@ -1102,6 +1124,7 @@ class CookieTests(unittest.TestCase):
["Set-Cookie: b=foo; max-age=oops"], ["Set-Cookie: b=foo; max-age=oops"],
# bad version # bad version
["Set-Cookie: b=foo; version=spam"], ["Set-Cookie: b=foo; version=spam"],
["Set-Cookie:; Expires=%s" % future],
]: ]:
c = cookiejar_from_cookie_headers(headers) c = cookiejar_from_cookie_headers(headers)
# these bad cookies shouldn't be set # these bad cookies shouldn't be set
......
...@@ -18,6 +18,9 @@ Core and Builtins ...@@ -18,6 +18,9 @@ Core and Builtins
Library Library
------- -------
- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar.
Patch by Demian Brecht.
- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now - Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now
handle exceptions raised by an iterator. Patch by Alon Diamant and Davin handle exceptions raised by an iterator. Patch by Alon Diamant and Davin
Potts. Potts.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment