Skip to content

Commit 743482a

Browse files
[3.13] gh-149231: tomllib: Limit the number of parts in a key (GH-149233) (GH-149815) (#149848)
[3.14] gh-149231: tomllib: Limit the number of parts in a key (GH-149233) (GH-149815) (cherry picked from commit bc7c102) (cherry picked from commit 724a5e5) Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 310125e commit 743482a

3 files changed

Lines changed: 27 additions & 1 deletion

File tree

Lib/test/test_tomllib/test_misc.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,16 @@ def test_inline_table_recursion_limit(self):
113113
nest_count=nest_count):
114114
recursive_table_toml = nest_count * "key = {" + nest_count * "}"
115115
tomllib.loads(recursive_table_toml)
116+
117+
def test_key_recursion_limit(self):
118+
nest_count = tomllib._parser.MAX_KEY_PARTS - 2
119+
nested_key_toml = "a." * nest_count + "a = 1"
120+
tomllib.loads(nested_key_toml)
121+
122+
nest_count = tomllib._parser.MAX_KEY_PARTS + 2
123+
nested_key_toml = "a." * nest_count + "a = 1"
124+
with self.assertRaisesRegex(
125+
RecursionError,
126+
r"TOML key has more than the allowed [0-9]+ parts",
127+
):
128+
tomllib.loads(nested_key_toml)

Lib/tomllib/_parser.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
from collections.abc import Iterable
88
import string
9+
import sys
910
from types import MappingProxyType
10-
from typing import Any, BinaryIO, NamedTuple
11+
from typing import Any, BinaryIO, NamedTuple, Final
1112

1213
from ._re import (
1314
RE_DATETIME,
@@ -19,6 +20,13 @@
1920
)
2021
from ._types import Key, ParseFloat, Pos
2122

23+
# Pathologically excessive number of parts in a key runs into quadratic
24+
# behavior (e.g. in Flags.is_).
25+
# Even if keys aren't currently parsed using recursion, they name a
26+
# recursive structure, so it makes sense to limit it using getrecursionlimit()
27+
# and RecursionError.
28+
MAX_KEY_PARTS: Final = sys.getrecursionlimit()
29+
2230
ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
2331

2432
# Neither of these sets include quotation mark or backslash. They are
@@ -385,6 +393,10 @@ def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:
385393
pos = skip_chars(src, pos, TOML_WS)
386394
pos, key_part = parse_key_part(src, pos)
387395
key += (key_part,)
396+
if len(key) > MAX_KEY_PARTS:
397+
raise RecursionError(
398+
f"TOML key has more than the allowed {MAX_KEY_PARTS} parts"
399+
)
388400
pos = skip_chars(src, pos, TOML_WS)
389401

390402

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In :mod:`tomllib`, the number of parts in TOML keys is now limited.

0 commit comments

Comments
 (0)