From 14f60809a06381258c084ac5f9580aef82cca485 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Tue, 30 Jun 2026 19:25:49 +0000 Subject: [PATCH] split_host_and_port: reject ports above 65535 with HTTPInputError The current regex accepts any number of digits, so 'example.com:70000' parses without complaint and surfaces as an out-of-range port deeper in the stack. Add an explicit range check that raises HTTPInputError for any port above 65535, matching what the rest of the option/address parsers in the project already do for invalid host:port inputs. --- tornado/httputil.py | 2 ++ tornado/test/httputil_test.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/tornado/httputil.py b/tornado/httputil.py index 4bd17786d..4fc12da38 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -1321,6 +1321,8 @@ def split_host_and_port(netloc: str) -> tuple[str, int | None]: if match: host = match.group(1) port: int | None = int(match.group(2)) + if port < 0 or port > 65535: + raise HTTPInputError("Invalid port number %r" % port) else: host = netloc port = None diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index 4e966eb50..a427bcc8e 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -19,6 +19,7 @@ parse_multipart_form_data, parse_request_start_line, qs_to_qsl, + split_host_and_port, url_concat, ) from tornado.log import gen_log @@ -623,6 +624,33 @@ def test_parse_request_start_line(self): self.assertEqual(parsed_start_line.version, self.VERSION) +class SplitHostAndPortTest(unittest.TestCase): + def test_host_only(self): + # No colon -> port is None and the whole netloc is the host. + self.assertEqual(split_host_and_port("example.com"), ("example.com", None)) + + def test_host_and_port(self): + self.assertEqual( + split_host_and_port("example.com:8080"), ("example.com", 8080) + ) + + def test_port_at_upper_bound(self): + # 65535 is the highest legal TCP port and must be accepted. + self.assertEqual(split_host_and_port("example.com:65535"), ("example.com", 65535)) + + def test_port_at_lower_bound(self): + # 0 is reserved but is still a valid integer in the port slot. + self.assertEqual(split_host_and_port("example.com:0"), ("example.com", 0)) + + def test_port_above_max_raises(self): + # Anything > 65535 is not a valid TCP port and must raise. + for port in (65536, 70000, 100000): + with self.subTest(port=port): + with self.assertRaises(HTTPInputError) as cm: + split_host_and_port(f"example.com:{port}") + self.assertIn(str(port), str(cm.exception)) + + class ParseCookieTest(unittest.TestCase): # These tests copied from Django: # https://github.com/django/django/pull/6277/commits/da810901ada1cae9fc1f018f879f11a7fb467b28