From 441c76f7af355c8f9454952f0268abf84be4c165 Mon Sep 17 00:00:00 2001 From: tofetpuzo Date: Sat, 7 Mar 2026 22:21:54 +0000 Subject: [PATCH] feat(uri): add telnet URI validation per RFC 4248 --- src/validators/uri.py | 49 ++++++++++++++++++++++++++++++++++++++++++- tests/test_uri.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/test_uri.py diff --git a/src/validators/uri.py b/src/validators/uri.py index 84b534e..5b3b35f 100644 --- a/src/validators/uri.py +++ b/src/validators/uri.py @@ -3,8 +3,12 @@ # Read: https://stackoverflow.com/questions/176264 # https://www.rfc-editor.org/rfc/rfc3986#section-3 +# standard +from urllib.parse import urlsplit + # local from .email import email +from .hostname import hostname from .url import url from .utils import validator @@ -21,6 +25,46 @@ def _ipfs_url(value: str): return True +def _telnet_url(value: str): + """Validate a telnet URL per RFC 4248. + + Ref: https://www.rfc-editor.org/rfc/rfc4248" + Examples: + >>> _telnet_url('telnet://example.com') + True + >>> _telnet_url('telnet://example.com:23') + True + >>> _telnet_url('telnet://example.com:23/') + True + + Args: + value: + Telnet URL to validate. + + Returns: + (Literal[True]): If `value` is a Telnet URI. + (ValidationError): If `value` is an invalid URI. + """ + try: + scheme, netloc, path, query, fragment = urlsplit(value) + except ValueError: + return False + + if scheme != "telnet": + return False + if not netloc: + return False + if query or fragment: + return False + if path and path != "/": + return False + + # handling any additional checks user/pass + if "@" in netloc: + netloc = netloc.rsplit("@", 1)[1] + return hostname(netloc, may_have_port=True) + + @validator def uri(value: str, /): """Return whether or not given value is a valid URI. @@ -60,7 +104,6 @@ def uri(value: str, /): "rtsp", "sftp", "ssh", - "telnet", } # fmt: on ): @@ -98,4 +141,8 @@ def uri(value: str, /): if value.startswith("urc:"): return True + # telnet + if value.startswith("telnet:"): + return _telnet_url(value) + return False diff --git a/tests/test_uri.py b/tests/test_uri.py new file mode 100644 index 0000000..da5a8bb --- /dev/null +++ b/tests/test_uri.py @@ -0,0 +1,39 @@ +"""Test URI.""" + +# external +import pytest + +# local +from validators.uri import uri + + +@pytest.mark.parametrize( + "value", + [ + "telnet://example.com", + "telnet://example.com/", + "telnet://example.com:23", + "telnet://example.com:23/", + "telnet://192.168.1.1", + "telnet://192.168.1.1:23", + "telnet://user:password@example.com", + "telnet://user:password@example.com:23/", + ], +) +def test_valid_telnet_uri(value: str): + """Test valid telnet URI.""" + assert uri(value) + + +@pytest.mark.parametrize( + "value", + [ + "telnet://", + "telnet://example.com/path", + "telnet://example.com?query=1", + "telnet://example.com#frag", + ], +) +def test_invalid_telnet_uri(value: str): + """Test invalid telnet URI.""" + assert not uri(value)