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

Patch by Demian Brecht.
This commit is contained in:
Serhiy Storchaka 2015-03-13 09:05:01 +02:00
parent 79fbeee237
commit 577fc4e87f
3 changed files with 57 additions and 15 deletions

View file

@ -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:

View file

@ -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):
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):
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):
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):
["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

View file

@ -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.