From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:48:24 +0000 (-0700) Subject: Raise BadTTL for non-decimal Unicode digits in dns.ttl.from_text(). (#1279) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7cc53f6b1043b671ee4873e65f17244f9df403c9;p=thirdparty%2Fdnspython.git Raise BadTTL for non-decimal Unicode digits in dns.ttl.from_text(). (#1279) from_text() gated its integer parsing on str.isdigit(), then handed the text to int(). str.isdigit() is True for Unicode "digit" characters whose category is No (e.g. the superscript "\u00b2" or the Ethiopic "\u1369"), but int() only accepts decimal digits, so such input escaped the parser's validation and raised a bare ValueError instead of the documented dns.ttl.BadTTL. This was reachable through the public zone-file parser: a resource-record TTL or a $TTL directive containing such a character crashed dns.zone.from_text() with a ValueError rather than a clean dns.exception.SyntaxError. Switch both isdigit() checks to isdecimal(). For a single character, int()-acceptance is exactly equivalent to isdecimal(), and isdecimal() is a strict subset of isdigit(), so every previously valid TTL still parses while the non-decimal digits are now rejected as BadTTL. Add a regression test covering the fast (all-digit) path and the BIND-style units path. --- diff --git a/dns/ttl.py b/dns/ttl.py index 11f6015b..801fb946 100644 --- a/dns/ttl.py +++ b/dns/ttl.py @@ -42,7 +42,7 @@ def from_text(text: str) -> int: :rtype: int """ - if text.isdigit(): + if text.isdecimal(): total = int(text) elif len(text) == 0: raise BadTTL @@ -51,7 +51,7 @@ def from_text(text: str) -> int: current = 0 need_digit = True for c in text: - if c.isdigit(): + if c.isdecimal(): current *= 10 current += int(c) need_digit = False diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 76dc0710..40a45a48 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -19,6 +19,12 @@ TBD is the bit position, and dns.flags.from_text() / edns_from_text() parse that form back, so the conversions round-trip. See issue #1264. +* dns.ttl.from_text() now raises dns.ttl.BadTTL, rather than leaking a bare + ValueError, when the text contains a non-decimal Unicode "digit" (e.g. the + superscript ``\u00b2``). Such characters are accepted by ``str.isdigit()`` but + rejected by ``int()``, so they previously escaped the parser's validation. This + also makes zone files with such a TTL fail with a clean dns.exception.SyntaxError. + 2.8.0 ----- diff --git a/tests/test_ttl.py b/tests/test_ttl.py index 4d17628d..32537dee 100644 --- a/tests/test_ttl.py +++ b/tests/test_ttl.py @@ -42,3 +42,12 @@ class TTLTestCase(unittest.TestCase): def test_empty(self): with self.assertRaises(dns.ttl.BadTTL): dns.ttl.from_text("") + + def test_non_decimal_unicode_digits(self): + # str.isdigit() is True for Unicode "digit" characters (e.g. the + # superscript "\u00b2" or the Ethiopic "\u1369") that int() cannot + # convert, so these must be rejected as a BadTTL rather than leaking a + # bare ValueError. + for text in ("\u00b2", "1\u00b2", "\u00b2w", "1\u00b2s", "\u1369"): + with self.assertRaises(dns.ttl.BadTTL): + dns.ttl.from_text(text)