Thanks to visit codestin.com
Credit goes to github.com

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 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 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
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
38 changes: 31 additions & 7 deletions Lib/http/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,15 +532,31 @@ def parse_ns_headers(ns_headers):
return result


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

def is_ip_like(text: str):
"""Return True if text is a valid hostname in the form of IP address."""
from ipaddress import IPv4Address, IPv6Address
# check for IPv4 address
try:
IPv4Address(text)
except ValueError:
# check for IPv6 address in []
if text.startswith('[') and text.endswith(']'):
try:
IPv6Address(text.removeprefix('[').removesuffix(']'))
except ValueError:
return False
else:
return False # not a IPv6 address in []
Comment on lines +546 to +552
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are IPv6 addressed not surrounded [] rejected? when making a request, we would use http://[::1] for instance, but we could decide to reject the domain ::1 itself. Please be more careful. Instead, we should make it allow both valid IPv6 addresses as well as IPv6 addresses enclosed in '[]'. But then, we need to be careful with the matching. Should [::1] be matched against ::1 or [::1] only?

Try to look at the RFC as well to see if there are more information about IPv6 domains.

return True
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(text):
return False
if text == "":
return False
Expand Down Expand Up @@ -593,9 +609,7 @@ def liberal_is_HDN(text):
For accepting/blocking domains.

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

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

"""
erhn = req_host = request_host(request)
if "." not in req_host:
if req_host.startswith('[') and req_host.endswith(']'):
from ipaddress import IPv6Address
try:
IPv6Address(req_host.removeprefix('[').removesuffix(']'))
is_ipV6 = True
except ValueError:
is_ipV6 = False
else:
is_ipV6 = False
if "." not in req_host and not is_ipV6:
# avoid adding .local at the end of a IPV6 address
erhn = req_host + ".local"
return req_host, erhn

Expand Down
50 changes: 48 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)

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

Expand Down Expand Up @@ -860,12 +860,24 @@ 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(self):
self.assertTrue(is_ip_like('[::1]'))
self.assertTrue(is_ip_like('[2001:db8:85a3::8a2e:370:7334]'))
self.assertTrue(is_ip_like('192.168.0.1'))

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

def test_reach(self):
self.assertEqual(reach("www.acme.com"), ".acme.com")
self.assertEqual(reach("acme.com"), "acme.com")
Expand All @@ -875,9 +887,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 +924,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 +1190,30 @@ 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)

def test_secure(self):
for ns in True, False:
for whitespace in " ", "":
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