Skip to content

Commit d3584e8

Browse files
committed
Let hostname() accept a bare single-label name with the RFC 1034 dot
hostname("yu", rfc_1034=True) passes today through the maybe_simple fast path, since _simple_hostname_regex has no problem with a lone alphanumeric label. hostname("yu.", rfc_1034=True) doesn't, because that regex has no notion of a trailing dot at all, and the fallback to domain() requires at least a second label before the TLD. So the exact same name is accepted or rejected purely based on whether it carries the dot RFC 1034 is supposed to permit, which is backwards from what that flag promises. Added a small wrapper around the simple-hostname match that strips a lone trailing dot first when rfc_1034 is set, mirroring what domain() already does for its own regex. Left everything else about the simple path untouched, so this doesn't change hostname() for the vast majority of callers who never pass rfc_1034. Fixes GH-442.
1 parent 70de324 commit d3584e8

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

src/validators/hostname.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ def _simple_hostname_regex():
2929
return re.compile(r"^(?!-)[a-z0-9](?:[a-z0-9-]{0,59}[a-z0-9])?(?<!-)$", re.IGNORECASE)
3030

3131

32+
def _simple_hostname_match(value: str, rfc_1034: bool):
33+
"""Match value against the simple hostname regex.
34+
35+
rfc_1034 promises an optional trailing dot is allowed, but the simple
36+
regex above has no notion of one, so a bare single-label name like
37+
"yu" would pass without the dot and fail with it. Strip a lone
38+
trailing dot first when that flag is set, same as domain() already
39+
does for its own regex, so the two paths agree.
40+
"""
41+
if rfc_1034 and value.endswith(".") and len(value) > 1:
42+
value = value[:-1]
43+
return _simple_hostname_regex().match(value)
44+
45+
3246
def _port_validator(value: str):
3347
"""Returns host segment if port is valid."""
3448
if value.count("]:") == 1:
@@ -115,14 +129,14 @@ def hostname(
115129

116130
if may_have_port and (host_seg := _port_validator(value)):
117131
return (
118-
(_simple_hostname_regex().match(host_seg) if maybe_simple else False)
132+
(_simple_hostname_match(host_seg, rfc_1034) if maybe_simple else False)
119133
or domain(host_seg, consider_tld=consider_tld, rfc_1034=rfc_1034, rfc_2782=rfc_2782)
120134
or (False if skip_ipv4_addr else ipv4(host_seg, cidr=False, private=private))
121135
or (False if skip_ipv6_addr else ipv6(host_seg, cidr=False))
122136
)
123137

124138
return (
125-
(_simple_hostname_regex().match(value) if maybe_simple else False)
139+
(_simple_hostname_match(value, rfc_1034) if maybe_simple else False)
126140
or domain(value, consider_tld=consider_tld, rfc_1034=rfc_1034, rfc_2782=rfc_2782)
127141
or (False if skip_ipv4_addr else ipv4(value, cidr=False, private=private))
128142
or (False if skip_ipv6_addr else ipv6(value, cidr=False))

tests/test_hostname.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
("[dead:beef:0:0:0:0000:42:1]:5731", False, False),
3131
("[0:0:0:0:0:ffff:1.2.3.4]:80", False, False),
3232
("[0:a:b:c:d:e:f::]:53", False, False),
33+
# bare single-label name with the RFC 1034 trailing dot, GH-442
34+
("yu.", True, False),
35+
("yu.:443", True, False),
3336
],
3437
)
3538
def test_returns_true_on_valid_hostname(value: str, rfc_1034: bool, rfc_2782: bool):
@@ -60,6 +63,10 @@ def test_returns_true_on_valid_hostname(value: str, rfc_1034: bool, rfc_2782: bo
6063
("[dead:beef:0:-:0:-:42:1]:5731", False, False),
6164
("[0:0:0:0:0:ffff:1.2.3.4]:-65538", False, False),
6265
("[0:&:b:c:@:e:f:::9999", False, False),
66+
# bad (trailing dot only allowed when rfc_1034 is requested)
67+
("yu.", False, False),
68+
# bad (a lone dot has no label to strip down to)
69+
(".", True, False),
6370
],
6471
)
6572
def test_returns_failed_validation_on_invalid_hostname(value: str, rfc_1034: bool, rfc_2782: bool):

0 commit comments

Comments
 (0)