Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion scripts/agent_loop_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@

try:
from scripts import agent_loop_report as report
from scripts import json_bounds
from scripts import agent_temporal_evidence as temporal
from scripts.e2e_generic_agent_ws import GenericFixture, GENERIC_CASES, pressure_script, mixed_note_script, encoded_csv_script
from scripts.agent_knowledge_fixture import KnowledgeFixture, CASE_IDS as KNOWLEDGE_CASES
from scripts import agent_knowledge_evidence as knowledge
except ModuleNotFoundError:
import agent_loop_report as report
import json_bounds
import agent_temporal_evidence as temporal
from e2e_generic_agent_ws import GenericFixture, GENERIC_CASES, pressure_script, mixed_note_script, encoded_csv_script
from agent_knowledge_fixture import KnowledgeFixture, CASE_IDS as KNOWLEDGE_CASES
Expand Down Expand Up @@ -439,7 +441,7 @@ def _load_driver_receipt(path: Path) -> dict[str, Any]:
except UnicodeDecodeError as exc:
raise BenchmarkError("driver result must be UTF-8 JSON") from exc
try:
value = json.loads(text)
value = json_bounds.loads(text)
except json.JSONDecodeError as exc:
raise BenchmarkError(f"driver result is not valid JSON: {exc}") from exc
except (RecursionError, ValueError) as exc:
Expand Down
7 changes: 6 additions & 1 deletion scripts/agent_loop_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
from pathlib import Path
from typing import Any, Iterable

try:
from scripts import json_bounds
except ModuleNotFoundError:
import json_bounds

SCHEMA_VERSION = 1
DEFAULT_CASE_MANIFEST = Path(__file__).with_name("agent_loop_cases.json")
CODE_MODE_TOOLS = frozenset(("execute_code_mode", "execute_effectful_code_mode", "execute_mutating_code_mode"))
Expand Down Expand Up @@ -177,7 +182,7 @@ def load_case_manifest(path: Path) -> dict[str, Any]:
except UnicodeDecodeError as exc:
raise ReportError(f"case manifest must be UTF-8: {path}") from exc
try:
value = json.loads(text)
value = json_bounds.loads(text)
except json.JSONDecodeError as exc:
raise ReportError(f"case manifest is not valid JSON: {path}:{exc.lineno}") from exc
except (RecursionError, ValueError) as exc:
Expand Down
7 changes: 6 additions & 1 deletion scripts/host_timing_dogfood.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
import sqlite3
import sys

try:
from scripts import json_bounds
except ModuleNotFoundError:
import json_bounds

MAX_ROWS = 100_000
# Read only bounded diagnostic projections from potentially larger audit rows.
# Oversized summaries remain selected events but cannot provide canonical proof.
Expand Down Expand Up @@ -73,7 +78,7 @@ def _read_bounded_json(path: Path) -> object:
if len(raw) > MAX_INPUT_JSON_BYTES:
raise ReportError("input JSON exceeds bounded size")
try:
return json.loads(raw.decode("utf-8"))
return json_bounds.loads(raw.decode("utf-8"))
except (UnicodeError, ValueError, RecursionError) as exc:
# Deeply nested JSON can exhaust the parser stack below the byte cap.
raise ReportError("invalid input JSON") from exc
Expand Down
42 changes: 42 additions & 0 deletions scripts/json_bounds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Explicit JSON parsing limits shared by offline Python tooling."""

from __future__ import annotations

import json
from typing import Any

MAX_NESTING = 256
MAX_INTEGER_DIGITS = 4300


def _bounded_integer(token: str) -> int:
if len(token.removeprefix("-")) > MAX_INTEGER_DIGITS:
raise ValueError("JSON integer exceeds bounded digits")
return int(token)


def loads(text: str) -> Any:
# Interpreter recursion guards vary. Bound JSON depth before allocating
# the decoded tree, ignoring punctuation inside escaped JSON strings.
depth = 0
in_string = False
escaped = False
for char in text:
if in_string:
if escaped:
escaped = False
elif char == "\\":
escaped = True
elif char == '"':
in_string = False
elif char == '"':
in_string = True
elif char in "[{":
depth += 1
if depth > MAX_NESTING:
raise ValueError("JSON nesting exceeds bounded depth")
elif char in "]}":
depth -= 1

# Structural and Unicode validation remain with Python's JSON decoder.
return json.loads(text, parse_int=_bounded_integer)
18 changes: 18 additions & 0 deletions scripts/tests/test_agent_loop_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pathlib import Path

from scripts import agent_loop_report as report
from scripts import json_bounds


def code_mode_composition(**overrides: object) -> dict[str, object]:
Expand Down Expand Up @@ -1395,6 +1396,23 @@ def test_manifest_loader_fail_closes_on_pathological_json(self) -> None:
with self.assertRaisesRegex(report.ReportError, "bounded JSON"):
report.load_case_manifest(huge_int)

def test_bounded_json_recognizes_structure_outside_quoted_strings(self) -> None:
literal = '"}[]{' * 100
expected = {"literal": literal, "data": {"ok": True}}
self.assertEqual(json_bounds.loads(json.dumps(expected)), expected)
with self.assertRaises(json.JSONDecodeError):
json_bounds.loads('{"value":]}')

def test_bounded_json_enforces_explicit_depth_and_integer_limits(self) -> None:
depth = json_bounds.MAX_NESTING
self.assertIsInstance(
json_bounds.loads("[" * depth + "0" + "]" * depth), list
)
with self.assertRaisesRegex(ValueError, "bounded depth"):
json_bounds.loads("[" * (depth + 1) + "0" + "]" * (depth + 1))
with self.assertRaisesRegex(ValueError, "bounded digits"):
json_bounds.loads("1" * (json_bounds.MAX_INTEGER_DIGITS + 1))

def test_manifest_requires_fields_and_stable_serialization(self) -> None:
manifest = report.load_case_manifest(report.DEFAULT_CASE_MANIFEST)
serialized_once = report._stable_json(manifest)
Expand Down
Loading