feat: prepare pinned Custodian worker bundle and required input admission
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
This commit is contained in:
parent
0319fe8be8
commit
fed119a17c
18 changed files with 1058 additions and 2 deletions
31
tests/test_custodian_bundle.py
Normal file
31
tests/test_custodian_bundle.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import importlib.util
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
spec = importlib.util.spec_from_file_location("bundle_verifier", ROOT / "scripts/verify_custodian_bundle.py")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
|
||||
def test_reviewed_bundle_bytes():
|
||||
result = module.verify(ROOT / "k8s/custodian-release/commands", "60d893af1bf77f457504b64ff1c9ffb5c6ae6579")
|
||||
assert result["verified_files"] == 4
|
||||
|
||||
|
||||
def test_tampered_installed_file_refused(tmp_path):
|
||||
shutil.copytree(ROOT / "k8s/custodian-release/commands", tmp_path / "bundle")
|
||||
(tmp_path / "bundle/scripts/security_flow_daily.py").write_text("changed")
|
||||
with pytest.raises(ValueError, match="hash mismatch"):
|
||||
module.verify(tmp_path / "bundle")
|
||||
|
||||
|
||||
def test_installed_symlink_refused(tmp_path):
|
||||
shutil.copytree(ROOT / "k8s/custodian-release/commands", tmp_path / "bundle")
|
||||
path = tmp_path / "bundle/scripts/security_flow_daily.py"
|
||||
path.unlink()
|
||||
path.symlink_to(ROOT / "k8s/custodian-release/commands/scripts/security_flow_daily.py")
|
||||
with pytest.raises(ValueError, match="regular installed"):
|
||||
module.verify(tmp_path / "bundle")
|
||||
93
tests/test_custodian_inputs.py
Normal file
93
tests/test_custodian_inputs.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import hashlib
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from activity_core.custodian_inputs import admit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def evidence(tmp_path):
|
||||
fleet = tmp_path / "fleet"
|
||||
file = fleet / "example/workplans/task.md"
|
||||
file.parent.mkdir(parents=True)
|
||||
file.write_text("task")
|
||||
manifest = {"schema": "repo-manager.fleet-input.v1", "freshness_verified": False,
|
||||
"repositories": [{"slug": "example", "commit": "a" * 40, "files": [
|
||||
{"path": "workplans/task.md", "bytes": 4, "sha256": hashlib.sha256(b"task").hexdigest()}]}]}
|
||||
raw = json.dumps(manifest).encode()
|
||||
(fleet / "manifest.json").write_bytes(raw)
|
||||
(fleet / "COMPLETE").write_text(hashlib.sha256(raw).hexdigest())
|
||||
policy = tmp_path / "policy.json"
|
||||
policy.write_text(json.dumps({"schema": "activity-core.custodian-input-policy.v1", "repositories": ["example"],
|
||||
"max_observation_age_seconds": 7200, "max_future_skew_seconds": 300,
|
||||
"max_snapshot_bytes": 1024, "expected_hub_instance": "railiance01"}))
|
||||
receipts = tmp_path / "receipts.json"
|
||||
receipt = {"schema": "repo-manager.freshness-receipt.v1", "repo_slug": "example", "forge_commit": "a" * 40,
|
||||
"outcome": "noop", "reason": "forge_head_matches_last_success", "finished_at": "2026-10-19T10:00:00Z",
|
||||
"hub": {"verified_primary": True, "instance_role": "primary", "instance_label": "railiance01"}}
|
||||
prior = {**receipt, "outcome": "applied", "reason": None,
|
||||
"finished_at": "2026-10-01T00:00:00Z",
|
||||
**{key: "a" * 40 for key in ("expected_commit", "derived_commit", "projected_commit")}}
|
||||
receipts.write_text(json.dumps([receipt, prior]))
|
||||
return fleet, policy, receipts
|
||||
|
||||
|
||||
def check(evidence):
|
||||
return admit(*evidence, ["example"], now=datetime(2026, 10, 19, 11, tzinfo=UTC))
|
||||
|
||||
|
||||
def test_accepts_independent_recent_receipt(evidence):
|
||||
assert len(check(evidence)) == 64
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", ["missing", "stale", "future", "wrong_commit", "wrong_hub", "failed"])
|
||||
def test_rejects_invalid_freshness(evidence, case):
|
||||
path = evidence[2]
|
||||
rows = json.loads(path.read_text())
|
||||
if case == "missing":
|
||||
rows = []
|
||||
elif case == "stale":
|
||||
rows[0]["finished_at"] = "2026-10-18T10:00:00Z"
|
||||
elif case == "future":
|
||||
rows[0]["finished_at"] = "2026-10-20T10:00:00Z"
|
||||
elif case == "wrong_commit":
|
||||
rows[0]["forge_commit"] = "b" * 40
|
||||
elif case == "wrong_hub":
|
||||
rows[0]["hub"]["instance_label"] = "workstation"
|
||||
else:
|
||||
rows[0]["outcome"] = "failed"
|
||||
path.write_text(json.dumps(rows))
|
||||
with pytest.raises(ValueError, match="fresh Forge receipt"):
|
||||
check(evidence)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", ["tamper", "extra", "marker", "extra_repo"])
|
||||
def test_rejects_changed_snapshot(evidence, case):
|
||||
fleet = evidence[0]
|
||||
if case == "tamper":
|
||||
(fleet / "example/workplans/task.md").write_text("changed")
|
||||
elif case == "extra":
|
||||
(fleet / "example/workplans/extra.md").write_text("extra")
|
||||
elif case == "extra_repo":
|
||||
(fleet / "outside").mkdir()
|
||||
else:
|
||||
(fleet / "COMPLETE").write_text("incorrect")
|
||||
with pytest.raises(ValueError):
|
||||
check(evidence)
|
||||
|
||||
|
||||
def test_native_noop_uses_protected_issuer_contract(evidence):
|
||||
path = evidence[2]
|
||||
path.write_text(json.dumps(json.loads(path.read_text())[:1]))
|
||||
assert len(check(evidence)) == 64
|
||||
|
||||
|
||||
def test_fifo_refused_without_open(evidence):
|
||||
import os
|
||||
path = evidence[2]
|
||||
path.unlink()
|
||||
os.mkfifo(path)
|
||||
with pytest.raises(ValueError, match="unsafe"):
|
||||
check(evidence)
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import json
|
||||
import hashlib
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
|
|
@ -21,6 +23,26 @@ def runtime(tmp_path, monkeypatch):
|
|||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_HUB", "http://127.0.0.1:8000")
|
||||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_REPOS", "the-custodian")
|
||||
(fleet / "the-custodian/workplans").mkdir(parents=True)
|
||||
(fleet / "the-custodian/workplans/task.md").write_text("task")
|
||||
manifest = {"schema": "repo-manager.fleet-input.v1", "repositories": [{
|
||||
"slug": "the-custodian", "commit": "a" * 40, "files": [{"path": "workplans/task.md",
|
||||
"bytes": 4, "sha256": hashlib.sha256(b"task").hexdigest()}]}]}
|
||||
raw = json.dumps(manifest).encode()
|
||||
(fleet / "manifest.json").write_bytes(raw)
|
||||
(fleet / "COMPLETE").write_text(hashlib.sha256(raw).hexdigest())
|
||||
policy = state / "policy.json"
|
||||
policy.write_text(json.dumps({"schema": "activity-core.custodian-input-policy.v1",
|
||||
"repositories": ["the-custodian"], "max_observation_age_seconds": 7200,
|
||||
"max_future_skew_seconds": 300, "max_snapshot_bytes": 1024,
|
||||
"expected_hub_instance": "railiance01"}))
|
||||
receipts = state / "receipts.json"
|
||||
receipts.write_text(json.dumps([{"schema": "repo-manager.freshness-receipt.v1",
|
||||
"repo_slug": "the-custodian", "outcome": "applied",
|
||||
"finished_at": datetime.now(timezone.utc).isoformat(),
|
||||
"hub": {"verified_primary": True, "instance_role": "primary", "instance_label": "railiance01"},
|
||||
**{key: "a" * 40 for key in ("forge_commit", "expected_commit", "derived_commit", "projected_commit")}}]))
|
||||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_INPUT_POLICY", str(policy))
|
||||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_FRESHNESS_RECEIPTS", str(receipts))
|
||||
for name in ("security_flow_daily.py", "weekly_allocation_report.py"):
|
||||
(root / "scripts" / name).write_text("# owner fixture\n")
|
||||
return root, fleet, state
|
||||
|
|
@ -168,3 +190,23 @@ def test_subprocess_timeout_kills_group():
|
|||
with pytest.raises(RuntimeError, match='timed out'):
|
||||
runner._execute(['fixed-owner-command'], Path('/tmp'), 1)
|
||||
kill.assert_called_once_with(12345, runner.signal.SIGKILL)
|
||||
|
||||
|
||||
def test_configured_freshness_admission_refuses_before_subprocess(runtime, monkeypatch):
|
||||
root, fleet, state = runtime
|
||||
policy = state / "policy.json"
|
||||
receipts = state / "receipts.json"
|
||||
policy.write_text("{}")
|
||||
receipts.write_text("[]")
|
||||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_INPUT_POLICY", str(policy))
|
||||
monkeypatch.setenv("ACTIVITY_CORE_CUSTODIAN_FRESHNESS_RECEIPTS", str(receipts))
|
||||
with patch.object(runner, "_execute") as execute, pytest.raises(ValueError):
|
||||
runner.run_custodian_operation("custodian_security_flow", {"apply": True})
|
||||
execute.assert_not_called()
|
||||
|
||||
|
||||
def test_missing_policy_refuses_before_subprocess(runtime, monkeypatch):
|
||||
monkeypatch.delenv("ACTIVITY_CORE_CUSTODIAN_INPUT_POLICY")
|
||||
with patch.object(runner, "_execute") as execute, pytest.raises(ValueError):
|
||||
runner.run_custodian_operation("custodian_security_flow", {"apply": True})
|
||||
execute.assert_not_called()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue