From 846bc860f306af6ccd359aadbb376d9d055fbe8d Mon Sep 17 00:00:00 2001 From: Pitchfork-and-Torch Date: Fri, 18 Sep 2026 02:52:42 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Reject=20out-of-range=20URL=20ports=20(must?= =?UTF-8?q?=20be=200=E2=80=9365535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit URL.from_text() and URL() accepted ports like -1 and 65536/99999 after int() conversion. Validate the TCP/UDP port range on parse and construction so callers get URLParseError/ValueError instead of serializing illegal authorities. --- src/hyperlink/_url.py | 6 ++++++ src/hyperlink/test/test_url.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/hyperlink/_url.py b/src/hyperlink/_url.py index 8797b5cc..ff69aa50 100644 --- a/src/hyperlink/_url.py +++ b/src/hyperlink/_url.py @@ -1035,6 +1035,8 @@ def __init__( ) self._fragment = _textcheck("fragment", fragment) self._port = _typecheck("port", port, int, NoneType) + if self._port is not None and not (0 <= self._port <= 65535): + raise ValueError("port must be in range 0-65535, not %r" % (self._port,)) self._rooted = _typecheck("rooted", rooted, bool) self._userinfo = _textcheck("userinfo", userinfo, "/?#@") @@ -1413,6 +1415,10 @@ def from_text(cls, text): if not port: # TODO: excessive? raise URLParseError("port must not be empty: %r" % au_text) raise URLParseError("expected integer for port, not %r" % port) + if not (0 <= port <= 65535): + raise URLParseError( + "port must be in range 0-65535, not %r" % (port,) + ) scheme = gs["scheme"] or u"" fragment = gs["fragment"] or u"" diff --git a/src/hyperlink/test/test_url.py b/src/hyperlink/test/test_url.py index 37c91726..9bcc3526 100644 --- a/src/hyperlink/test/test_url.py +++ b/src/hyperlink/test/test_url.py @@ -1493,3 +1493,25 @@ def test_idna_corners(self): assert ( URL.from_text(text).to_uri().get_decoded_url().host == "example.com" ) + + +class OutOfRangePortTests(object): + def test_from_text_rejects_out_of_range_port(self): + from hyperlink import URL, URLParseError + for bad in ("http://ex.com:-1", "http://ex.com:65536", "http://ex.com:99999"): + try: + URL.from_text(bad) + except URLParseError: + pass + else: + raise AssertionError("expected URLParseError for %r" % (bad,)) + + def test_ctor_rejects_out_of_range_port(self): + from hyperlink import URL + for bad in (-1, 65536, 99999): + try: + URL(scheme=u"http", host=u"ex.com", port=bad) + except ValueError: + pass + else: + raise AssertionError("expected ValueError for port %r" % (bad,)) From 5e791b8c6b96208587029ac93f7a5029afa4b910 Mon Sep 17 00:00:00 2001 From: Pitchfork-and-Torch Date: Fri, 18 Sep 2026 02:52:54 +0000 Subject: [PATCH 2/2] Use HyperlinkTestCase for out-of-range port tests --- src/hyperlink/test/test_url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperlink/test/test_url.py b/src/hyperlink/test/test_url.py index 9bcc3526..7ea1517c 100644 --- a/src/hyperlink/test/test_url.py +++ b/src/hyperlink/test/test_url.py @@ -1495,7 +1495,7 @@ def test_idna_corners(self): ) -class OutOfRangePortTests(object): +class OutOfRangePortTests(HyperlinkTestCase): def test_from_text_rejects_out_of_range_port(self): from hyperlink import URL, URLParseError for bad in ("http://ex.com:-1", "http://ex.com:65536", "http://ex.com:99999"):