]> granicus.if.org Git - python/commitdiff
Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar.
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 13 Mar 2015 07:05:01 +0000 (09:05 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Fri, 13 Mar 2015 07:05:01 +0000 (09:05 +0200)
Patch by Demian Brecht.

Lib/http/cookiejar.py
Lib/test/test_http_cookiejar.py
Misc/NEWS

index 7e4982f3e4d0f5aff2caed7719c9ff550a6dc986..bfc6ae99932b3fe9f2b29c81a4b8fa21728570e2 100644 (file)
@@ -472,26 +472,42 @@ def parse_ns_headers(ns_headers):
     for ns_header in ns_headers:
         pairs = []
         version_set = False
-        for ii, param in enumerate(re.split(r";\s*", ns_header)):
-            param = param.rstrip()
-            if param == "": continue
-            if "=" not in param:
-                k, v = param, None
-            else:
-                k, v = re.split(r"\s*=\s*", param, 1)
-                k = k.lstrip()
+
+        # XXX: The following does not strictly adhere to RFCs in that empty
+        # names and values are legal (the former will only appear once and will
+        # be overwritten if multiple occurrences are present). This is
+        # 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:
+                    continue
+
+            # allow for a distinction between present and empty and missing
+            # altogether
+            val = val.strip() if sep else None
+
             if ii != 0:
-                lc = k.lower()
+                lc = key.lower()
                 if lc in known_attrs:
-                    k = lc
-                if k == "version":
+                    key = lc
+
+                if key == "version":
                     # This is an RFC 2109 cookie.
-                    v = strip_quotes(v)
+                    if val is not None:
+                        val = strip_quotes(val)
                     version_set = True
-                if k == "expires":
+                elif key == "expires":
                     # convert expires date to seconds since epoch
-                    v = http2time(strip_quotes(v))  # None if invalid
-            pairs.append((k, v))
+                    if val is not None:
+                        val = http2time(strip_quotes(val))  # None if invalid
+            pairs.append((key, val))
 
         if pairs:
             if not version_set:
index fb66f6f2df063f4080bcdd5d5996c49640a64b6a..e9f0356050783238ac9bd304ccbcb288e54d9059 100644 (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/", 'spam=eggs; '
                           '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"]
         self.assertEqual(cookie.domain, ".acme.com")
@@ -505,6 +508,16 @@ class CookieTests(unittest.TestCase):
         self.assertIsNone(foo.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):
         # names such as 'expires' are not special in first name=value pair
         # of Set-Cookie: header
@@ -1080,6 +1093,13 @@ class CookieTests(unittest.TestCase):
             parse_ns_headers(["foo"]),
             [[("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
         self.assertEqual(parse_ns_headers([""]), [])
 
@@ -1092,6 +1112,8 @@ class CookieTests(unittest.TestCase):
             c.extract_cookies(r, req)
             return c
 
+        future = time2netscape(time.time()+3600)
+
         # none of these bad headers should cause an exception to be raised
         for headers in [
             ["Set-Cookie: "],  # actually, nothing wrong with this
@@ -1102,6 +1124,7 @@ class CookieTests(unittest.TestCase):
             ["Set-Cookie: b=foo; max-age=oops"],
             # bad version
             ["Set-Cookie: b=foo; version=spam"],
+            ["Set-Cookie:; Expires=%s" % future],
             ]:
             c = cookiejar_from_cookie_headers(headers)
             # these bad cookies shouldn't be set
index 793cc107417de54a851792dc6ccd421f6775f592..170cd4e94cb66a971c7909caa139d4def82e3064 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Core and Builtins
 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
   handle exceptions raised by an iterator.  Patch by Alon Diamant and Davin
   Potts.