diff --git a/src/validators/uri.py b/src/validators/uri.py index a342749..21e5fa4 100644 --- a/src/validators/uri.py +++ b/src/validators/uri.py @@ -4,6 +4,7 @@ # https://www.rfc-editor.org/rfc/rfc3986#section-3 # standard +import re from urllib.parse import urlsplit # local @@ -88,7 +89,7 @@ def uri(value: str, /): (Literal[True]): If `value` is a valid URI. (ValidationError): If `value` is an invalid URI. """ - if not value: + if not value or re.search(r"[\x00-\x20\x7f]", value): return False # TODO: work on various validations diff --git a/tests/test_uri.py b/tests/test_uri.py index 4feb247..f33865e 100644 --- a/tests/test_uri.py +++ b/tests/test_uri.py @@ -41,3 +41,35 @@ def test_valid_telnet_uri(value: str): def test_invalid_telnet_uri(value: str): """Test invalid telnet URI.""" assert not uri(value) + + +@pytest.mark.parametrize( + "value", + [ + "file:///tmp/readme.txt", + "ipfs://QmYwAPJzv5CZsnAzt8auVZRnGiUncKEy6PBDoDeZ7hA9Rx/readme.txt", + "magnet:?xt=urn:btih:0123456789012345678901234567890123456789", + "tel:+1234567890", + "data:text/plain;base64,SGVsbG8=", + "urn:isbn:9780140328721", + "urc:example", + ], +) +@pytest.mark.parametrize("invalid_char", ["\x00", "\t", "\n", "\r", " ", "\x7f"]) +def test_rejects_raw_ascii_controls_and_spaces_in_uri(value: str, invalid_char: str): + """Test rejects raw ASCII controls and spaces in URI.""" + assert not uri(value + invalid_char) + + +@pytest.mark.parametrize( + "value", + [ + "file:///tmp/readme%20name.txt", + "ipfs://QmYwAPJzv5CZsnAzt8auVZRnGiUncKEy6PBDoDeZ7hA9Rx/readme%0A.txt", + "magnet:?xt=urn:btih:0123456789012345678901234567890123456789&dn=readme%20name", + "data:text/plain,hello%20world", + ], +) +def test_accepts_percent_encoded_characters_in_uri(value: str): + """Test accepts percent encoded characters in URI.""" + assert uri(value)