[Python-checkins] bpo-38976: Add support for HTTP Only flag in MozillaCookieJar (#17471)

orsenthil webhook-mailer at python.org
Fri Oct 23 18:49:04 EDT 2020


https://github.com/python/cpython/commit/16ee68da6e12bb2d79751b32cc37523fe4f4bb48
commit: 16ee68da6e12bb2d79751b32cc37523fe4f4bb48
branch: master
author: Jacob Neil Taylor <me at jacobtaylor.id.au>
committer: orsenthil <skumaran at gatech.edu>
date: 2020-10-23T15:48:55-07:00
summary:

bpo-38976: Add support for HTTP Only flag in MozillaCookieJar (#17471)

Add support for HTTP Only flag in MozillaCookieJar

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst
M Lib/http/cookiejar.py
M Lib/test/test_http_cookiejar.py

diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py
index 47ed5c3d64ab7..eaa76c26b9c59 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -50,10 +50,18 @@ def _debug(*args):
         logger = logging.getLogger("http.cookiejar")
     return logger.debug(*args)
 
-
+HTTPONLY_ATTR = "HTTPOnly"
+HTTPONLY_PREFIX = "#HttpOnly_"
 DEFAULT_HTTP_PORT = str(http.client.HTTP_PORT)
+NETSCAPE_MAGIC_RGX = re.compile("#( Netscape)? HTTP Cookie File")
 MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar "
                          "instance initialised with one)")
+NETSCAPE_HEADER_TEXT =  """\
+# Netscape HTTP Cookie File
+# http://curl.haxx.se/rfc/cookie_spec.html
+# This is a generated file!  Do not edit.
+
+"""
 
 def _warn_unhandled_exception():
     # There are a few catch-all except: statements in this module, for
@@ -2004,19 +2012,11 @@ class MozillaCookieJar(FileCookieJar):
     header by default (Mozilla can cope with that).
 
     """
-    magic_re = re.compile("#( Netscape)? HTTP Cookie File")
-    header = """\
-# Netscape HTTP Cookie File
-# http://curl.haxx.se/rfc/cookie_spec.html
-# This is a generated file!  Do not edit.
-
-"""
 
     def _really_load(self, f, filename, ignore_discard, ignore_expires):
         now = time.time()
 
-        magic = f.readline()
-        if not self.magic_re.search(magic):
+        if not NETSCAPE_MAGIC_RGX.match(f.readline()):
             raise LoadError(
                 "%r does not look like a Netscape format cookies file" %
                 filename)
@@ -2024,8 +2024,17 @@ def _really_load(self, f, filename, ignore_discard, ignore_expires):
         try:
             while 1:
                 line = f.readline()
+                rest = {}
+
                 if line == "": break
 
+                # httponly is a cookie flag as defined in rfc6265
+                # when encoded in a netscape cookie file,
+                # the line is prepended with "#HttpOnly_"
+                if line.startswith(HTTPONLY_PREFIX):
+                    rest[HTTPONLY_ATTR] = ""
+                    line = line[len(HTTPONLY_PREFIX):]
+
                 # last field may be absent, so keep any trailing tab
                 if line.endswith("\n"): line = line[:-1]
 
@@ -2063,7 +2072,7 @@ def _really_load(self, f, filename, ignore_discard, ignore_expires):
                            discard,
                            None,
                            None,
-                           {})
+                           rest)
                 if not ignore_discard and c.discard:
                     continue
                 if not ignore_expires and c.is_expired(now):
@@ -2083,16 +2092,17 @@ def save(self, filename=None, ignore_discard=False, ignore_expires=False):
             else: raise ValueError(MISSING_FILENAME_TEXT)
 
         with open(filename, "w") as f:
-            f.write(self.header)
+            f.write(NETSCAPE_HEADER_TEXT)
             now = time.time()
             for cookie in self:
+                domain = cookie.domain
                 if not ignore_discard and cookie.discard:
                     continue
                 if not ignore_expires and cookie.is_expired(now):
                     continue
                 if cookie.secure: secure = "TRUE"
                 else: secure = "FALSE"
-                if cookie.domain.startswith("."): initial_dot = "TRUE"
+                if domain.startswith("."): initial_dot = "TRUE"
                 else: initial_dot = "FALSE"
                 if cookie.expires is not None:
                     expires = str(cookie.expires)
@@ -2107,7 +2117,9 @@ def save(self, filename=None, ignore_discard=False, ignore_expires=False):
                 else:
                     name = cookie.name
                     value = cookie.value
+                if cookie.has_nonstandard_attr(HTTPONLY_ATTR):
+                    domain = HTTPONLY_PREFIX + domain
                 f.write(
-                    "\t".join([cookie.domain, initial_dot, cookie.path,
+                    "\t".join([domain, initial_dot, cookie.path,
                                secure, expires, name, value])+
                     "\n")
diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py
index 99d038fa15c1e..fdf15efde12ef 100644
--- a/Lib/test/test_http_cookiejar.py
+++ b/Lib/test/test_http_cookiejar.py
@@ -1773,6 +1773,10 @@ def test_mozilla(self):
         interact_netscape(c, "http://www.foo.com/",
                           "fooc=bar; Domain=www.foo.com; %s" % expires)
 
+        for cookie in c:
+            if cookie.name == "foo1":
+                cookie.set_nonstandard_attr("HTTPOnly", "")
+
         def save_and_restore(cj, ignore_discard):
             try:
                 cj.save(ignore_discard=ignore_discard)
@@ -1787,6 +1791,7 @@ def save_and_restore(cj, ignore_discard):
         new_c = save_and_restore(c, True)
         self.assertEqual(len(new_c), 6)  # none discarded
         self.assertIn("name='foo1', value='bar'", repr(new_c))
+        self.assertIn("rest={'HTTPOnly': ''}", repr(new_c))
 
         new_c = save_and_restore(c, False)
         self.assertEqual(len(new_c), 4)  # 2 of them discarded on save
diff --git a/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst b/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst
new file mode 100644
index 0000000000000..7a48943a6c6cc
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-12-05-05-22-49.bpo-38976.5MG7Uu.rst
@@ -0,0 +1,4 @@
+The :mod:`http.cookiejar` module now supports the parsing of cookies in CURL-style cookiejar files through MozillaCookieJar 
+on all platforms.  Previously, such cookie entries would be silently ignored when loading a cookiejar with such entries.
+
+Additionally, the HTTP Only attribute is persisted in the object, and will be correctly written to file if the MozillaCookieJar object is subsequently dumped.
\ No newline at end of file



More information about the Python-checkins mailing list