Skip to content

Commit 577fc4e

Browse files
Issue python#23138: Fixed parsing cookies with absent keys or values in cookiejar.
Patch by Demian Brecht.
1 parent 79fbeee commit 577fc4e

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

Lib/http/cookiejar.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -472,26 +472,42 @@ def parse_ns_headers(ns_headers):
472472
for ns_header in ns_headers:
473473
pairs = []
474474
version_set = False
475-
for ii, param in enumerate(re.split(r";\s*", ns_header)):
476-
param = param.rstrip()
477-
if param == "": continue
478-
if "=" not in param:
479-
k, v = param, None
480-
else:
481-
k, v = re.split(r"\s*=\s*", param, 1)
482-
k = k.lstrip()
475+
476+
# XXX: The following does not strictly adhere to RFCs in that empty
477+
# names and values are legal (the former will only appear once and will
478+
# be overwritten if multiple occurrences are present). This is
479+
# mostly to deal with backwards compatibility.
480+
for ii, param in enumerate(ns_header.split(';')):
481+
param = param.strip()
482+
483+
key, sep, val = param.partition('=')
484+
key = key.strip()
485+
486+
if not key:
487+
if ii == 0:
488+
break
489+
else:
490+
continue
491+
492+
# allow for a distinction between present and empty and missing
493+
# altogether
494+
val = val.strip() if sep else None
495+
483496
if ii != 0:
484-
lc = k.lower()
497+
lc = key.lower()
485498
if lc in known_attrs:
486-
k = lc
487-
if k == "version":
499+
key = lc
500+
501+
if key == "version":
488502
# This is an RFC 2109 cookie.
489-
v = strip_quotes(v)
503+
if val is not None:
504+
val = strip_quotes(val)
490505
version_set = True
491-
if k == "expires":
506+
elif key == "expires":
492507
# convert expires date to seconds since epoch
493-
v = http2time(strip_quotes(v)) # None if invalid
494-
pairs.append((k, v))
508+
if val is not None:
509+
val = http2time(strip_quotes(val)) # None if invalid
510+
pairs.append((key, val))
495511

496512
if pairs:
497513
if not version_set:

Lib/test/test_http_cookiejar.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ def test_ns_parser(self):
479479
interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=')
480480
interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; '
481481
'expires="Foo Bar 25 33:22:11 3022"')
482+
interact_netscape(c, 'http://www.acme.com/', 'fortytwo=')
483+
interact_netscape(c, 'http://www.acme.com/', '=unladenswallow')
484+
interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade')
482485

483486
cookie = c._cookies[".acme.com"]["/"]["spam"]
484487
self.assertEqual(cookie.domain, ".acme.com")
@@ -505,6 +508,16 @@ def test_ns_parser(self):
505508
self.assertIsNone(foo.expires)
506509
self.assertIsNone(spam.expires)
507510

511+
cookie = c._cookies['www.acme.com']['/']['fortytwo']
512+
self.assertIsNotNone(cookie.value)
513+
self.assertEqual(cookie.value, '')
514+
515+
# there should be a distinction between a present but empty value
516+
# (above) and a value that's entirely missing (below)
517+
518+
cookie = c._cookies['www.acme.com']['/']['holyhandgrenade']
519+
self.assertIsNone(cookie.value)
520+
508521
def test_ns_parser_special_names(self):
509522
# names such as 'expires' are not special in first name=value pair
510523
# of Set-Cookie: header
@@ -1080,6 +1093,13 @@ def test_parse_ns_headers(self):
10801093
parse_ns_headers(["foo"]),
10811094
[[("foo", None), ("version", "0")]]
10821095
)
1096+
# missing cookie values for parsed attributes
1097+
self.assertEqual(
1098+
parse_ns_headers(['foo=bar; expires']),
1099+
[[('foo', 'bar'), ('expires', None), ('version', '0')]])
1100+
self.assertEqual(
1101+
parse_ns_headers(['foo=bar; version']),
1102+
[[('foo', 'bar'), ('version', None)]])
10831103
# shouldn't add version if header is empty
10841104
self.assertEqual(parse_ns_headers([""]), [])
10851105

@@ -1092,6 +1112,8 @@ def cookiejar_from_cookie_headers(headers):
10921112
c.extract_cookies(r, req)
10931113
return c
10941114

1115+
future = time2netscape(time.time()+3600)
1116+
10951117
# none of these bad headers should cause an exception to be raised
10961118
for headers in [
10971119
["Set-Cookie: "], # actually, nothing wrong with this
@@ -1102,6 +1124,7 @@ def cookiejar_from_cookie_headers(headers):
11021124
["Set-Cookie: b=foo; max-age=oops"],
11031125
# bad version
11041126
["Set-Cookie: b=foo; version=spam"],
1127+
["Set-Cookie:; Expires=%s" % future],
11051128
]:
11061129
c = cookiejar_from_cookie_headers(headers)
11071130
# these bad cookies shouldn't be set

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar.
22+
Patch by Demian Brecht.
23+
2124
- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now
2225
handle exceptions raised by an iterator. Patch by Alon Diamant and Davin
2326
Potts.

0 commit comments

Comments
 (0)