Skip to content

Commit

Permalink
00444: pythongh-103848: Adds checks to ensure that bracketed hosts fo…
Browse files Browse the repository at this point in the history
…und by urlsplit are of IPv6 or IPvFuture format (pythonGH-103849) (python#104349)

pythongh-103848: Adds checks to ensure that bracketed hosts found by urlsplit are of IPv6 or IPvFuture format (pythonGH-103849)

* Adds checks to ensure that bracketed hosts found by urlsplit are of IPv6 or IPvFuture format

Tests are adjusted because Python <3.9 don't support scoped IPv6 addresses.

(cherry picked from commit 29f348e)

Co-authored-by: JohnJamesUtley <[email protected]>
Co-authored-by: Gregory P. Smith <[email protected]>
Co-authored-by: Lumír Balhar <[email protected]>
  • Loading branch information
4 people committed Nov 14, 2024
1 parent f20031e commit ca130e5
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
26 changes: 26 additions & 0 deletions Lib/test/test_urlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,32 @@ def test_issue14072(self):
self.assertEqual(p2.scheme, 'tel')
self.assertEqual(p2.path, '+31641044153')

def test_invalid_bracketed_hosts(self):
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[192.0.2.146]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[important.com:8000]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123r.IP]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v12ae]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v.IP]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123.]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query')
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path')

def test_splitting_bracketed_hosts(self):
p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query')
self.assertEqual(p1.hostname, 'v6a.ip')
self.assertEqual(p1.username, 'user')
self.assertEqual(p1.path, '/path')
p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7]/path?query')
self.assertEqual(p2.hostname, '0439:23af:2309::fae7')
self.assertEqual(p2.username, 'user')
self.assertEqual(p2.path, '/path')
p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146]/path?query')
self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146')
self.assertEqual(p3.username, 'user')
self.assertEqual(p3.path, '/path')

def test_telurl_params(self):
p1 = urllib.parse.urlparse('tel:123-4;phone-context=+1-650-516')
self.assertEqual(p1.scheme, 'tel')
Expand Down
15 changes: 15 additions & 0 deletions Lib/urllib/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import re
import sys
import collections
import ipaddress

__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
"urlsplit", "urlunsplit", "urlencode", "parse_qs",
Expand Down Expand Up @@ -425,6 +426,17 @@ def _remove_unsafe_bytes_from_url(url):
url = url.replace(b, "")
return url

# Valid bracketed hosts are defined in
# https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/
def _check_bracketed_host(hostname):
if hostname.startswith('v'):
if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname):
raise ValueError(f"IPvFuture address is invalid")
else:
ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4
if isinstance(ip, ipaddress.IPv4Address):
raise ValueError(f"An IPv4 address cannot be in brackets")

def urlsplit(url, scheme='', allow_fragments=True):
"""Parse a URL into 5 components:
<scheme>://<netloc>/<path>?<query>#<fragment>
Expand Down Expand Up @@ -480,6 +492,9 @@ def urlsplit(url, scheme='', allow_fragments=True):
if (('[' in netloc and ']' not in netloc) or
(']' in netloc and '[' not in netloc)):
raise ValueError("Invalid IPv6 URL")
if '[' in netloc and ']' in netloc:
bracketed_host = netloc.partition('[')[2].partition(']')[0]
_check_bracketed_host(bracketed_host)
if allow_fragments and '#' in url:
url, fragment = url.split('#', 1)
if '?' in url:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add checks to ensure that ``[`` bracketed ``]`` hosts found by
:func:`urllib.parse.urlsplit` are of IPv6 or IPvFuture format.

0 comments on commit ca130e5

Please sign in to comment.