feat: select retained Custodian snapshot through pinned descriptor
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
This commit is contained in:
parent
fed119a17c
commit
3b25aea7b0
6 changed files with 150 additions and 17 deletions
|
|
@ -1,6 +1,7 @@
|
|||
"""Fail-closed admission for trusted-owner fleet snapshots and freshness receipts."""
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import stat
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path, PurePosixPath
|
||||
|
|
@ -79,3 +80,21 @@ def admit(fleet, policy_path, receipts_path, repos, now=None):
|
|||
if actual_files != expected_files or total > policy["max_snapshot_bytes"]:
|
||||
raise ValueError("snapshot content outside manifest or size policy")
|
||||
return hashlib.sha256(raw).hexdigest()
|
||||
|
||||
|
||||
def select_snapshot(snapshot_root, state_root, descriptor_path):
|
||||
"""Read one protected atomic descriptor; never follow a mutable symlink pointer."""
|
||||
root, state, path = Path(snapshot_root), Path(state_root), Path(descriptor_path)
|
||||
if not path.is_absolute() or ".." in path.parts or state not in path.parents:
|
||||
raise ValueError("snapshot descriptor outside private state")
|
||||
descriptor = json.loads(_read(path))
|
||||
snapshot_id = descriptor.get("snapshot_id", "")
|
||||
if descriptor.get("schema") != "activity-core.custodian-snapshot-selection.v1" or not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}", snapshot_id):
|
||||
raise ValueError("invalid snapshot selection")
|
||||
selected = root / snapshot_id
|
||||
manifest_hash = hashlib.sha256(_read(selected / "manifest.json")).hexdigest()
|
||||
receipts = state / "inputs" / (snapshot_id + ".receipts.json")
|
||||
receipts_hash = hashlib.sha256(_read(receipts)).hexdigest()
|
||||
if manifest_hash != descriptor.get("manifest_sha256") or receipts_hash != descriptor.get("receipts_sha256"):
|
||||
raise ValueError("snapshot selection digest mismatch")
|
||||
return selected, receipts
|
||||
|
|
|
|||
|
|
@ -84,6 +84,11 @@ def build_command(operation: str, params: dict) -> tuple[list[str], Path, Path,
|
|||
argv = [sys.executable, str(script)]
|
||||
request = None
|
||||
if operation == "custodian_security_flow":
|
||||
from activity_core.custodian_inputs import select_snapshot
|
||||
descriptor = os.environ.get("ACTIVITY_CORE_CUSTODIAN_SNAPSHOT_DESCRIPTOR")
|
||||
selected_receipts = None
|
||||
if descriptor:
|
||||
fleet, selected_receipts = select_snapshot(fleet, state, descriptor)
|
||||
repos = os.environ.get("ACTIVITY_CORE_CUSTODIAN_REPOS", "").split(",")
|
||||
if not repos or any(re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", repo) is None for repo in repos):
|
||||
raise ValueError("explicit canonical security-flow repository list required")
|
||||
|
|
@ -91,7 +96,7 @@ def build_command(operation: str, params: dict) -> tuple[list[str], Path, Path,
|
|||
_path(str(fleet / repo / "workplans"), directory=True)
|
||||
from activity_core.custodian_inputs import admit
|
||||
policy_path = _path(os.environ.get("ACTIVITY_CORE_CUSTODIAN_INPUT_POLICY"))
|
||||
receipts_path = _path(os.environ.get("ACTIVITY_CORE_CUSTODIAN_FRESHNESS_RECEIPTS"))
|
||||
receipts_path = selected_receipts or _path(os.environ.get("ACTIVITY_CORE_CUSTODIAN_FRESHNESS_RECEIPTS"))
|
||||
admit(fleet, policy_path, receipts_path, repos)
|
||||
hub = os.environ.get("ACTIVITY_CORE_CUSTODIAN_HUB", "")
|
||||
parsed = urlsplit(hub)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue