From abbc073429900bdf91bf717fcef7adc7ec81f403 Mon Sep 17 00:00:00 2001 From: red person Date: Tue, 2 Jun 2026 21:59:05 +0300 Subject: [PATCH] Reject invalid preset CLI stores (#1395) --- scripts/odysseus-preset | 5 ++++- tests/test_preset_cli_store.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/test_preset_cli_store.py diff --git a/scripts/odysseus-preset b/scripts/odysseus-preset index f13ccd7..ce08f6d 100755 --- a/scripts/odysseus-preset +++ b/scripts/odysseus-preset @@ -28,9 +28,12 @@ def _load() -> dict: if not _PATH.exists(): return {} try: - return json.loads(_PATH.read_text()) + data = json.loads(_PATH.read_text()) except json.JSONDecodeError as e: fail(f"presets.json corrupt: {e}") + if not isinstance(data, dict): + fail("presets.json corrupt: expected an object") + return data def _save(data: dict) -> None: diff --git a/tests/test_preset_cli_store.py b/tests/test_preset_cli_store.py new file mode 100644 index 0000000..c9cc0bb --- /dev/null +++ b/tests/test_preset_cli_store.py @@ -0,0 +1,28 @@ +import importlib.machinery +import importlib.util +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).resolve().parents[1] + + +def _load_cli(): + path = ROOT / "scripts" / "odysseus-preset" + loader = importlib.machinery.SourceFileLoader("odysseus_preset_cli", str(path)) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return module + + +def test_load_rejects_non_object_preset_store(tmp_path, capsys): + cli = _load_cli() + cli._PATH = tmp_path / "presets.json" + cli._PATH.write_text("[]") + + with pytest.raises(SystemExit): + cli._load() + + assert "expected an object" in capsys.readouterr().err