gh-149231: tomllib: Limit the number of parts in a key (GH-149233)

Co-authored-by: Stan Ulbrych <stan@python.org>
This commit is contained in:
Petr ViktorinandStan Ulbrych authored and GitHub committed 2026-05-04 13:49:07 +02:00
1 parent 246fe14e7c
commit bc7c102f34
3 files changed
+25

No files matched your search

+13
View 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.
+11
View 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)
@@ -0,0 +1 @@
In :mod:`tomllib`, the number of parts in TOML keys is now limited.