Files
aios/active-runtime/app/trusted_release_execution_plan.py
T

102 lines
7.9 KiB
Python

#!/usr/bin/env python3
"""Canonical, fail-closed projection for trusted release execution plans.
A release plan is the only authority for candidate, source, artifact, policy,
rollback, and release identity. Callers may not provide executable paths or
replace individual fields after the plan has been accepted.
"""
from __future__ import annotations
import hashlib, json, re, stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
PLAN_ID_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{2,255}$")
TOP_LEVEL_KEYS = frozenset({"schema","version","candidate_id","source_commit","tree_oid","artifact_sha256","manifest_sha256","package_closure_sha256","deployment_plan_sha256","policy_sha256","release_plan_id","approval_plan_id","evidence_closure_oid","campaign_authorization_sha256","campaign_generation","artifact","rollback","baseline","adapter_policy","release_plan_sha256"})
FORBIDDEN_KEYS = frozenset({"command","commands","executable","exec","shell","argv","adapter_command","tag_command","deploy_command","registry_command","baseline_command","sudo","force_push","direct_main_push"})
class PlanError(ValueError):
"""A release plan is malformed, inconsistent, or unsafe."""
def canonical_json(value: object) -> bytes:
return (json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8")
def _sha(value: Any, label: str) -> str:
if not isinstance(value, str) or not SHA_RE.fullmatch(value): raise PlanError(f"{label}_invalid")
return value
def _oid(value: Any, label: str) -> str:
if not isinstance(value, str) or not OID_RE.fullmatch(value): raise PlanError(f"{label}_invalid")
return value
def _walk_forbidden(value: Any) -> None:
if isinstance(value, Mapping):
for key, item in value.items():
if str(key).lower() in FORBIDDEN_KEYS: raise PlanError(f"forbidden_plan_key:{key}")
_walk_forbidden(item)
elif isinstance(value, list):
for item in value: _walk_forbidden(item)
def _exact_mapping(value: Any, required: set[str], label: str) -> Mapping[str, Any]:
if not isinstance(value, Mapping) or set(value) != required: raise PlanError(f"{label}_fields_invalid")
return value
def _secure_json(path: Path) -> dict[str, Any]:
try: st = path.lstat()
except OSError as exc: raise PlanError(f"plan_unreadable:{path}") from exc
if path.is_symlink() or not stat.S_ISREG(st.st_mode): raise PlanError("plan_must_be_regular_file")
if st.st_uid != 0 or st.st_mode & 0o022: raise PlanError("plan_ownership_or_mode_invalid")
try: value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc: raise PlanError("plan_json_invalid") from exc
if not isinstance(value, dict): raise PlanError("plan_object_required")
return value
@dataclass(frozen=True)
class TrustedReleasePlan:
value: Mapping[str, Any]
release_plan_sha256: str
@classmethod
def from_mapping(cls, raw: Mapping[str, Any]) -> "TrustedReleasePlan":
if not isinstance(raw, Mapping) or set(raw) - TOP_LEVEL_KEYS: raise PlanError("release_plan_fields_invalid")
required = TOP_LEVEL_KEYS - {"release_plan_sha256"}
if set(raw) not in (required, TOP_LEVEL_KEYS): raise PlanError("release_plan_fields_incomplete")
_walk_forbidden(raw)
if raw.get("schema") != "aios.trusted-release-execution-plan.v1": raise PlanError("release_plan_schema_invalid")
version = raw.get("version")
if not isinstance(version, str) or not re.fullmatch(r"V[0-9]+R[0-9]+C[0-9]+", version): raise PlanError("version_invalid")
if not isinstance(raw.get("candidate_id"), str) or not IDENTIFIER_RE.fullmatch(raw["candidate_id"]): raise PlanError("candidate_id_invalid")
for key in ("source_commit","tree_oid","evidence_closure_oid"): _oid(raw.get(key), key)
for key in ("artifact_sha256","manifest_sha256","package_closure_sha256","deployment_plan_sha256","policy_sha256","campaign_authorization_sha256"): _sha(raw.get(key), key)
for key in ("release_plan_id","approval_plan_id"):
if not isinstance(raw.get(key), str) or not PLAN_ID_RE.fullmatch(raw[key]): raise PlanError(f"{key}_invalid")
if not isinstance(raw.get("campaign_generation"), int) or raw["campaign_generation"] < 1: raise PlanError("campaign_generation_invalid")
artifact = _exact_mapping(raw.get("artifact"), {"path","size"}, "artifact")
if not isinstance(artifact["path"], str) or not artifact["path"].startswith("/") or "\\" in artifact["path"]: raise PlanError("artifact_path_invalid")
if not isinstance(artifact["size"], int) or artifact["size"] <= 0: raise PlanError("artifact_size_invalid")
rollback = _exact_mapping(raw.get("rollback"), {"version","target_oid","artifact_sha256"}, "rollback")
if not isinstance(rollback["version"], str) or not rollback["version"] or rollback["version"] == version: raise PlanError("rollback_version_invalid")
_oid(rollback["target_oid"], "rollback_target_oid"); _sha(rollback["artifact_sha256"], "rollback_artifact_sha256")
baseline = _exact_mapping(raw.get("baseline"), {"version","target_oid"}, "baseline")
if baseline["version"] != rollback["version"]: raise PlanError("baseline_rollback_version_mismatch")
_oid(baseline["target_oid"], "baseline_target_oid")
adapter = _exact_mapping(raw.get("adapter_policy"), {"policy_id","service","port"}, "adapter_policy")
if adapter["policy_id"] != "aios.production-adapter.allowlisted.v1": raise PlanError("adapter_policy_invalid")
if not isinstance(adapter["service"], str) or not IDENTIFIER_RE.fullmatch(adapter["service"]): raise PlanError("adapter_service_invalid")
if not isinstance(adapter["port"], int) or not 1024 <= adapter["port"] <= 65535: raise PlanError("adapter_port_invalid")
payload = {key: raw[key] for key in TOP_LEVEL_KEYS if key != "release_plan_sha256"}
digest = hashlib.sha256(canonical_json(payload)).hexdigest()
if raw.get("release_plan_sha256") is not None and raw["release_plan_sha256"] != digest: raise PlanError("release_plan_sha256_mismatch")
return cls(value=dict(payload), release_plan_sha256=digest)
@classmethod
def load(cls, path: Path) -> "TrustedReleasePlan": return cls.from_mapping(_secure_json(path))
@property
def candidate_binding_sha256(self) -> str:
value = {key: self.value[key] for key in ("candidate_id","source_commit","tree_oid","artifact_sha256","manifest_sha256")}
return hashlib.sha256(canonical_json(value)).hexdigest()
def as_dict(self) -> dict[str, Any]: return {**dict(self.value),"release_plan_sha256":self.release_plan_sha256,"candidate_binding_sha256":self.candidate_binding_sha256}
def assert_binding(self, binding: Mapping[str, Any]) -> None:
expected={"version":self.value["version"],"target_oid":self.value["source_commit"],"evidence_closure_oid":self.value["evidence_closure_oid"],"artifact_sha256":self.value["artifact_sha256"],"artifact_size":self.value["artifact"]["size"],"approval_plan_id":self.value["approval_plan_id"]}
for key,value in expected.items():
if binding.get(key) != value: raise PlanError(f"binding_plan_mismatch:{key}")
if binding.get("candidate_id") is not None and binding.get("candidate_id") != self.value["candidate_id"]:
raise PlanError("binding_plan_mismatch:candidate_id")
def assert_artifact(self, artifact: Mapping[str, Any]) -> None:
expected=self.value["artifact"]
if artifact.get("sha256") != self.value["artifact_sha256"] or artifact.get("size") != expected["size"]: raise PlanError("artifact_plan_mismatch")
if artifact.get("manifest_sha256") != self.value["manifest_sha256"]: raise PlanError("manifest_plan_mismatch")
if artifact.get("package_closure_sha256") != self.value["package_closure_sha256"]: raise PlanError("package_closure_plan_mismatch")