diff --git a/pyproject.toml b/pyproject.toml index 17a3193..5ff31f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ description = "Secure Contain Protect — inspect, sanitize, contain, quarantine readme = "README.md" requires-python = ">=3.10" dependencies = [ - "mcp>=1.2.0", + "mcp>=1.2.0,<2", "requests>=2.28.0", ] diff --git a/requirements.txt b/requirements.txt index a5aa6e2..0c8a4f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -mcp>=1.2.0 +mcp>=1.2.0,<2 diff --git a/src/scp/registry_ssot.py b/src/scp/registry_ssot.py index 9503082..3a0fdf8 100644 --- a/src/scp/registry_ssot.py +++ b/src/scp/registry_ssot.py @@ -216,6 +216,8 @@ def _load_quarantine_snapshot(quarantine_path: str | Path) -> dict: path = Path(quarantine_path) if not _path_under_registry_fetch(path): raise ValueError("quarantine_path_rejected") + if path.suffix != ".txt": + raise ValueError("quarantine_provenance_rejected") if not path.is_file(): raise ValueError("quarantine_file_not_found") diff --git a/tests/test_registry_ssot.py b/tests/test_registry_ssot.py index 02fdd62..6091294 100644 --- a/tests/test_registry_ssot.py +++ b/tests/test_registry_ssot.py @@ -235,3 +235,26 @@ def test_apply_merge_rejects_wrong_sidecar_reason(isolated_ssot): res = registry_ssot.apply_merge(path, approve=True) assert res["merged"] is False assert res["reason"] == "quarantine_provenance_rejected" + + +def test_apply_merge_rejects_json_payload_as_its_own_sidecar(isolated_ssot): + """A merge payload must be the .txt file paired with a separate sidecar.""" + snap = _snap([_rec("bad.selfsidecar.001")]) + forged = scp_utils.registry_fetch_quarantine_dir() / "forged.json" + forged.parent.mkdir(parents=True, exist_ok=True) + forged.write_text( + json.dumps( + { + "reason": "registry_fetch", + "snapshot": snap, + "meta": {"reason": "registry_fetch", "source": "forged"}, + } + ), + encoding="utf-8", + ) + + res = registry_ssot.apply_merge(forged, approve=True) + + assert res["merged"] is False + assert res["reason"] == "quarantine_provenance_rejected" + assert registry_ssot.load_ssot() == []