#!/usr/bin/env python3 """Bounded read-only audit of C314-C322 published evidence packets.""" from pathlib import Path import hashlib, json, subprocess, sys, tempfile, zipfile ROOT = Path('/workspace/scratch/2cd312e78692') OUT = ROOT / 'axis_ledger_work/review' TEMP = Path(tempfile.mkdtemp(prefix='c323_archive_integrity_')) issues = [] records = [] def sha(raw): return hashlib.sha256(raw).hexdigest() def check(condition, kind, step, detail): if not condition: item = {'step':step, 'kind':kind, 'detail':detail} issues.append(item) print('MISMATCH '+json.dumps(item), flush=True) for number in range(314,323): step = f'C{number}' archive = ROOT / f'deliverables/490d_{step}_Evidence_20260924.zip' rec = {'step':step, 'archive':str(archive.relative_to(ROOT)), 'archive_bytes':archive.stat().st_size, 'archive_sha256':sha(archive.read_bytes())} with zipfile.ZipFile(archive) as z: members=z.namelist() rec['zip_members']=len(members) rec['zip_crc_passed']=z.testzip() is None check(rec['zip_crc_passed'],'zip_crc',step,archive.name) # Reject traversal before extracting to a new isolated directory. check(all(not Path(n).is_absolute() and '..' not in Path(n).parts for n in members), 'safe_zip_paths',step,archive.name) base=TEMP/step z.extractall(base) packet=base/step rec['isolated_packet_path']=str(packet) manifest=json.loads((packet/'ARTIFACT_MANIFEST.json').read_text()) listed=set() rec['manifest_payloads']=len(manifest['files']) for ent in manifest['files']: p=packet/ent['path']; listed.add(ent['path']) check(p.exists(),'manifest_missing',step,ent['path']) if p.exists(): raw=p.read_bytes() check(len(raw)==ent['bytes'] and sha(raw)==ent['sha256'],'manifest_hash_size',step,ent['path']) local=ROOT/'axis_ledger_work'/step/ent['path'] check(local.exists() and local.read_bytes()==raw,'sealed_workspace_identity',step,ent['path']) actual={str(p.relative_to(packet)) for p in packet.rglob('*') if p.is_file()} rec['unmanifested_members']=sorted(actual-listed) check(actual-listed=={'ARTIFACT_MANIFEST.json'},'unexpected_payloads',step,rec['unmanifested_members']) cert=json.loads((packet/'CERTIFICATE.json').read_text()) rec['certificate_sha256']=sha((packet/'CERTIFICATE.json').read_bytes()) rec['certificate_bindings']=len(cert['bindings']) for key,path in cert['bindings'].items(): p=packet/path check(p.exists() and sha(p.read_bytes())==cert[key+'_sha256'],'certificate_binding',step,path) prev_raw=(packet/'sources/PREVIOUS_CERTIFICATE.json').read_bytes() with zipfile.ZipFile(ROOT/f'deliverables/490d_C{number-1}_Evidence_20260924.zip') as prior: matches=[n for n in prior.namelist() if n==f'C{number-1}/CERTIFICATE.json'] check(len(matches)==1,'predecessor_certificate_member',step,matches) prior_raw=prior.read(matches[0]) rec['predecessor_step']=f'C{number-1}' rec['predecessor_sha256']=sha(prev_raw) check(prev_raw==prior_raw and sha(prev_raw)==cert['previous_certificate_sha256'], 'predecessor_chain',step,rec['predecessor_step']) source_manifest=json.loads((packet/'SOURCE_MANIFEST.json').read_text()) rec['frozen_sources']=len(source_manifest) for name,item in source_manifest.items(): raw=(packet/'sources'/name).read_bytes() check(len(raw)==item['bytes'] and sha(raw)==item['sha256'],'source_binding',step,name) p=Path(item['original_workspace_path']) check(p.exists() and p.read_bytes()==raw,'source_original_identity',step,name) replay=base/'replay' run=subprocess.run([sys.executable,str(packet/'evidence/check.py'),'--out',str(replay)], cwd=base,capture_output=True,text=True,timeout=60) rec['main_replay']={'returncode':run.returncode,'stdout':run.stdout.strip(),'stderr':run.stderr.strip()} check(run.returncode==0,'main_replay_exit',step,run.stderr[-2500:]) outputs=[] for name in ['DATA.json','RESULTS.json']: generated=replay/name; archived=packet/'evidence'/name identical=generated.exists() and generated.read_bytes()==archived.read_bytes() outputs.append({'name':name,'byte_identical':identical, 'archived_sha256':sha(archived.read_bytes()), 'replayed_sha256':sha(generated.read_bytes()) if generated.exists() else None}) check(identical,'main_replay_bytes',step,name) rec['main_replay']['outputs']=outputs recorded=json.loads((packet/'evidence/RESULTS.json').read_text()) check(recorded['assertions_passed']==cert['assertions_passed'] and recorded['source_checks_passed']==cert['source_checks_passed'], 'certificate_result_counts',step,recorded['assertions_passed']) rec['assertions_passed']=recorded['assertions_passed'] rec['source_checks_passed']=recorded['source_checks_passed'] independent=base/'review_replay' script=packet/f'evidence/{step}_Independent_Math.py' run=subprocess.run([sys.executable,str(script),'--sources',str(packet/'sources'),'--out',str(independent)], cwd=base,capture_output=True,text=True,timeout=60) rec['independent_replay']={'returncode':run.returncode,'stdout':run.stdout.strip(),'stderr':run.stderr.strip()} check(run.returncode==0,'independent_replay_exit',step,run.stderr[-2500:]) name=f'{step}_Independent_Math_Results.json' generated=independent/name;archived=packet/'evidence'/name identical=generated.exists() and generated.read_bytes()==archived.read_bytes() rec['independent_replay'].update({'byte_identical':identical,'archived_sha256':sha(archived.read_bytes()), 'replayed_sha256':sha(generated.read_bytes()) if generated.exists() else None}) check(identical,'independent_replay_bytes',step,name) rec['issues']=[i for i in issues if i['step']==step] rec['status']='passed' if not rec['issues'] else 'failed' records.append(rec) print(json.dumps({'step':step,'status':rec['status'],'payloads':rec['manifest_payloads'], 'main_replay':rec['main_replay']['returncode'],'independent_replay':rec['independent_replay']['returncode']}),flush=True) result={'audit':'C323 recovery gate: sealed C314-C322 packet integrity and portable replay', 'date':'2026-09-24','auditor':'archive_integrity subagent','status':'passed' if not issues else 'failed', 'research_scope':'No C323 research performed; replayed existing C314-C322 checkers with frozen packet sources.', 'sealed_files_modified':False,'fresh_isolated_replay_root':str(TEMP), 'packets':records,'issues':issues, 'totals':{'packets':len(records),'manifest_payloads':sum(r['manifest_payloads'] for r in records), 'certificate_bindings':sum(r['certificate_bindings'] for r in records), 'predecessor_links':len(records),'frozen_source_payloads':sum(r['frozen_sources'] for r in records), 'main_output_comparisons':2*len(records),'independent_output_comparisons':len(records)}} OUT.mkdir(parents=True,exist_ok=True) (OUT/'C323_Archive_Integrity_Results.json').write_text(json.dumps(result,indent=2)+'\n') print('FINAL '+json.dumps({'status':result['status'],'issues':issues,'totals':result['totals']}))