From 17d339e2f1af883dccba5ce7cb7512ebf505fbda Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:48:25 +0530 Subject: [PATCH] Reject raw ASCII controls before parsing URLs --- src/validators/url.py | 2 +- tests/test_url.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/validators/url.py b/src/validators/url.py index f8d14e66..ad42140e 100644 --- a/src/validators/url.py +++ b/src/validators/url.py @@ -231,7 +231,7 @@ def url( (Literal[True]): If `value` is a valid url. (ValidationError): If `value` is an invalid url. """ - if not value or re.search(r"\s", value): + if not value or re.search(r"[\s\x00-\x1f\x7f]", value): # url must not contain any white # spaces, they must be encoded return False diff --git a/tests/test_url.py b/tests/test_url.py index 26495842..8d5023b6 100644 --- a/tests/test_url.py +++ b/tests/test_url.py @@ -232,3 +232,28 @@ def test_returns_failed_validation_on_invalid_private_url(value: str, private: O def test_returns_string_for_valid_file_url(value: str): """Test returns true on valid file url. Ref: RFC 8089.""" assert url(value) + + +@pytest.mark.parametrize("control", [chr(code) for code in range(32)] + ["\x7f"]) +@pytest.mark.parametrize( + "template", + [ + "{control}https://example.com/", + "https://user{control}@example.com/", + "https://example.com/a{control}b", + "https://example.com/?q=a{control}b", + "https://example.com/#a{control}b", + ], +) +def test_url_rejects_raw_ascii_controls(control: str, template: str): + """Reject raw control characters before URL parsing can discard them.""" + assert isinstance(url(template.format(control=control)), ValidationError) + + +@pytest.mark.parametrize("encoded", ["%00", "%09", "%0A", "%1F", "%7F"]) +@pytest.mark.parametrize( + "template", ["https://example.com/a{encoded}b", "https://example.com/?q=a{encoded}b"] +) +def test_url_accepts_percent_encoded_controls(encoded: str, template: str): + """Keep escaped data valid while rejecting literal control characters.""" + assert url(template.format(encoded=encoded)) is True