https://github.com/python/cpython/commit/d59d96853be8def13cb419d50991af77c0e26330
commit: d59d96853be8def13cb419d50991af77c0e26330
branch: main
author: Gregory P. Smith <[email protected]>
committer: gpshead <[email protected]>
date: 2026-10-25T07:01:05Z
summary:
gh-160300: Reject cookies with a negative version in http.cookiejar (GH-160301)
DefaultCookiePolicy only applied its domain checks to cookies with
version 0 or a positive version.
ANT-2026-TA36I7V7
files:
A Misc/NEWS.d/next/Security/2026-10-22-08-04-33.gh-issue-160300.q7Zk2B.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 5c5dc78b064885d..21dd6a9a6c2f523 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -974,6 +974,12 @@ def set_ok_version(self, cookie, request):
_debug(" Set-Cookie2 without version attribute (%s=%s)",
cookie.name, cookie.value)
return False
+ if cookie.version < 0:
+ # Neither the Netscape (version 0) nor the RFC 2965 (version > 0)
+ # rules would apply, so the domain checks would all be skipped.
+ _debug(" negative version attribute (%s=%s)",
+ cookie.name, cookie.value)
+ return False
if cookie.version > 0 and not self.rfc2965:
_debug(" RFC 2965 cookies are switched off")
return False
@@ -1117,6 +1123,9 @@ def return_ok(self, cookie, request):
return True
def return_ok_version(self, cookie, request):
+ if cookie.version < 0:
+ _debug(" negative version attribute")
+ return False
if cookie.version > 0 and not self.rfc2965:
_debug(" RFC 2965 cookies are switched off")
return False
@@ -1505,6 +1514,8 @@ def _cookie_from_cookie_tuple(self, tup, request):
version = int(version)
except ValueError:
return None # invalid version, ignore cookie
+ if version < 0:
+ return None # invalid version, ignore cookie
secure = standard.get("secure", False)
# (discard is also set if expires is Absent)
discard = standard.get("discard", False)
diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py
index 218181f11853659..da6f468b2f37188 100644
--- a/Lib/test/test_http_cookiejar.py
+++ b/Lib/test/test_http_cookiejar.py
@@ -943,10 +943,37 @@ def test_wrong_domain(self):
# XXX far from complete
c = CookieJar()
- interact_2965(c, "http://www.nasty.com/",
- 'foo=bar; domain=friendly.org; Version="1"')
+ interact_2965(c, "http://www.example.com/",
+ 'foo=bar; domain=python.org; Version="1"')
self.assertEqual(len(c), 0)
+ def test_negative_version(self):
+ # A negative version is neither Netscape (0) nor RFC 2965 (> 0), so
+ # such cookies must be neither set nor returned.
+ for rfc2965 in False, True:
+ for interact in interact_netscape, interact_2965:
+ with self.subTest(rfc2965=rfc2965, interact=interact.__name__):
+ c = CookieJar(DefaultCookiePolicy(rfc2965=rfc2965))
+ interact(c, "http://www.example.com/",
+ 'foo=bar; domain=python.org; Version=-1')
+ self.assertEqual(len(c), 0)
+ interact(c, "http://www.example.com/", 'foo=bar;
Version=-1')
+ self.assertEqual(len(c), 0)
+
+ # Such a header is discarded before the policy is consulted.
+ c = CookieJar()
+ req = urllib.request.Request("http://www.example.com/")
+ res = FakeResponse(["Set-Cookie: foo=bar; Version=-1"],
+ "http://www.example.com/")
+ self.assertEqual(c.make_cookies(res, req), [])
+
+ # Not set from a response, for example loaded from an LWP cookie file.
+ c = CookieJar()
+ c.set_cookie(Cookie(-1, "foo", "bar", None, False,
+ ".python.org", True, True, "/", True,
+ False, None, True, None, None, {}))
+ self.assertEqual(interact_netscape(c, "http://www.python.org/"), "")
+
def test_strict_domain(self):
# Cookies whose domain is a country-code tld like .co.uk should
# not be set if CookiePolicy.strict_domain is true.
@@ -1446,6 +1473,7 @@ def cookiejar_from_cookie_headers(headers):
["Set-Cookie: b=foo; max-age=oops"],
# bad version
["Set-Cookie: b=foo; version=spam"],
+ ["Set-Cookie: b=foo; version=-1"],
["Set-Cookie:; Expires=%s" % future],
]:
c = cookiejar_from_cookie_headers(headers)
diff --git
a/Misc/NEWS.d/next/Security/2026-10-22-08-04-33.gh-issue-160300.q7Zk2B.rst
b/Misc/NEWS.d/next/Security/2026-10-22-08-04-33.gh-issue-160300.q7Zk2B.rst
new file mode 100644
index 000000000000000..3cadc6c2c4fd1fb
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-10-22-08-04-33.gh-issue-160300.q7Zk2B.rst
@@ -0,0 +1,3 @@
+Fix :class:`http.cookiejar.DefaultCookiePolicy` accepting and returning
+cookies with a negative ``Version`` attribute without checking their domain
+against the request host. Such cookies are now ignored.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]