Prepare reviewed attended reuse-reader custody execution
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
This commit is contained in:
parent
6e8955fcd0
commit
635daec6b4
6 changed files with 496 additions and 0 deletions
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"id": "CCR-2026-0046-ATTENDED-CUSTODY-HANDOFF-R3",
|
||||
"revision": 3,
|
||||
"work_item": "RPF-WP-0058-T03",
|
||||
"status": "prepared; founder/live execution approval pending",
|
||||
"source_published_commit": "6e8955fcd09b278a6f16c14beffd076b72d10c95",
|
||||
"consumer_image_digest": "sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
|
||||
"commands": {
|
||||
"read_only_claims_preflight": "env PATH=\"/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1:$PATH\" BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- python3 /home/worsch/railiance-platform/scripts/apply-reuse-surface-reader-custody.py --preflight --receipt /tmp/cust81-installed-claims-r1.jsonl",
|
||||
"approved_policy_and_bootstrap": "env PATH=\"/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1:$PATH\" BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 warden access openbao-platform-admin-login --exec -- python3 /home/worsch/railiance-platform/scripts/apply-reuse-surface-reader-custody.py --execute --approval /tmp/cust81-approved-custody-r1.json --receipt /tmp/cust81-policy-custody-r1.jsonl --bootstrap-receipt /tmp/cust81-forge-reader-r1.jsonl",
|
||||
"existing_inner_forgejo_route": "warden access forgejo-admin-api-token --field API_TOKEN --exec -- python3 /home/worsch/railiance-platform/scripts/provision-reuse-surface-forgejo-source.py --inner --approval /tmp/cust81-approved-custody-r1.json --receipt /tmp/cust81-forge-reader-r1.jsonl"
|
||||
},
|
||||
"approval_receipt_schema": {
|
||||
"ccr_id": "CCR-2026-0046",
|
||||
"decision": "approved only after actual founder disposition",
|
||||
"supervisor": "recorded supervisor identity",
|
||||
"decision_ref": "resolved exact founder credential-change decision reference",
|
||||
"approved_artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "23039fae6137e7bdd71de0ed3278596b1ed60ae636018eb4bfcc83e53c4ada02",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "2bb72671e0a5f230497ab8ef2967a20ee68d1f2cf081f07c84f6db3442e18d8b",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "abf60a754596baa1610d23b1fd367385b0222ac8aeaebba0f2cfb7ec4c62055b",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b"
|
||||
},
|
||||
"runner_artifacts": {
|
||||
"scripts/apply-reuse-surface-reader-custody.py": "934b421c357e154f6508ab4b7c30f1aadfea2bf9a2f187d83968928d5eecad42",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json": "178dd2aeddf3544438d584fa16e3ceb1a541a357d4dff5b8b4b0e95db2f357ac"
|
||||
}
|
||||
},
|
||||
"authorization_caveat": "Schema is explanatory, not an approved receipt. Authentic authority relies on governed attended supervisor lane. Proposal binds exact future CCR bytes plus predecessor and only-two-field transition rule; actual installed role proof is mandatory before transition. StoreReady, mode labels or candidate fields never establish observed claims.",
|
||||
"immutable_role_preflight": {
|
||||
"path": "auth/kubernetes/role/reuse-surface-runtime-eso",
|
||||
"bound_service_account_names": [
|
||||
"reuse-surface-eso"
|
||||
],
|
||||
"bound_service_account_namespaces": [
|
||||
"reuse"
|
||||
],
|
||||
"audience": "openbao",
|
||||
"token_policies": [
|
||||
"workload-kv-read-reuse-surface-runtime-eso"
|
||||
],
|
||||
"token_ttl": 900,
|
||||
"token_max_ttl": 900,
|
||||
"token_explicit_max_ttl": 900,
|
||||
"token_no_default_policy": true,
|
||||
"token_period": 0,
|
||||
"token_bound_cidrs": []
|
||||
},
|
||||
"policy_only_reason": "Generic CCR metadata applier would rewrite existing ESO role; CCR0046 does not carry all CCR0005 maxTTL/no-default fields. This runner NEVER writes role and only adds exact approved ACL path after old-or-approved byte comparison. OpenBao ACL has no CAS; immediate predecessor recheck and successor verification are attended guards, not atomicCAS.",
|
||||
"receipt_semantics": "Durable sanitized JSONL; no role objects/secret/network output stored. Output mismatch or ambiguity stops; inspect child receipt before retry. Warden envelope owns login persistence, session selfrevoke and cleanup; its13 exit means child may have succeeded, revocation unconfirmed.",
|
||||
"gates": [
|
||||
"Integrate/publish/review new quiet runner and tests before handing concrete command to founder.",
|
||||
"Existing owner-prepared WSL opener must match pinned cmd.exe/xdg-open bytes and be prefixed through task-local PATH for attended identity act; no global/browser/BROWSER workaround or capturedURL fallback.",
|
||||
"Actual founder credential provisioning approval, independent owner review of new exact-policy runner, and confirmed installedclaims before execute.",
|
||||
"No default/personal/root session fallback; nested Forgejo route must resolve using existing platform-admin caller custody and capture output.",
|
||||
"No delivery by this runner: apps owner separately deploys approved image+exact14URLconfig with live headroom/dryrun and confirms currentsource inventory.",
|
||||
"Final nativefetch verification and scopedwriter seven registrations plus fresh hosted70/70 remain required."
|
||||
],
|
||||
"validation": {
|
||||
"runner_boundary_tests": 17,
|
||||
"result": "passed",
|
||||
"target": "wrongclaims/ttl/policy refused, approved conditional-state scope, verified policy snapshot stdin, predecessor/idempotent drift, final immutable role+desired ACL revalidation before verified terminal, output suppression"
|
||||
},
|
||||
"live_actions_performed": false,
|
||||
"conditional_ccr_transition": {
|
||||
"candidate_wrapper": "docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json",
|
||||
"candidate_wrapper_sha256": "178dd2aeddf3544438d584fa16e3ceb1a541a357d4dff5b8b4b0e95db2f357ac",
|
||||
"source_preimage_sha256": "90522912fb78fed72e625382fb7114a0e9aafe76d59e36bffecab83c5bb7d643",
|
||||
"approved_future_ccr_sha256": "23039fae6137e7bdd71de0ed3278596b1ed60ae636018eb4bfcc83e53c4ada02",
|
||||
"state": "Preparation only: candidate approved/confirmed fields are not observed facts, not active approval, not installed proof.",
|
||||
"rule": "After actual founder decision exact receipt, validate all artifact/candidate/predecessor hashes; prove actual existing role claims from suppressed read; only then atomically install exact two-field approved/confirmed CCR bytes with durable receipt, preserve immutable role. Unchanged provisioner approval check now sees prebound future CCR bytes. Any mismatch stops without installing candidate.",
|
||||
"post_execution_recording": "Source transition must be committed/projected by platform owner with observedclaim receipt and actual supervisor decision. No additional edits to approved CCR bytes are required to execute; preserve original proposed revision and this candidate hash."
|
||||
},
|
||||
"browser_opener": {
|
||||
"path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1",
|
||||
"cmd_exe_sha256": "4b91a4dc635e464143bfe1f484eccb2456ccd992326ac7a126b53d0a9a3c7f96",
|
||||
"xdg_open_sha256": "4b91a4dc635e464143bfe1f484eccb2456ccd992326ac7a126b53d0a9a3c7f96",
|
||||
"status": "owner prepared/validated; no GUI or login executed",
|
||||
"contract": "Installed Bao2.5.5 capv0.12.0 WSL calls cmd.exe /c start; task-local PATH adapter preserves existing HOME/token envelope. BROWSER unsupported; no global opener change.",
|
||||
"validation_sha256": "9cbd8be4d9acfc19241e70bcda5df93eb2eacc83cb00771a5543022acc4ed474"
|
||||
},
|
||||
"r2_changes_requested": {
|
||||
"review_path": "/tmp/cust81-forge-reader-owner/docs/proposals/cust81-private-reader/forge-owner-attended-runner-review-r2.json",
|
||||
"review_sha256": "c4e61cd185bfc738fe8e2f7e89b71408ed35d441dfa38183cd83bad9c0832445",
|
||||
"confirmed_defects": [
|
||||
"Mutable approved policy filepath reopened at write",
|
||||
"Idempotent path/final receipt lacked refreshed ACL and immutable-role verification"
|
||||
],
|
||||
"r3_fix": "Capture exact policy bytes and reverify cached approved digest after baseapproval; stdin-only policywrite snapshot. Both before and after bootstrap verify approved desired ACL and immutable role including idempotent path. Drift stops without overwrite or retry.",
|
||||
"same_proposal": "CCR0046 refinement; preserve R2 failures, no new scored trial"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "RPF-WP-0058-PLATFORM-ATTENDED-RUNNER-REVIEW-R3",
|
||||
"work_item": "RPF-WP-0058-T03",
|
||||
"reviewer": "platform-reader-owner delegated worker",
|
||||
"owner_repo": "railiance-platform",
|
||||
"decision": "approved",
|
||||
"scope": "Exact-policy runner and conditional CCR state transition PREPARATION only; no founder/live authorization, candidate remains unapplied/unverified",
|
||||
"basis": "INTENT shared custody/Q1 delegated apply and stable delivery. Existing ESO role must retain CCR0005 constraints; scoped ACL-only change avoids generic role rewrite. Existing approved credential lane transports retained.",
|
||||
"reviewed_artifacts": {
|
||||
"scripts/apply-reuse-surface-reader-custody.py": "934b421c357e154f6508ab4b7c30f1aadfea2bf9a2f187d83968928d5eecad42",
|
||||
"tests/test_reuse_surface_reader_custody_apply.py": "cb2c4b5f8a96cfb71015d94c5d51de0e1bbf5c8e3d90e866b207a070c1a7a266",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json": "178dd2aeddf3544438d584fa16e3ceb1a541a357d4dff5b8b4b0e95db2f357ac",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/attended-custody-handoff-r1.json": "1247ad5aa73289a26a48e999b870ce2d60abb409132783e14456ea318689acff"
|
||||
},
|
||||
"tests": "17 privileged-boundary tests passed;45 allreader tests. Verifiedsnapshot stdin, before/after bootstrap actualrole+desiredACL rechecks on idempotent path; R2 failures preserved, sameproposal refinement. No live calls/credentials/mutations.",
|
||||
"pending": "Founder exact execution disposition, independent owner finalreview of R3, successful installedclaimspreflight. Owner-pinned WSL opener prepared/validated; login itself not performed. No authrole migration or admin-token bypass.",
|
||||
"revision": 3
|
||||
}
|
||||
242
scripts/apply-reuse-surface-reader-custody.py
Executable file
242
scripts/apply-reuse-surface-reader-custody.py
Executable file
|
|
@ -0,0 +1,242 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Silent attended exact-policy update and existing reviewed reader bootstrap.
|
||||
|
||||
Unlike generic CCR metadata apply, this command NEVER writes the reused ESO role.
|
||||
Installed role drift and differing ACL stop before mutations. --preflight reads
|
||||
only role/ACL metadata; it does not request Forgejo credentials. --execute needs
|
||||
approved CCR plus exact bootstrap and runner artifact receipts. Outer sanctioned
|
||||
OpenBao login owns identity, browser, helper containment and self-revocation.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
ROOT=Path(__file__).resolve().parent.parent
|
||||
SCRIPT='scripts/apply-reuse-surface-reader-custody.py'
|
||||
CANDIDATE_FILE='docs/proposals/2026-10-15-reuse-private-reader/conditional-approved-ccr-state-candidate-r1.json'
|
||||
POLICY_FILE='openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl'
|
||||
POLICY='workload-kv-read-reuse-surface-runtime-eso'
|
||||
ROLE='auth/kubernetes/role/reuse-surface-runtime-eso'
|
||||
BASELINE='''path "platform/data/workloads/reuse/reuse-surface/runtime-secrets" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/lookup-self" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "auth/token/revoke-self" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
'''
|
||||
spec=importlib.util.spec_from_file_location('reuse_reader_bootstrap',ROOT/'scripts/provision-reuse-surface-forgejo-source.py')
|
||||
p=importlib.util.module_from_spec(spec);spec.loader.exec_module(p)
|
||||
|
||||
|
||||
def validate_role(data):
|
||||
expected={'bound_service_account_names':['reuse-surface-eso'],'bound_service_account_namespaces':['reuse'],
|
||||
'audience':'openbao','token_ttl':900,'token_max_ttl':900,'token_explicit_max_ttl':900,
|
||||
'token_no_default_policy':True,'token_policies':[POLICY]}
|
||||
if not isinstance(data,dict) or any(data.get(k)!=v for k,v in expected.items()):
|
||||
raise p.Refused('installed_eso_role_drift')
|
||||
if 'policies' in data and data['policies']!=[POLICY]:
|
||||
raise p.Refused('installed_eso_role_drift')
|
||||
if data.get('token_period',0)!=0 or data.get('token_bound_cidrs',[]) or data.get('bound_service_account_namespace_selector',''):
|
||||
raise p.Refused('installed_eso_role_drift')
|
||||
|
||||
|
||||
def read_policy():
|
||||
result=p.Bao.run(['policy','read',POLICY])
|
||||
if result.returncode!=0:
|
||||
raise p.Refused('policy_read_failed')
|
||||
try:
|
||||
return result.stdout.decode().strip()
|
||||
except Exception:
|
||||
raise p.Refused('policy_invalid_encoding') from None
|
||||
|
||||
|
||||
def preflight(journal, desired_text=None):
|
||||
result=p.Bao.run(['read','-format=json',ROLE])
|
||||
if result.returncode!=0:
|
||||
raise p.Refused('installed_eso_role_read_failed')
|
||||
try:
|
||||
data=json.loads(result.stdout)['data']
|
||||
except Exception:
|
||||
raise p.Refused('installed_eso_role_invalid') from None
|
||||
validate_role(data)
|
||||
desired=(ROOT/POLICY_FILE).read_text().strip() if desired_text is None else desired_text
|
||||
current=read_policy()
|
||||
if current not in (BASELINE.strip(),desired):
|
||||
raise p.Refused('installed_policy_drift')
|
||||
journal.put('verified',operation='installed_eso_role_and_policy',status='matched_immutable_role')
|
||||
return current,desired
|
||||
|
||||
|
||||
def approval_envelope(path):
|
||||
"""Bind conditional future CCR bytes without recording imaginary consent."""
|
||||
try:
|
||||
receipt=json.loads(Path(path).read_text())
|
||||
if receipt['ccr_id']!=p.CCR or receipt['decision']!='approved' or not receipt['supervisor'] or not receipt['decision_ref']:
|
||||
raise p.Refused('runner_approval_invalid')
|
||||
if set(receipt['runner_artifacts'])!={SCRIPT,POLICY_FILE,CANDIDATE_FILE}:
|
||||
raise p.Refused('runner_approval_artifact_set')
|
||||
for rel in (SCRIPT,POLICY_FILE,CANDIDATE_FILE):
|
||||
if hashlib.sha256((ROOT/rel).read_bytes()).hexdigest()!=receipt['runner_artifacts'][rel]:
|
||||
raise p.Refused('runner_approved_revision_mismatch')
|
||||
wrapper=json.loads((ROOT/CANDIDATE_FILE).read_text())
|
||||
before=wrapper['source_yaml'].encode()
|
||||
after=wrapper['candidate_yaml'].encode()
|
||||
if hashlib.sha256(before).hexdigest()!=wrapper['source_sha256'] or hashlib.sha256(after).hexdigest()!=wrapper['candidate_sha256']:
|
||||
raise p.Refused('candidate_digest_invalid')
|
||||
old=yaml.safe_load(before); new=yaml.safe_load(after)
|
||||
if old['id']!=p.CCR or old['status']!='proposed' or old['openbao']['auth']['bound_claims_confirmed'] is not False:
|
||||
raise p.Refused('candidate_predecessor_invalid')
|
||||
old['status']='approved'; old['openbao']['auth']['bound_claims_confirmed']=True
|
||||
if old!=new:
|
||||
raise p.Refused('candidate_changes_exceed_scope')
|
||||
exact={p.CCR_FILE,p.PLAN_FILE,p.SCRIPT_FILE,POLICY_FILE}
|
||||
if set(receipt['approved_artifacts'])!=exact or receipt['approved_artifacts'][p.CCR_FILE]!=wrapper['candidate_sha256']:
|
||||
raise p.Refused('candidate_approval_invalid')
|
||||
for rel in exact-{p.CCR_FILE}:
|
||||
if hashlib.sha256((ROOT/rel).read_bytes()).hexdigest()!=receipt['approved_artifacts'][rel]:
|
||||
raise p.Refused('bootstrap_approved_revision_mismatch')
|
||||
current=(ROOT/p.CCR_FILE).read_bytes()
|
||||
if current not in (before,after):
|
||||
raise p.Refused('ccr_source_precondition_failed')
|
||||
return before,after,receipt['approved_artifacts'][POLICY_FILE]
|
||||
except p.Refused:
|
||||
raise
|
||||
except Exception:
|
||||
raise p.Refused('runner_approval_invalid') from None
|
||||
|
||||
|
||||
def activate_candidate(before,after,journal):
|
||||
"""Called ONLY after receipt validation and installed claims verification."""
|
||||
path=ROOT/p.CCR_FILE
|
||||
if path.read_bytes()==after:
|
||||
return
|
||||
if path.read_bytes()!=before:
|
||||
raise p.Refused('ccr_source_precondition_failed')
|
||||
journal.put('intent',operation='conditional_ccr_state_transition')
|
||||
temporary=None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(dir=path.parent,prefix='.cust81-ccr-',delete=False) as file:
|
||||
temporary=Path(file.name)
|
||||
file.write(after);file.flush();os.fsync(file.fileno())
|
||||
os.chmod(temporary,path.stat().st_mode & 0o777)
|
||||
if path.read_bytes()!=before:
|
||||
raise p.Refused('ccr_source_precondition_failed')
|
||||
os.replace(temporary,path);temporary=None
|
||||
directory=os.open(path.parent,os.O_DIRECTORY)
|
||||
try: os.fsync(directory)
|
||||
finally: os.close(directory)
|
||||
if path.read_bytes()!=after:
|
||||
raise p.Refused('ccr_transition_unverified')
|
||||
journal.put('verified',operation='conditional_ccr_state_transition',status='exact_candidate_installed_after_claim_proof')
|
||||
finally:
|
||||
if temporary is not None:
|
||||
temporary.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def verify_ready(journal,desired):
|
||||
current,_=preflight(journal,desired_text=desired)
|
||||
if current!=desired:
|
||||
raise p.Refused('approved_policy_not_installed')
|
||||
journal.put('verified',operation='desired_acl_and_immutable_role',status='matched')
|
||||
|
||||
|
||||
def execute(approval,receipt,journal):
|
||||
before,after,approved_policy_sha256=approval_envelope(approval)
|
||||
current,_=preflight(journal)
|
||||
activate_candidate(before,after,journal)
|
||||
p.approval(approval)
|
||||
# Capture exact approved bytes once. Later repo-file changes cannot alter
|
||||
# the authorized ACL input: Bao receives only this snapshot through stdin.
|
||||
policy_snapshot=(ROOT/POLICY_FILE).read_bytes()
|
||||
if hashlib.sha256(policy_snapshot).hexdigest()!=approved_policy_sha256:
|
||||
raise p.Refused('approved_policy_snapshot_mismatch')
|
||||
try:
|
||||
desired=policy_snapshot.decode().strip()
|
||||
except Exception:
|
||||
raise p.Refused('policy_invalid_encoding') from None
|
||||
if current!=desired:
|
||||
# Recheck immediately before the exact write. OpenBao ACL has no CAS;
|
||||
# this is an attended owner operation, never an unattended reconcile.
|
||||
if read_policy()!=current:
|
||||
raise p.Refused('policy_changed_since_preflight')
|
||||
journal.put('intent',operation='exact_eso_policy_update')
|
||||
try:
|
||||
result=p.Bao.run(['policy','write',POLICY,'-'],stdin=policy_snapshot)
|
||||
except p.Refused:
|
||||
journal.put('outcome',operation='exact_eso_policy_update',status='ambiguous')
|
||||
raise
|
||||
journal.put('outcome',operation='exact_eso_policy_update',status='observed' if result.returncode==0 else 'ambiguous')
|
||||
if read_policy()!=desired:
|
||||
raise p.Refused('policy_update_unverified')
|
||||
journal.put('verified',operation='exact_eso_policy_update',status='matched')
|
||||
# Verify both immutable role and desired ACL even on the idempotent path.
|
||||
verify_ready(journal,desired)
|
||||
journal.put('intent',operation='reviewed_forgejo_bootstrap_route')
|
||||
try:
|
||||
result=subprocess.run([sys.executable,str(ROOT/p.SCRIPT_FILE),'--approval',str(Path(approval).resolve()),'--receipt',str(Path(receipt).resolve())],capture_output=True,check=False,timeout=1000,cwd=ROOT)
|
||||
except Exception:
|
||||
journal.put('outcome',operation='reviewed_forgejo_bootstrap_route',status='ambiguous')
|
||||
raise p.Refused('bootstrap_observation_failed') from None
|
||||
# Even whitespace violates quiet owner command. Raw child/network bytes
|
||||
# never enter receipts or caller output; failed/ambiguous writes retained.
|
||||
if result.returncode!=0 or result.stdout or result.stderr:
|
||||
journal.put('outcome',operation='reviewed_forgejo_bootstrap_route',status='stopped_check_child_receipt')
|
||||
raise p.Refused('bootstrap_route_unverified')
|
||||
journal.put('outcome',operation='reviewed_forgejo_bootstrap_route',status='observed_success')
|
||||
verify_ready(journal,desired)
|
||||
journal.put('terminal',outcome='policy_and_custody_verified_delivery_pending')
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser=argparse.ArgumentParser()
|
||||
mode=parser.add_mutually_exclusive_group(required=True)
|
||||
mode.add_argument('--preflight',action='store_true')
|
||||
mode.add_argument('--execute',action='store_true')
|
||||
parser.add_argument('--approval')
|
||||
parser.add_argument('--receipt',required=True)
|
||||
parser.add_argument('--bootstrap-receipt')
|
||||
args=parser.parse_args(argv)
|
||||
journal=None
|
||||
lane_lock=None
|
||||
try:
|
||||
if args.execute and (not args.approval or not args.bootstrap_receipt or Path(args.receipt).resolve()==Path(args.bootstrap_receipt).resolve()):
|
||||
raise p.Refused('execute_inputs_invalid')
|
||||
lane_lock=os.open('/tmp/'+p.CCR+'-policy-state-'+str(os.getuid())+'.lock',os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW,0o600)
|
||||
if os.fstat(lane_lock).st_uid!=os.getuid() or os.fstat(lane_lock).st_mode & 0o077:
|
||||
raise p.Refused('lane_lock_owner_or_permissions')
|
||||
p.fcntl.flock(lane_lock,p.fcntl.LOCK_EX|p.fcntl.LOCK_NB)
|
||||
journal=p.Journal(args.receipt)
|
||||
if args.preflight:
|
||||
preflight(journal)
|
||||
journal.put('terminal',outcome='read_only_preflight_verified')
|
||||
else:
|
||||
execute(args.approval,args.bootstrap_receipt,journal)
|
||||
return 0
|
||||
except Exception as error:
|
||||
if journal:
|
||||
journal.put('terminal',outcome='stopped_reconcile_before_retry',error_code=str(error) if isinstance(error,p.Refused) else 'internal_failure')
|
||||
return 1
|
||||
finally:
|
||||
if journal:
|
||||
journal.close()
|
||||
if lane_lock is not None:
|
||||
os.close(lane_lock)
|
||||
|
||||
if __name__=='__main__':
|
||||
try:
|
||||
result=main()
|
||||
except Exception:
|
||||
result=1
|
||||
raise SystemExit(result)
|
||||
116
tests/test_reuse_surface_reader_custody_apply.py
Normal file
116
tests/test_reuse_surface_reader_custody_apply.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import importlib.util
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
import json
|
||||
import yaml
|
||||
import hashlib
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
spec=importlib.util.spec_from_file_location('custody_apply',Path(__file__).parents[1]/'scripts/apply-reuse-surface-reader-custody.py');r=importlib.util.module_from_spec(spec);spec.loader.exec_module(r)
|
||||
class Journal:
|
||||
def __init__(self): self.rows=[]
|
||||
def put(self,event,**kw): self.rows.append((event,kw))
|
||||
class Tests(unittest.TestCase):
|
||||
def role(self): return {'bound_service_account_names':['reuse-surface-eso'],'bound_service_account_namespaces':['reuse'],'audience':'openbao','token_ttl':900,'token_max_ttl':900,'token_explicit_max_ttl':900,'token_no_default_policy':True,'token_policies':[r.POLICY]}
|
||||
def envelope_fixture(self,root,extra=False):
|
||||
old={'id':r.p.CCR,'status':'proposed','openbao':{'auth':{'bound_claims_confirmed':False}}}
|
||||
new={'id':r.p.CCR,'status':'approved','openbao':{'auth':{'bound_claims_confirmed':True}}}
|
||||
if extra: new['unexpected_privilege']='broader'
|
||||
before=yaml.safe_dump(old).encode();after=yaml.safe_dump(new).encode()
|
||||
files={r.SCRIPT,r.POLICY_FILE,r.CANDIDATE_FILE,r.p.CCR_FILE,r.p.PLAN_FILE,r.p.SCRIPT_FILE}
|
||||
for rel in files:
|
||||
f=root/rel;f.parent.mkdir(parents=True,exist_ok=True);f.write_text('mock')
|
||||
(root/r.p.CCR_FILE).write_bytes(before)
|
||||
wrapper={'source_yaml':before.decode(),'candidate_yaml':after.decode(),'source_sha256':hashlib.sha256(before).hexdigest(),'candidate_sha256':hashlib.sha256(after).hexdigest()}
|
||||
(root/r.CANDIDATE_FILE).write_text(json.dumps(wrapper))
|
||||
record={'ccr_id':r.p.CCR,'decision':'approved','supervisor':'founder','decision_ref':'mock','runner_artifacts':{rel:hashlib.sha256((root/rel).read_bytes()).hexdigest() for rel in (r.SCRIPT,r.POLICY_FILE,r.CANDIDATE_FILE)},'approved_artifacts':{rel:hashlib.sha256((root/rel).read_bytes()).hexdigest() for rel in (r.p.CCR_FILE,r.p.PLAN_FILE,r.p.SCRIPT_FILE,r.POLICY_FILE)}}
|
||||
record['approved_artifacts'][r.p.CCR_FILE]=wrapper['candidate_sha256']
|
||||
approval=root/'approval.json';approval.write_text(json.dumps(record));return approval,before,after
|
||||
def test_conditional_candidate_exact_future_hash_without_premature_install(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);approval,before,after=self.envelope_fixture(root)
|
||||
with patch.object(r,'ROOT',root): self.assertEqual(r.approval_envelope(approval),(before,after,hashlib.sha256(b'mock').hexdigest()))
|
||||
self.assertEqual((root/r.p.CCR_FILE).read_bytes(),before)
|
||||
def test_candidate_cannot_change_other_privilege_fields(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);approval,_,_=self.envelope_fixture(root,extra=True)
|
||||
with patch.object(r,'ROOT',root),self.assertRaisesRegex(r.p.Refused,'changes_exceed_scope'): r.approval_envelope(approval)
|
||||
def test_candidate_predecessor_changed_refused(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);approval,_,_=self.envelope_fixture(root);(root/r.p.CCR_FILE).write_text('different')
|
||||
with patch.object(r,'ROOT',root),self.assertRaisesRegex(r.p.Refused,'source_precondition'):r.approval_envelope(approval)
|
||||
def test_candidate_atomic_exact_transition_idempotent(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);_,before,after=self.envelope_fixture(root);j=Journal()
|
||||
with patch.object(r,'ROOT',root):
|
||||
r.activate_candidate(before,after,j);r.activate_candidate(before,after,j)
|
||||
self.assertEqual((root/r.p.CCR_FILE).read_bytes(),after)
|
||||
self.assertEqual(len(j.rows),2)
|
||||
def test_role_claims_policy_ttl_drift_rejected(self):
|
||||
for key,value in [('audience','other'),('bound_service_account_names',['other']),('token_ttl',0),('token_no_default_policy',False),('token_policies',[r.POLICY,'default'])]:
|
||||
data=self.role(); data[key]=value
|
||||
with self.subTest(key=key),self.assertRaisesRegex(r.p.Refused,'role_drift'): r.validate_role(data)
|
||||
def test_role_unchanged_baseline_accepted(self): r.validate_role(self.role())
|
||||
def test_unapproved_inputs_before_metadata_reads_or_writes(self):
|
||||
with patch.object(r,'approval_envelope',side_effect=r.p.Refused('runner_approval_invalid')),patch.object(r.p.Bao,'run') as run:
|
||||
with self.assertRaises(r.p.Refused): r.execute('mock','mock',Journal())
|
||||
run.assert_not_called()
|
||||
def test_installed_claim_failure_prevents_candidate_activation(self):
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'preflight',side_effect=r.p.Refused('installed_eso_role_drift')),patch.object(r,'activate_candidate') as activate,patch.object(r.subprocess,'run') as child:
|
||||
with self.assertRaisesRegex(r.p.Refused,'role_drift'): r.execute('mock','mock',Journal())
|
||||
activate.assert_not_called(); child.assert_not_called()
|
||||
def test_policy_drift_after_preflight_prevents_write_and_credential_route(self):
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',return_value=('old','new')),patch.object(r,'read_policy',return_value='changed'),patch.object(r.p.Bao,'run') as run,patch.object(r.subprocess,'run') as child:
|
||||
with self.assertRaisesRegex(r.p.Refused,'changed_since'): r.execute('mock','mock',Journal())
|
||||
run.assert_not_called(); child.assert_not_called()
|
||||
def snapshot(self): return (r.ROOT/r.POLICY_FILE).read_bytes().decode().strip()
|
||||
def test_exact_policy_only_then_existing_bootstrap_route(self):
|
||||
j=Journal(); ok=type('R',(),{'returncode':0,'stdout':b'','stderr':b''})()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',return_value=('old','new')),patch.object(r,'read_policy',side_effect=['old',self.snapshot()]),patch.object(r,'verify_ready') as verify,patch.object(r.p.Bao,'run',return_value=ok) as run,patch.object(r.subprocess,'run',return_value=ok) as child:
|
||||
r.execute('mockapproval','mockreceipt',j)
|
||||
self.assertEqual(verify.call_count,2);self.assertEqual(run.call_count,1); self.assertEqual(run.call_args.args[0],['policy','write',r.POLICY,'-']);self.assertEqual(run.call_args.kwargs['stdin'],(r.ROOT/r.POLICY_FILE).read_bytes()); self.assertIn(r.p.SCRIPT_FILE,str(child.call_args.args[0])); self.assertNotIn('auth/kubernetes',str(run.call_args))
|
||||
def test_policy_write_timeout_recorded_without_bootstrap_retry(self):
|
||||
j=Journal()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',return_value=('old','new')),patch.object(r,'read_policy',return_value='old'),patch.object(r.p.Bao,'run',side_effect=r.p.Refused('bao_observation_failed')),patch.object(r.subprocess,'run') as child:
|
||||
with self.assertRaises(r.p.Refused): r.execute('mock','mock',j)
|
||||
self.assertEqual(j.rows[-1],('outcome',{'operation':'exact_eso_policy_update','status':'ambiguous'})); child.assert_not_called()
|
||||
def test_mutable_policy_replaced_at_write_uses_approved_stdin_snapshot(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);f=root/r.POLICY_FILE;f.parent.mkdir(parents=True);f.write_bytes(b'approved-policy')
|
||||
j=Journal();seen=[];ok=type('R',(),{'returncode':0,'stdout':b'','stderr':b''})()
|
||||
def write(args,stdin=None):
|
||||
f.write_bytes(b'unapproved-replacement');seen.append((args,stdin));return ok
|
||||
with patch.object(r,'ROOT',root),patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256(b'approved-policy').hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',return_value=('old','approved-policy')),patch.object(r,'read_policy',side_effect=['old','approved-policy']),patch.object(r.p.Bao,'run',side_effect=write),patch.object(r,'verify_ready'),patch.object(r.subprocess,'run',return_value=ok):
|
||||
r.execute('mock','mock',j)
|
||||
self.assertEqual(seen,[(['policy','write',r.POLICY,'-'],b'approved-policy')])
|
||||
def test_policy_changed_before_snapshot_refuses_before_write(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp);f=root/r.POLICY_FILE;f.parent.mkdir(parents=True);f.write_bytes(b'approved-policy')
|
||||
def late_approval(*args):f.write_bytes(b'unapproved-replacement')
|
||||
with patch.object(r,'ROOT',root),patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256(b'approved-policy').hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval',side_effect=late_approval),patch.object(r,'preflight',return_value=('old','approved-policy')),patch.object(r.p.Bao,'run') as write,patch.object(r.subprocess,'run') as child:
|
||||
with self.assertRaisesRegex(r.p.Refused,'snapshot_mismatch'):r.execute('mock','mock',Journal())
|
||||
write.assert_not_called();child.assert_not_called()
|
||||
def test_idempotent_acl_drift_before_bootstrap_refuses(self):
|
||||
desired=self.snapshot();j=Journal()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',side_effect=[(desired,desired),(r.BASELINE.strip(),desired)]),patch.object(r.p.Bao,'run') as write,patch.object(r.subprocess,'run') as child:
|
||||
with self.assertRaisesRegex(r.p.Refused,'not_installed'):r.execute('mock','mock',j)
|
||||
write.assert_not_called();child.assert_not_called()
|
||||
self.assertFalse(any(e=='terminal' for e,kw in j.rows))
|
||||
def test_acl_drift_after_bootstrap_prevents_verified_terminal(self):
|
||||
desired=self.snapshot();j=Journal();ok=type('R',(),{'returncode':0,'stdout':b'','stderr':b''})()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',side_effect=[(desired,desired),(desired,desired),(r.BASELINE.strip(),desired)]),patch.object(r.subprocess,'run',return_value=ok) as child:
|
||||
with self.assertRaisesRegex(r.p.Refused,'not_installed'):r.execute('mock','mock',j)
|
||||
child.assert_called_once()
|
||||
self.assertFalse(any(e=='terminal' for e,kw in j.rows))
|
||||
def test_immutable_role_drift_after_bootstrap_prevents_verified_terminal(self):
|
||||
desired=self.snapshot();j=Journal();ok=type('R',(),{'returncode':0,'stdout':b'','stderr':b''})()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',side_effect=[(desired,desired),(desired,desired),r.p.Refused('installed_eso_role_drift')]),patch.object(r.subprocess,'run',return_value=ok):
|
||||
with self.assertRaisesRegex(r.p.Refused,'role_drift'):r.execute('mock','mock',j)
|
||||
self.assertFalse(any(e=='terminal' for e,kw in j.rows))
|
||||
def test_child_output_stops_without_exposing_bytes(self):
|
||||
j=Journal(); bad=type('R',(),{'returncode':0,'stdout':b'fake-secret-provider-output','stderr':b''})()
|
||||
with patch.object(r,'approval_envelope',return_value=(b'before',b'after',hashlib.sha256((r.ROOT/r.POLICY_FILE).read_bytes()).hexdigest())),patch.object(r,'activate_candidate'),patch.object(r.p,'approval'),patch.object(r,'preflight',return_value=(self.snapshot(),self.snapshot())),patch.object(r,'verify_ready'),patch.object(r.subprocess,'run',return_value=bad):
|
||||
with self.assertRaisesRegex(r.p.Refused,'bootstrap_route_unverified'): r.execute('mock','mock',j)
|
||||
self.assertNotIn('secret',str(j.rows))
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -87,3 +87,17 @@ Revision2 of the same CCR preserves R1 capped-page failure and the independently
|
|||
corrected preliminary owned-only endpoint concern. Twenty bounded pages until
|
||||
empty, duplicate IDs and supplied total-count consistency prevent silent short
|
||||
page acceptance. No live complete-grant proof is claimed by these mocks.
|
||||
|
||||
|
||||
Attended execution preparation R3 is complete, while T03 stays `wait` because no
|
||||
founder credential decision, login, installed-claim proof or live write has run.
|
||||
The quiet owner runner captures hash-verified policy bytes for stdin, preserves
|
||||
the existing ESO role, and verifies the actual immutable role/desired ACL before
|
||||
and after bootstrap, including idempotent runs. A conditional CCR candidate binds
|
||||
only approved/confirmed state transitions; these fields are not observed facts
|
||||
until actual founder disposition and installed-role proof satisfy the rule.
|
||||
Independent Forge preparation review R3 approved the exact runner; its receipt
|
||||
SHA256 is e4a17ee59fb1691f7b99a09d2473178740721aad0c1394cae79b503118c97040.
|
||||
Forty-five targeted reader tests passed (17 attended-runner boundary cases).
|
||||
The handoff binds the owner-prepared task-local WSL opener and existing sanctioned
|
||||
OpenBao/Forgejo routes. No secret value or live state has been acquired or changed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue