Finalizer v2 lands three audit fixes: - Gitea consistency proven via authoritative git transport (remote get-url / symbolic-ref --quiet HEAD / ls-remote --exit-code origin <exact branch>); receipt records transport=git-ls-remote, api_verified=false (no REST invented). - Receipts are append-only immutable: each run uses its own round3-gate-w/<source[:12]>/<run-id>/<kind>.json namespace; existing ref fails closed (GITEA_PROVIDER_RECEIPT_EXISTS, O_EXCL+O_NOFOLLOW root-owned). - Gate semantics separated: provider_auth_closed / tests_result_passed / secret_scan_result_passed / gitea_consistent / gate_w_ready independent; authentication PASS never converts a red result gate into Gate-W PASS. New files: active-runtime/app/gitea_transport_consistency_provider.py, scripts/round3_provider_gatew_prove_v2.py (replaces prove v1), tests/test_gitea_transport_consistency_provider.py. prove v2 enforces a real clean git checkout (HEAD==source, tree, clean) and returns RC 0/1/2/3. Finalizer zip SHA256=c5f031967a7359b89398eccacb68c07162d92e1c5b2c5f4badf1a669c9f31ef6 (manifest verified). Three prior tests failures resolved without changing assertions or adding allowlist: V2R1C6 digest + rollback = CRLF/tar-export contamination false failures (LF normalize reproduces exact hash; sh returns 42+BLOCKED); signer self-test = real deployment config (allowed_signers pubkey synced to actual credential, SIGNER_SELF_TEST=PASS rc=0). This final source commit is FINAL_SOURCE_HEAD; subsequent commits on source branch are prohibited (package goes to artifact store / detached).
155 lines
5.9 KiB
Python
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())
|