Files
aios/scripts/round3_provider_gatew_prove_v2.py
T

155 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Round-3 Gate-W proof using immutable, append-only provider receipts.
Requires a REAL clean Git checkout at FINAL_SOURCE_HEAD. It never deletes or
overwrites old receipts. Each run writes a new namespace.
It distinguishes three dimensions:
* provider authentication closure,
* provider result gates (e.g. tests passed),
* overall Gate-W readiness.
A resolver PASS never converts ``tests.result.passed=false`` into Gate-W PASS.
"""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import json
import os
from pathlib import Path
import subprocess
import sys
_APP = Path(__file__).resolve().parents[1] / "active-runtime" / "app"
sys.path.insert(0, str(_APP))
from gitea_transport_consistency_provider import produce_gitea_consistency_receipt
from trusted_evidence_providers import TrustedProviderExecutionError, _validate_oid, produce
from trusted_provider_receipts import TrustedProviderPolicy, TrustedProviderReceiptResolver
GATE_W = ("tests", "secret_scan", "gitea")
def mode(v):
return v if isinstance(v, int) else int(str(v), 8)
def build_resolver(policy_path):
raw = json.loads(Path(policy_path).read_text(encoding="utf-8"))
defaults = dict(raw.get("defaults") or {})
policies = {}
for name,cfg in dict(raw.get("providers") or {}).items():
policies[name] = TrustedProviderPolicy(
evidence_kind=name,
provider_identity=str(cfg["provider_identity"]),
store_id=str(cfg["store_id"]),
store_root=Path(str(cfg["store_root"])),
allowed_operations=frozenset(map(str,cfg["allowed_operations"])),
expected_owner_uid=int(cfg.get("expected_owner_uid", defaults.get("expected_owner_uid",0))),
allowed_directory_modes=frozenset(mode(x) for x in cfg.get("allowed_directory_modes",defaults.get("allowed_directory_modes",["0700","0750"]))),
allowed_receipt_modes=frozenset(mode(x) for x in cfg.get("allowed_receipt_modes",defaults.get("allowed_receipt_modes",["0600","0640"]))),
authentication_kind=str(cfg.get("authentication_kind",defaults.get("authentication_kind","root-owned-store-v1"))),
max_age_seconds=cfg.get("max_age_seconds",defaults.get("max_age_seconds")),
)
return TrustedProviderReceiptResolver(policies), policies
def git(repo, *args):
p = subprocess.run(["git",*args],cwd=str(repo),text=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,check=False)
if p.returncode:
raise SystemExit(f"GIT_PRECHECK_FAILED {' '.join(args)}: {p.stderr.strip()}")
return p.stdout.strip()
def main():
ap=argparse.ArgumentParser()
ap.add_argument("--source-oid",required=True)
ap.add_argument("--tree-oid",required=True)
ap.add_argument("--checkout",required=True)
ap.add_argument("--policy",required=True)
ap.add_argument("--run-id",default=None)
args=ap.parse_args()
source=_validate_oid(args.source_oid,"source_oid")
tree=_validate_oid(args.tree_oid,"tree_oid")
repo=Path(args.checkout).resolve()
if git(repo,"rev-parse","HEAD^{commit}").lower()!=source:
raise SystemExit("FINAL_SOURCE_HEAD_MISMATCH")
if git(repo,"rev-parse",f"{source}^{{tree}}").lower()!=tree:
raise SystemExit("FINAL_SOURCE_TREE_MISMATCH")
if git(repo,"status","--porcelain"):
raise SystemExit("FINAL_SOURCE_WORKTREE_DIRTY")
run_id=args.run_id or datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
resolver,policies=build_resolver(args.policy)
os.environ["AIOS_REPO_ROOT"]=str(repo)
os.environ.setdefault("AIOS_GITLEAKS_BIN","/usr/bin/gitleaks")
produced=[]
mechanism_required=[]
for kind in GATE_W:
ref=f"round3-gate-w/{source[:12]}/{run_id}/{kind}.json"
try:
if kind=="gitea":
sha,_payload=produce_gitea_consistency_receipt(
repo_root=repo,
source_oid=source,
tree_oid=tree,
policy_path=args.policy,
receipt_ref=ref,
)
else:
sha=produce(
kind,
source_oid=source,
tree_oid=tree,
receipt_ref=ref,
store_root=policies[kind].store_root,
)
produced.append((kind,ref,sha))
except TrustedProviderExecutionError as exc:
if "PROVIDER_MECHANISM_REQUIRED" in str(exc):
mechanism_required.append((kind,str(exc)))
else:
raise
auth_ok=True
results={}
for kind,ref,sha in produced:
r=resolver.resolve(kind,ref,source_oid=source,tree_oid=tree)
results[kind]=dict(r.result)
print(f"[AUTH PASS] {kind} ref={ref} sha={sha} result={json.dumps(r.result,sort_keys=True)}")
expected=set(GATE_W)
closed={k for k,_,_ in produced}
auth_closed=auth_ok and closed==expected and not mechanism_required
test_gate=bool(results.get("tests",{}).get("passed") is True)
secret_gate=bool(results.get("secret_scan",{}).get("passed") is True)
gitea_gate=bool(results.get("gitea",{}).get("consistent") is True)
gate_ready=auth_closed and test_gate and secret_gate and gitea_gate
summary={
"source_oid":source,
"tree_oid":tree,
"run_id":run_id,
"authenticated_providers":sorted(closed),
"mechanism_required":[k for k,_ in mechanism_required],
"provider_auth_closed":auth_closed,
"tests_result_passed":test_gate,
"secret_scan_result_passed":secret_gate,
"gitea_consistent":gitea_gate,
"gate_w_ready":gate_ready,
}
print("GATE_W_PROVE="+json.dumps(summary,sort_keys=True))
if mechanism_required:
return 2
if not auth_closed:
return 1
if not gate_ready:
return 3
return 0
if __name__=="__main__":
raise SystemExit(main())