Skip to content

Commit a0bf98c

Browse files
Merge pull request #478 from Aditya8369/471
fix: Depth validation uses recursion, enabling CPU/resource exhaustion
2 parents d655751 + a7bf364 commit a0bf98c

2 files changed

Lines changed: 36 additions & 15 deletions

File tree

‎backend/security_parsers.py‎

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,33 @@ def safe_get_json(
145145

146146
def _validate_depth(obj: Any, current_depth: int = 0, max_depth: int = MAX_NESTED_DEPTH) -> bool:
147147
"""
148-
Recursively validate JSON nesting depth to prevent stack overflow attacks.
148+
Iteratively validate JSON nesting depth to prevent stack overflow attacks.
149149
150150
Args:
151151
obj: Object to validate
152-
current_depth: Current recursion depth (internal use)
152+
current_depth: Starting depth for the provided object (internal use)
153153
max_depth: Maximum allowed depth
154154
155155
Returns:
156156
bool: True if depth is within limits
157157
"""
158-
if current_depth > max_depth:
159-
return False
160-
161-
if isinstance(obj, dict):
162-
for value in obj.values():
163-
if not _validate_depth(value, current_depth + 1, max_depth):
164-
return False
165-
elif isinstance(obj, (list, tuple)):
166-
for item in obj:
167-
if not _validate_depth(item, current_depth + 1, max_depth):
168-
return False
169-
158+
stack = [(obj, current_depth)]
159+
160+
while stack:
161+
value, depth = stack.pop()
162+
163+
if depth > max_depth:
164+
return False
165+
166+
if isinstance(value, dict):
167+
next_depth = depth + 1
168+
for child in value.values():
169+
stack.append((child, next_depth))
170+
elif isinstance(value, (list, tuple)):
171+
next_depth = depth + 1
172+
for child in value:
173+
stack.append((child, next_depth))
174+
170175
return True
171176

172177

‎tests/test_security.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"""
77
import pytest
88
import json
9+
import sys
910
from types import SimpleNamespace
1011
from unittest.mock import patch, MagicMock
1112

1213
# Import security modules
1314
from backend.security_parsers import (
14-
safe_get_json, get_request_arg_safe, validate_content_type,
15+
safe_get_json, get_request_arg_safe, validate_content_type, _validate_depth,
1516
JSONParseError, MAX_JSON_SIZE_BYTES
1617
)
1718
from backend.sanitizer import (
@@ -85,6 +86,21 @@ def test_non_object_root_allowed_when_explicitly_opted_out(self):
8586
assert error is None
8687
assert data == [{"id": 1}]
8788

89+
def test_validate_depth_handles_deep_payload_without_recursion_error(self):
90+
"""Iterative traversal should reject deep payloads without recursion overhead."""
91+
nested = {}
92+
current = nested
93+
for _ in range(30):
94+
current["nested"] = {}
95+
current = current["nested"]
96+
97+
old_limit = sys.getrecursionlimit()
98+
try:
99+
sys.setrecursionlimit(20)
100+
assert _validate_depth(nested, max_depth=10) is False
101+
finally:
102+
sys.setrecursionlimit(old_limit)
103+
88104

89105
class TestXSSSanitization:
90106
"""Test XSS attack prevention through sanitization."""

0 commit comments

Comments
 (0)