Skip to content

Commit 2982d93

Browse files
authored
gh-161102: Fix idna incremental decoder after leading empty labels (GH-161103)
Count the separating dot for every label after the first, so dots after leading empty labels are not decoded again by the next call. ANT-2026-KFX1PUWZ
1 parent 82d3439 commit 2982d93

3 files changed

Lines changed: 25 additions & 2 deletions

File tree

‎Lib/encodings/idna.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ def _buffer_decode(self, input, errors, final):
364364
result = []
365365
size = 0
366366
for label in labels:
367+
if result:
368+
# Join with U+002E
369+
size += 1
367370
try:
368371
u_label = ToUnicode(label)
369372
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
@@ -376,8 +379,6 @@ def _buffer_decode(self, input, errors, final):
376379
)
377380
else:
378381
result.append(u_label)
379-
if size:
380-
size += 1
381382
size += len(label)
382383

383384
result = ".".join(result) + trailing_dot

‎Lib/test/test_codecs.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,8 @@ class IDNACodecTest(unittest.TestCase):
16711671
(b"xn--pythn-&mua.org", UnicodeDecodeError("idna", b"xn--pythn-&mua.org", 10, 11, "")),
16721672
(b"xn--pythn-m&ua.org", UnicodeDecodeError("idna", b"xn--pythn-m&ua.org", 11, 12, "")),
16731673
(b"xn--pythn-mua&.org", UnicodeDecodeError("idna", b"xn--pythn-mua&.org", 13, 14, "")),
1674+
(b"bugs.xn--pythn-&mua.org", UnicodeDecodeError("idna", b"bugs.xn--pythn-&mua.org", 15, 16, "")),
1675+
(b".xn--pythn-&mua.org", UnicodeDecodeError("idna", b".xn--pythn-&mua.org", 11, 12, "")),
16741676
]
16751677
invalid_encode_testcases = [
16761678
(f"foo.{'\xff'*60}", UnicodeEncodeError("idna", f"foo.{'\xff'*60}", 4, 64, "")),
@@ -1790,6 +1792,22 @@ def test_incremental_decode(self):
17901792
self.assertEqual(decoder.decode(b"rg."), "org.")
17911793
self.assertEqual(decoder.decode(b"", True), "")
17921794

1795+
def test_incremental_decode_leading_empty_labels(self):
1796+
# The dot after an empty label at the start of the input has to be
1797+
# counted as consumed, or it is decoded again by the next call.
1798+
for data in b".python.org", b"..python.org.", b"..", b"." * 100:
1799+
with self.subTest(data=data):
1800+
decoder = codecs.getincrementaldecoder("idna")()
1801+
result = decoder.decode(data)
1802+
self.assertNotIn(b".", decoder.getstate()[0])
1803+
result += decoder.decode(b"", True)
1804+
self.assertEqual(result, data.decode("idna"))
1805+
self.assertEqual(decoder.getstate(), (b"", 0))
1806+
self.assertEqual(decoder.decode(b"", True), "")
1807+
1808+
with io.TextIOWrapper(io.BytesIO(b"." * 100), encoding="idna") as f:
1809+
self.assertEqual(f.readline(), "." * 100)
1810+
17931811
def test_incremental_decode_invalid(self):
17941812
iterdecode_testcases = [
17951813
(b"\xFFpython.org", UnicodeDecodeError("idna", b"\xFF", 0, 1, "")),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix the incremental decoder of the :mod:`idna <encodings.idna>` codec
2+
decoding some dots twice when its input starts with empty labels, for example
3+
``b".example.com"``. Reading a run of dots through :class:`io.TextIOWrapper`
4+
no longer produces quadratically more text than the input holds.

0 commit comments

Comments
 (0)