Skip to content

gh-135768: fix allowed/blocked IPv6 domains in http.cookiejar #135771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 62 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
e3cfeb6
Update cookiejar.py
LamentXU123 Jun 20, 2025
8c5320a
📜🤖 Added by blurb_it.
blurb-it[bot] Jun 20, 2025
4c452f9
Update 2025-06-20-17-03-51.gh-issue-135768.DhUJWf.rst
LamentXU123 Jun 20, 2025
60304ce
Update 2025-06-20-17-03-51.gh-issue-135768.DhUJWf.rst
LamentXU123 Jun 20, 2025
714d4d8
Update 2025-06-20-17-03-51.gh-issue-135768.DhUJWf.rst
LamentXU123 Jun 20, 2025
4c9ab02
Update Lib/http/cookiejar.py
LamentXU123 Jun 20, 2025
2c9f6a4
Update Lib/http/cookiejar.py
LamentXU123 Jun 20, 2025
a5198ee
Update Lib/http/cookiejar.py
LamentXU123 Jun 20, 2025
baf62d1
Update Lib/http/cookiejar.py
LamentXU123 Jun 20, 2025
0783fc1
Update cookiejar.py
LamentXU123 Jun 20, 2025
dffb204
add IPV6 regex
LamentXU123 Jun 20, 2025
056b44d
Update Misc/NEWS.d/next/Library/2025-06-20-17-03-51.gh-issue-135768.D…
LamentXU123 Jun 20, 2025
37c1933
add string method instead of regex to detect IPV6
LamentXU123 Jun 20, 2025
779e443
update is_ip
LamentXU123 Jun 20, 2025
1c00c39
update comments
LamentXU123 Jun 20, 2025
4c12cec
Update test_http_cookiejar.py
LamentXU123 Jun 20, 2025
0cf11d7
Update Lib/http/cookiejar.py
LamentXU123 Jun 21, 2025
88a6af1
Update Lib/http/cookiejar.py
LamentXU123 Jun 21, 2025
d8411c0
Update cookiejar.py
LamentXU123 Jun 21, 2025
7646042
more test added
LamentXU123 Jun 21, 2025
9eb52e3
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
09f5a61
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
d440b54
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
f9aa74f
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
7f65ca9
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
a3a93f5
Merge branch 'main' into support-IPv6-in-cookiejar
LamentXU123 Jun 21, 2025
dd04e81
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
e553fe8
Update Lib/http/cookiejar.py
LamentXU123 Jun 21, 2025
af9d29e
Update Lib/test/test_http_cookiejar.py
LamentXU123 Jun 21, 2025
56ac545
Update cookiejar.py
LamentXU123 Jun 21, 2025
f90b354
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
3d1cc91
Update cookiejar.py
LamentXU123 Jun 21, 2025
0879750
update is_ip
LamentXU123 Jun 21, 2025
7731e7f
delete test for ::1
LamentXU123 Jun 21, 2025
35f8b9e
Merge branch 'main' into support-IPv6-in-cookiejar
LamentXU123 Jun 21, 2025
9c4a91f
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
1b0f228
Update test_http_cookiejar.py
LamentXU123 Jun 21, 2025
8f2c9c3
Change is_ip to is_ip_like
LamentXU123 Jun 21, 2025
e01652e
Update test script for is_ip_like
LamentXU123 Jun 21, 2025
3d59ed9
Update cookiejar.py
LamentXU123 Jun 21, 2025
befc9e2
Update test_http_cookiejar.py
LamentXU123 Jun 22, 2025
57fca8b
Update cookiejar.py
LamentXU123 Jun 22, 2025
abb4320
Update Lib/test/test_http_cookiejar.py
LamentXU123 Jun 22, 2025
2aac70f
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
3b9620c
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
267d499
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
a875311
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
720c80f
add false test for ::1
LamentXU123 Jun 22, 2025
2f90ecd
Update cookiejar.py
LamentXU123 Jun 22, 2025
c37042c
Update cookiejar.py
LamentXU123 Jun 22, 2025
7b6feea
Update cookiejar.py
LamentXU123 Jun 22, 2025
1fd504b
Update cookiejar.py
LamentXU123 Jun 22, 2025
6238901
Update cookiejar.py
LamentXU123 Jun 22, 2025
aef5edf
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
c5a1be2
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
e3b6a1c
Update cookiejar.py
LamentXU123 Jun 22, 2025
e37015c
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
36cb03b
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
6fe7c76
Merge branch 'main' into support-IPv6-in-cookiejar
LamentXU123 Jun 22, 2025
e7e1d83
Update Lib/http/cookiejar.py
LamentXU123 Jun 22, 2025
3cea83c
Merge branch 'main' into support-IPv6-in-cookiejar
LamentXU123 Jun 23, 2025
1ab21ea
Update ACKS
LamentXU123 Jun 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions Lib/http/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,15 +532,41 @@ def parse_ns_headers(ns_headers):
return result


# only kept for backwards compatibilty.
IPV4_RE = re.compile(r"\.\d+$", re.ASCII)

def _is_ipv4_hostname(text):
from ipaddress import IPv4Address
try:
IPv4Address(text)
except ValueError:
return False
return True

def _is_ipv6_hostname(text):
if text.startswith('[') and text.endswith(']'):
from ipaddress import IPv6Address
try:
IPv6Address(text[1:-1])
except ValueError:
return False
return True
return False

def is_ip_like_hostname(text):
"""Check if *text* is a valid IP-like hostname.

A valid IP-like hostname is either an IPv4 address or
an IPv6 enclosed in brackets (for instance, "[::1]").
"""
return _is_ipv4_hostname(text) or _is_ipv6_hostname(text)

def is_HDN(text):
"""Return True if text is a host domain name."""
# XXX
# This may well be wrong. Which RFC is HDN defined in, if any (for
# the purposes of RFC 2965)?
# For the current implementation, what about IPv6? Remember to look
# at other uses of IPV4_RE also, if change this.
if IPV4_RE.search(text):
if is_ip_like_hostname(text):
return False
if text == "":
return False
Expand Down Expand Up @@ -593,9 +619,7 @@ def liberal_is_HDN(text):
For accepting/blocking domains.

"""
if IPV4_RE.search(text):
return False
return True
return not is_ip_like_hostname(text)

def user_domain_match(A, B):
"""For blocking/accepting domains.
Expand Down Expand Up @@ -641,7 +665,10 @@ def eff_request_host(request):

"""
erhn = req_host = request_host(request)
if "." not in req_host:
if "." not in req_host and not _is_ipv6_hostname(req_host):
# Avoid adding .local at the end of an IPv6 address.
# See RFC 2965 [1] for the rationale of ".local".
# [1]: https://www.rfc-editor.org/rfc/rfc2965
erhn = req_host + ".local"
return req_host, erhn

Expand Down
59 changes: 57 additions & 2 deletions Lib/test/test_http_cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
CookieJar, DefaultCookiePolicy, LWPCookieJar, MozillaCookieJar,
LoadError, lwp_cookie_str, DEFAULT_HTTP_PORT, escape_path,
reach, is_HDN, domain_match, user_domain_match, request_path,
request_port, request_host)
request_port, request_host, is_ip_like_hostname)

mswindows = (sys.platform == "win32")

Expand Down Expand Up @@ -860,12 +860,25 @@ def test_is_HDN(self):
self.assertTrue(is_HDN("foo.bar.com"))
self.assertTrue(is_HDN("1foo2.3bar4.5com"))
self.assertFalse(is_HDN("192.168.1.1"))
self.assertFalse(is_HDN("[::1]"))
self.assertFalse(is_HDN("[2001:db8:85a3::8a2e:370:7334]"))
self.assertFalse(is_HDN(""))
self.assertFalse(is_HDN("."))
self.assertFalse(is_HDN(".foo.bar.com"))
self.assertFalse(is_HDN("..foo"))
self.assertFalse(is_HDN("foo."))

def test_is_ip_like_hostname(self):
self.assertTrue(is_ip_like_hostname('[::1]'))
self.assertTrue(is_ip_like_hostname('[2001:db8:85a3::8a2e:370:7334]'))
self.assertTrue(is_ip_like_hostname('192.168.0.1'))

self.assertFalse(is_ip_like_hostname('::1'))
self.assertFalse(is_ip_like_hostname('256.256.256.256'))
self.assertFalse(is_ip_like_hostname('[::2001:db8:85a3::]'))
self.assertFalse(is_ip_like_hostname('acme.com'))
self.assertFalse(is_ip_like_hostname(''))

def test_reach(self):
self.assertEqual(reach("www.acme.com"), ".acme.com")
self.assertEqual(reach("acme.com"), "acme.com")
Expand All @@ -875,9 +888,16 @@ def test_reach(self):
self.assertEqual(reach("."), ".")
self.assertEqual(reach(""), "")
self.assertEqual(reach("192.168.0.1"), "192.168.0.1")
self.assertEqual(reach("[::1]"), "[::1]")
self.assertEqual(reach("[2001:db8:85a3::8a2e:370:7334]"),
"[2001:db8:85a3::8a2e:370:7334]")

def test_domain_match(self):
self.assertTrue(domain_match("192.168.1.1", "192.168.1.1"))
self.assertTrue(domain_match("[::1]", "[::1]"))
self.assertFalse(domain_match("[::1]", "::1"))
self.assertTrue(domain_match("[2001:db8:85a3::8a2e:370:7334]",
"[2001:db8:85a3::8a2e:370:7334]"))
self.assertFalse(domain_match("192.168.1.1", ".168.1.1"))
self.assertTrue(domain_match("x.y.com", "x.Y.com"))
self.assertTrue(domain_match("x.y.com", ".Y.com"))
Expand Down Expand Up @@ -905,8 +925,11 @@ def test_domain_match(self):
self.assertFalse(user_domain_match("x.y.com", ".m"))
self.assertFalse(user_domain_match("x.y.com", ""))
self.assertFalse(user_domain_match("x.y.com", "."))
self.assertTrue(user_domain_match("192.168.1.1", "192.168.1.1"))
# not both HDNs, so must string-compare equal to match
self.assertTrue(user_domain_match("192.168.1.1", "192.168.1.1"))
self.assertTrue(user_domain_match("[::1]", "[::1]"))
self.assertTrue(domain_match("2001:db8::", "2001:db8::"))
self.assertFalse(user_domain_match("[::1]", "::1"))
self.assertFalse(user_domain_match("192.168.1.1", ".168.1.1"))
self.assertFalse(user_domain_match("192.168.1.1", "."))
# empty string is a special case
Expand Down Expand Up @@ -1168,6 +1191,38 @@ def test_domain_block(self):
c.add_cookie_header(req)
self.assertFalse(req.has_header("Cookie"))

def test_block_ip_domains(self):
pol = DefaultCookiePolicy(
rfc2965=True, blocked_domains=[])
c = CookieJar(policy=pol)
headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"]

pol.set_blocked_domains(["[::1]"])
req = urllib.request.Request("http://[::1]:8080")
res = FakeResponse(headers, "http://[::1]:8080")
c.extract_cookies(res, req)
self.assertEqual(len(c), 0)

pol.set_blocked_domains(["[2001:db8:85a3::8a2e:370:7334]"])
req = urllib.request.Request("http://[2001:db8:85a3::8a2e:370:7334]:8080")
res = FakeResponse(headers, "http://[2001:db8:85a3::8a2e:370:7334]:8080")
c.extract_cookies(res, req)
self.assertEqual(len(c), 0)

pol.set_blocked_domains([""])
req = urllib.request.Request("http://[::1]:8080")
res = FakeResponse(headers, "http://[::1]:8080")
c.extract_cookies(res, req)
self.assertEqual(len(c), 1)

c.clear()

pol.set_blocked_domains(["::1"])
req = urllib.request.Request("http://[::1]:8080")
res = FakeResponse(headers, "http://[::1]:8080")
c.extract_cookies(res, req)
self.assertEqual(len(c), 1)

def test_secure(self):
for ns in True, False:
for whitespace in " ", "":
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ Dean Draayer
Fred L. Drake, Jr.
Mehdi Drissi
Derk Drukker
Weilin Du
John DuBois
Paul Dubois
Jacques Ducasse
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:mod:`http.cookiejar`: fix allowed and blocked IPv6 domains
in :class:`~http.cookiejar.DefaultCookiePolicy`.
Loading