Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit df34308

Browse filesBrowse files
Adds checks to ensure that bracketed hosts found by urlsplit are of IPv6 or IPvFuture format
1 parent a44568b commit df34308
Copy full SHA for df34308

File tree

2 files changed

+38
-3
lines changed
Filter options

2 files changed

+38
-3
lines changed

‎Lib/test/test_urlparse.py

Copy file name to clipboardExpand all lines: Lib/test/test_urlparse.py
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,29 @@ def test_issue14072(self):
10371037
self.assertEqual(p2.scheme, 'tel')
10381038
self.assertEqual(p2.path, '+31641044153')
10391039

1040+
def test_splitting_bracketed_hosts(self):
1041+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[192.0.2.146]/Path?Query')
1042+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[important.com:8000]/Path?Query')
1043+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123r.IP]/Path?Query')
1044+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v12ae]/Path?Query')
1045+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v.IP]/Path?Query')
1046+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v123.]/Path?Query')
1047+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[v]/Path?Query')
1048+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query')
1049+
self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query')
1050+
p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query')
1051+
self.assertEqual(p1.hostname, 'v6a.ip')
1052+
self.assertEqual(p1.username, 'user')
1053+
self.assertEqual(p1.path, '/path')
1054+
p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query')
1055+
self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test')
1056+
self.assertEqual(p2.username, 'user')
1057+
self.assertEqual(p2.path, '/path')
1058+
p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query')
1059+
self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test')
1060+
self.assertEqual(p3.username, 'user')
1061+
self.assertEqual(p3.path, '/path')
1062+
10401063
def test_port_casting_failure_message(self):
10411064
message = "Port could not be cast to integer value as 'oracle'"
10421065
p1 = urllib.parse.urlparse('http://Server=sde; Service=sde:oracle')

‎Lib/urllib/parse.py

Copy file name to clipboardExpand all lines: Lib/urllib/parse.py
+15-3Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import re
3434
import types
3535
import warnings
36+
import ipaddress
3637

3738
__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
3839
"urlsplit", "urlunsplit", "urlencode", "parse_qs",
@@ -199,7 +200,7 @@ def _hostinfo(self):
199200
_, _, hostinfo = netloc.rpartition('@')
200201
_, have_open_br, bracketed = hostinfo.partition('[')
201202
if have_open_br:
202-
hostname, _, port = bracketed.partition(']')
203+
hostname, _, port = bracketed.rpartition(']')
203204
_, _, port = port.partition(':')
204205
else:
205206
hostname, _, port = hostinfo.partition(':')
@@ -229,7 +230,7 @@ def _hostinfo(self):
229230
_, _, hostinfo = netloc.rpartition(b'@')
230231
_, have_open_br, bracketed = hostinfo.partition(b'[')
231232
if have_open_br:
232-
hostname, _, port = bracketed.partition(b']')
233+
hostname, _, port = bracketed.rpartition(b']')
233234
_, _, port = port.partition(b':')
234235
else:
235236
hostname, _, port = hostinfo.partition(b':')
@@ -426,6 +427,15 @@ def _checknetloc(netloc):
426427
if c in netloc2:
427428
raise ValueError("netloc '" + netloc + "' contains invalid " +
428429
"characters under NFKC normalization")
430+
431+
def _check_bracketed_host(hostname):
432+
if hostname.startswith('v'):
433+
if not re.match(r"\Av[a-fA-F0-9]+\..+\Z", hostname):
434+
raise ValueError(f"IPvFuture address is invalid")
435+
else:
436+
ip = ipaddress.ip_address(hostname) # Throws Value Error if not IPv6 or IPv4
437+
if isinstance(ip, ipaddress.IPv4Address):
438+
raise ValueError(f"An IPv4 address cannot be in brackets")
429439

430440
# typed=True avoids BytesWarnings being emitted during cache key
431441
# comparison since this API supports both bytes and str input.
@@ -466,12 +476,14 @@ def urlsplit(url, scheme='', allow_fragments=True):
466476
break
467477
else:
468478
scheme, url = url[:i].lower(), url[i+1:]
469-
470479
if url[:2] == '//':
471480
netloc, url = _splitnetloc(url, 2)
472481
if (('[' in netloc and ']' not in netloc) or
473482
(']' in netloc and '[' not in netloc)):
474483
raise ValueError("Invalid IPv6 URL")
484+
if '[' in netloc and ']' in netloc:
485+
bracketed_host = netloc.partition('[')[2].rpartition(']')[0]
486+
_check_bracketed_host(bracketed_host)
475487
if allow_fragments and '#' in url:
476488
url, fragment = url.split('#', 1)
477489
if '?' in url:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.