Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions tests/fuzz_idna_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@ def fuzz_decode(fdp):
return
# RFC 5891 §5.3: an A-label that decodes must re-encode to itself, so
# under strict non-UTS46 processing any ASCII input that decodes is (up
# to case) its own encoding. decode() does not enforce the 63-octet
# label limit (nor does UTS #46 ToUnicode) but encode() does, so skip
# overlong labels.
# to case) its own encoding. decode()'s length checks are deliberately
# lenient (no 63-octet label limit, and the whole-domain limit allows
# the trailing-dot octet whether or not one is present, as UTS #46
# ToUnicode checks no lengths at all) while encode() enforces both
# exactly, so skip inputs past either limit.
if isinstance(data, str):
if not data.isascii():
return
data = data.encode("ascii")
if all(len(label) <= 63 for label in data.split(b".")):
if idna.valid_string_length(data, data.endswith(b".")) and all(len(label) <= 63 for label in data.split(b".")):
assert idna.encode(decoded, strict=True) == data.lower(), (data, decoded)


Expand Down
12 changes: 12 additions & 0 deletions tests/test_idna.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ def test_valid_label_length(self):
self.assertFalse(idna.valid_label_length("a" * 64))
self.assertRaises(idna.IDNAError, idna.encode, "a" * 64)

def test_decode_domain_length_leniency(self):
# UTS #46 ToUnicode checks no lengths, so decode()'s whole-domain
# bound is a lenient pre-filter that always allows the trailing-dot
# octet, while encode() enforces the exact RFC 1035 limit: a
# 254-octet domain without a trailing dot decodes but does not
# re-encode (OSS-Fuzz 4694619275984896), and with one it round-trips.
domain = b"aaa." * 63 + b"aa" # 254 octets, every label within limits
decoded = idna.decode(domain, strict=True)
self.assertRaises(idna.IDNAError, idna.encode, decoded, strict=True)
dotted = domain[:-1] + b"."
self.assertEqual(idna.encode(idna.decode(dotted, strict=True), strict=True), dotted)

def test_oversized_input_rejected_promptly(self):
# GHSA-65pc-fj4g-8rjx: encode/decode must reject inputs that
# exceed the maximum DNS domain length before per-codepoint
Expand Down
1 change: 1 addition & 0 deletions tests/test_idna_fuzz_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
b"xn---bbk.example", # non-canonical spelling of xn--bbk
b"XN--MNCHEN-3YA.example",
b"a." * 200, # every label valid, but past the 253-octet domain limit
b"aaa." * 63 + b"aa", # 254 octets, no trailing dot: decodes, too long to encode
("\ufdfa" * 100).encode(), # UTS #46 maps each to 18 characters, past the input cap
]

Expand Down