diff --git a/Lib/test/test_tomllib/test_misc.py b/Lib/test/test_tomllib/test_misc.py index abd0842d10b254..af7ab91bba7cd1 100644 --- a/Lib/test/test_tomllib/test_misc.py +++ b/Lib/test/test_tomllib/test_misc.py @@ -119,6 +119,19 @@ def test_inline_table_recursion_limit(self): 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. diff --git a/Lib/tomllib/_parser.py b/Lib/tomllib/_parser.py index 8aa01301dcea32..b89934808008ef 100644 --- a/Lib/tomllib/_parser.py +++ b/Lib/tomllib/_parser.py @@ -29,6 +29,13 @@ 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 index 00000000000000..c265b54db8bed4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst @@ -0,0 +1 @@ +In :mod:`tomllib`, the number of parts in TOML keys is now limited.