]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149231: tomllib: Limit the number of parts in a key (GH-149233)
authorPetr Viktorin <encukou@gmail.com>
Mon, 4 May 2026 11:49:07 +0000 (13:49 +0200)
committerGitHub <noreply@github.com>
Mon, 4 May 2026 11:49:07 +0000 (13:49 +0200)
Co-authored-by: Stan Ulbrych <stan@python.org>
Lib/test/test_tomllib/test_misc.py
Lib/tomllib/_parser.py
Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst [new file with mode: 0644]

index abd0842d10b254f7197a89c798a1b84470125c07..af7ab91bba7cd1f81ad086c589bf541ed13bae68 100644 (file)
@@ -119,6 +119,19 @@ class TestMiscellaneous(unittest.TestCase):
                 recursive_table_toml = nest_count * "key = {" + nest_count * "}"
                 tomllib.loads(recursive_table_toml)
 
+    def test_key_recursion_limit(self):
+        nest_count = tomllib._parser.MAX_KEY_PARTS - 2
+        nested_key_toml = "a." * nest_count + "a = 1"
+        tomllib.loads(nested_key_toml)
+
+        nest_count = tomllib._parser.MAX_KEY_PARTS + 2
+        nested_key_toml = "a." * nest_count + "a = 1"
+        with self.assertRaisesRegex(
+            RecursionError,
+            r"TOML key has more than the allowed [0-9]+ parts",
+        ):
+            tomllib.loads(nested_key_toml)
+
     def test_types_import(self):
         """Test that `_types` module runs.
 
index 8aa01301dcea321f058787952ef5035214cef03a..b89934808008efc56a410967293236deaa6b1e1e 100644 (file)
@@ -29,6 +29,13 @@ if TYPE_CHECKING:
 
     from ._types import Key, ParseFloat, Pos
 
+# Pathologically excessive number of parts in a key runs into quadratic
+# behavior (e.g. in Flags.is_).
+# Even if keys aren't currently parsed using recursion, they name a
+# recursive structure, so it makes sense to limit it using getrecursionlimit()
+# and RecursionError.
+MAX_KEY_PARTS: Final = sys.getrecursionlimit()
+
 ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
 
 # Neither of these sets include quotation mark or backslash. They are
@@ -470,6 +477,10 @@ def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:
         pos = skip_chars(src, pos, TOML_WS)
         pos, key_part = parse_key_part(src, pos)
         key += (key_part,)
+        if len(key) > MAX_KEY_PARTS:
+            raise RecursionError(
+                f"TOML key has more than the allowed {MAX_KEY_PARTS} parts"
+            )
         pos = skip_chars(src, pos, TOML_WS)
 
 
diff --git a/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst b/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst
new file mode 100644 (file)
index 0000000..c265b54
--- /dev/null
@@ -0,0 +1 @@
+In :mod:`tomllib`, the number of parts in TOML keys is now limited.