Skip to content

Commit 86edfef

Browse files
committed
gh-153795: Stop urllib.robotparser.can_fetch crashing on a malformed URL
RobotFileParser.can_fetch() is documented to return a bool, but it passed the url straight to urllib.parse._urlsplit(), so a malformed url such as an unterminated IPv6 authority raised a bare ValueError out of can_fetch(). Treat a url that cannot be parsed as matching no rule and return True, matching RFC 9309's default that access is not restricted when no rule applies.
1 parent e51b616 commit 86edfef

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

Lib/test/test_robotparser.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,19 @@ class DisallowQueryStringTest(BaseRobotTest, unittest.TestCase):
483483
'/yet/one/path?name=value&more']
484484

485485

486+
class CanFetchMalformedURLTest(unittest.TestCase):
487+
def test_malformed_url_is_allowed(self):
488+
# A URL that cannot be parsed matches no rule, so can_fetch() returns
489+
# True (RFC 9309 default) rather than raising.
490+
parser = urllib.robotparser.RobotFileParser()
491+
parser.parse(['User-agent: *', 'Disallow: /'])
492+
# The rule above disallows a well-formed path.
493+
self.assertFalse(parser.can_fetch('*', '/x'))
494+
# An unparsable URL is allowed, not raised.
495+
self.assertTrue(parser.can_fetch('*', 'http://[::1'))
496+
self.assertTrue(parser.can_fetch('*', 'http://['))
497+
498+
486499
class PercentEncodingTest(BaseRobotTest, unittest.TestCase):
487500
robots_txt = """\
488501
User-agent: *

Lib/urllib/robotparser.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,11 @@ def can_fetch(self, useragent, url):
178178
return False
179179
# TODO: The private API is used in order to preserve an empty query.
180180
# This is temporary until the public API starts supporting this feature.
181-
parsed_url = urllib.parse._urlsplit(url, '')
181+
try:
182+
parsed_url = urllib.parse._urlsplit(url, '')
183+
except ValueError:
184+
# A malformed URL matches no rule.
185+
return True
182186
url = urllib.parse._urlunsplit(None, None, *parsed_url[2:])
183187
url = normalize_uri(url)
184188
if not url:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:meth:`urllib.robotparser.RobotFileParser.can_fetch` no longer raises
2+
:exc:`ValueError` for a malformed URL. Patch by tonghuaroot.

0 commit comments

Comments
 (0)