158 lines
12 KiB
Python
158 lines
12 KiB
Python
"""Independent archive byte/receipt review; no archived code or binaries run."""
|
|
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
import hashlib
|
|
import io
|
|
import json
|
|
import subprocess
|
|
import zipfile
|
|
|
|
root = Path(__file__).resolve().parents[2]
|
|
folder = Path(__file__).resolve().parent
|
|
sha = lambda data: hashlib.sha256(data).hexdigest()
|
|
failures = []
|
|
checks = 0
|
|
def check(condition, message):
|
|
global checks
|
|
checks += 1
|
|
if not condition:
|
|
failures.append(message)
|
|
def safe(path):
|
|
return not PurePosixPath(path).is_absolute() and not PureWindowsPath(path).drive and '..' not in PurePosixPath(path).parts and '\\' not in path
|
|
def git(*args, data=None):
|
|
return subprocess.check_output(['git', *args], cwd=root, input=data)
|
|
|
|
receipt = json.loads((folder / 'archive-result.json').read_bytes())
|
|
raw = (folder / receipt['archive']).read_bytes()
|
|
check(sha(raw) == receipt['sha256'] == 'abff68aeb2bd893577d4226c9ec4b6b0823b4ec22895a273ab2d22f344354207', 'archive SHA')
|
|
check(len(raw) == receipt['bytes'] == 6449971, 'archive bytes')
|
|
zipfile_data = zipfile.ZipFile(io.BytesIO(raw))
|
|
names = zipfile_data.namelist()
|
|
prefix = 'source-rename-native-closure/'
|
|
check(len(names) == len(set(names)) == receipt['members'] == 355, 'member count/duplicates')
|
|
check(zipfile_data.testzip() is None, 'archive CRC')
|
|
check(all(safe(n) and n.startswith(prefix) for n in names), 'member path safety')
|
|
check(all(PurePosixPath(n).suffix.lower() not in {'.exe', '.dll', '.pyc', '.zip', '.7z', '.gz'} for n in names), 'excluded binary/archive payload')
|
|
check(all(not ({'bin', 'profile', 'tmp', '__pycache__', '.git', 'target'} & set(PurePosixPath(n).parts)) for n in names), 'excluded directory payload')
|
|
payload = {n.removeprefix(prefix): zipfile_data.read(n) for n in names}
|
|
manifest = json.loads(payload['PAYLOAD-MANIFEST.json'])
|
|
check(manifest['selfExcluded'] == 'PAYLOAD-MANIFEST.json', 'manifest self exclusion')
|
|
entries = manifest['members']
|
|
check(len(entries) == len({e['path'] for e in entries}) == len(payload) - 1, 'manifest duplicate/count')
|
|
check({e['path'] for e in entries} == set(payload) - {'PAYLOAD-MANIFEST.json'}, 'manifest full membership')
|
|
for entry in entries:
|
|
data = payload[entry['path']]
|
|
check(sha(data) == entry['sha256'] and len(data) == entry['bytes'], 'member bytes: ' + entry['path'])
|
|
originals = json.loads(payload['ORIGINAL-SOURCES.json'])
|
|
check(len(originals) == receipt['originalReadbacks'] == 351, 'original count')
|
|
for path, entry in originals.items():
|
|
original = (root / path).read_bytes()
|
|
check(original == payload[entry['member']], 'original differs: ' + path)
|
|
check(sha(original) == entry['sha256'] and len(original) == entry['bytes'], 'original manifest: ' + path)
|
|
|
|
fixture = json.loads(payload['preparation-and-reviews/fixture-manifest.json'])
|
|
check(sha(payload['preparation-and-reviews/fixture-manifest.json']) == 'a25651305b23da985eb6d8c637a687a0c4261b5a8c0d8ef84dda95a51fbed931', 'fixture manifest pin')
|
|
trees = []
|
|
for case in ['simple', 'representative']:
|
|
for variant in ['baseline', 'expected', 'work']:
|
|
path = 'fixtures/' + case + '/' + variant + '/'
|
|
actual = {n.removeprefix(path): {'sha256': sha(data), 'bytes': len(data)} for n, data in payload.items() if n.startswith(path)}
|
|
expected = fixture['cases'][case]['original_files' if variant == 'baseline' else 'expected_files']
|
|
check(actual == expected, 'complete physical fixture: ' + path)
|
|
source_folder = root / '.local/source-rename-native-followup-20260912' / case / variant
|
|
physical = {p.relative_to(source_folder).as_posix(): {'sha256': sha(p.read_bytes()), 'bytes': p.stat().st_size} for p in source_folder.rglob('*') if p.is_file()}
|
|
check(physical == actual, 'physical tree membership: ' + path)
|
|
trees.append({'case': case, 'variant': variant, 'files': len(actual)})
|
|
check(sum(t['files'] for t in trees if t['variant'] == 'work') == receipt['finalPhysicalFiles'] == 41, 'final physical file count')
|
|
|
|
launch = json.loads(payload['trial/launch.json'])
|
|
closed = json.loads(payload['trial/closed.json'])
|
|
check(launch['pid'] == closed['pid'] == 9808 and closed['exitCode'] == 0, 'process identity/exit')
|
|
check(closed['finished'] == '2026-09-12T22:20:26.337292+00:00', 'actual closure timestamp')
|
|
check(closed['binaryUnchanged'] and closed['runtimeUnchanged'], 'closure artifact unchanged receipt')
|
|
for stream in ['stdout', 'stderr']:
|
|
check(payload['trial/' + stream + '.log'] == b'' and closed[stream + 'Sha256'] == sha(b''), 'empty closed log: ' + stream)
|
|
check(launch['binarySha256'] == '2486eecc00d8de9160ecd930f69394e00b1a1ce102d76fb776b978db33414ed3', 'native GUI pin')
|
|
check(sha(Path(launch['executable']).read_bytes()) == launch['binarySha256'], 'preserved native binary')
|
|
runtime = Path(launch['executable']).parent / 'libunwind.dll'
|
|
check(sha(runtime.read_bytes()) == launch['dllSha256'] == fixture['dll_sha256'], 'preserved native runtime')
|
|
verification_names = sorted(n for n in payload if n.startswith('trial/verification-') and n.endswith('/result.json'))
|
|
check(len(verification_names) == 8, 'eight oracle receipts')
|
|
verification_results = []
|
|
for name in verification_names:
|
|
result = json.loads(payload[name])
|
|
case = 'representative' if '/verification-representative-' in name else 'simple'
|
|
expected_key = 'original_files' if result['expectedVariant'] == 'baseline' else 'expected_files'
|
|
check(result['expectedVariant'] in {'baseline', 'expected'}, name + ': expected variant')
|
|
check(result['passed'] and result['exitCode'] == 0 and result['failure'] is None and result['physicalBytesMatch'] and result['actualUnchangedByOracle'], name + ': successful receipt')
|
|
check(result['actualFiles'] == fixture['cases'][case][expected_key], name + ': physical map')
|
|
check(result['binarySha256'] == launch['binarySha256'] and result['oracleSha256'] == fixture['oracle_sha256'] and result['dllSha256'] == fixture['dll_sha256'], name + ': artifact bindings')
|
|
for stream in ['stdout', 'stderr']:
|
|
check(sha(payload[name.replace('result.json', stream + '.log')]) == result[stream + 'Sha256'], name + ': ' + stream)
|
|
oracle_log = payload[name.replace('result.json', 'stdout.log')].decode()
|
|
check(('PASS exact model/identity/' + ('30' if case == 'representative' else '4') + ' reachable sources') in oracle_log, name + ': oracle scope output')
|
|
check('Saved disk state only' in result['scope'], name + ': unsaved scope boundary')
|
|
verification_results.append({'step': name.split('/')[1], 'case': case, 'expected': result['expectedVariant'], 'files': len(result['actualFiles']), 'stdout': oracle_log.strip()})
|
|
|
|
excluded = json.loads(payload['EXCLUSIONS.json'])
|
|
check(len(excluded) == 1 and excluded[0] == receipt['excludedImage'], 'excluded image record')
|
|
excluded_name = PureWindowsPath(excluded[0]['path']).name
|
|
check(excluded_name == '023-resume-declaration-preview-0.jpg' and 'trial/screenshots/' + excluded_name not in payload, '023 pixels excluded')
|
|
excluded_original = (root / excluded[0]['path']).read_bytes()
|
|
check(sha(excluded_original) == excluded[0]['sha256'] == 'c45c0fb6b1769a4a84d4f96bc6d6cd92c89c121ac96d4cce5829c97451899288' and len(excluded_original) == excluded[0]['bytes'], 'excluded original hash/bytes')
|
|
metadata_names = sorted(n for n in payload if n.startswith('trial/screenshots/') and n.endswith('.json'))
|
|
jpeg_names = {n for n in payload if n.startswith('trial/screenshots/') and n.endswith('.jpg')}
|
|
check(len(metadata_names) == receipt['metadata'] == 94 and len(jpeg_names) == receipt['includedJpeg'] == 94, 'capture counts')
|
|
check({int(PurePosixPath(n).name[:3]) for n in metadata_names} == set(range(1, 95)), 'complete metadata sequence')
|
|
referenced = set()
|
|
capture_times = {}
|
|
for name in metadata_names:
|
|
meta = json.loads(payload[name])
|
|
capture_times[int(PurePosixPath(name).name[:3])] = meta['atUtc']
|
|
for screenshot in meta['screenshots']:
|
|
path = 'trial/screenshots/' + screenshot['file']
|
|
if screenshot['file'] == excluded_name:
|
|
continue
|
|
check(path in jpeg_names, name + ': missing screenshot')
|
|
referenced.add(path)
|
|
check(referenced == jpeg_names, 'no orphan screenshots')
|
|
check(capture_times[22] == '2026-09-12T14:26:54.662Z' and capture_times[23] == '2026-09-12T21:54:27.800Z', 'recorded interruption interval')
|
|
|
|
binding = json.loads(payload['commit-binding/commit-binding.json'])
|
|
check(binding['commit'] == 'dfb6102ba5aec9e26a48cc5472b51a4b349459fd' and binding['fileCount'] == 516, 'historical committed scope')
|
|
check(sha(payload['commit-binding/verify_commit.py']) == binding['verifierSha256'], 'commit verifier binding')
|
|
gate = root / '.local/source-rename-integration-20260912/attempt-7'
|
|
for name, info in binding['evidenceBindings'].items():
|
|
data = (gate / name).read_bytes()
|
|
check(sha(data) == info['sha256'] and len(data) == info['bytes'], 'external gate binding: ' + name)
|
|
gate_manifest = json.loads((gate / 'inputs.json').read_bytes())
|
|
gate_zip = zipfile.ZipFile(gate / 'inputs.zip')
|
|
gate_files = {entry['path']: entry for entry in gate_manifest['files']}
|
|
check(gate_zip.testzip() is None and len(gate_zip.namelist()) == len(gate_files) == 516 and set(gate_zip.namelist()) == set(gate_files), 'external raw input membership/CRC')
|
|
scope_paths = git('ls-tree', '-r', '--name-only', '-z', binding['commit'], '--', *binding['capturedScopes']).decode().split('\0')[:-1]
|
|
check(set(scope_paths) == set(gate_files), 'exact committed 516-path scope')
|
|
ordered = sorted(gate_files)
|
|
blob_stream = git('cat-file', '--batch', data=''.join(binding['commit'] + ':' + p + '\n' for p in ordered).encode())
|
|
cursor = 0
|
|
current_deltas = []
|
|
for path in ordered:
|
|
end = blob_stream.index(b'\n', cursor)
|
|
header = blob_stream[cursor:end].split()
|
|
check(len(header) == 3 and header[1] == b'blob', 'committed blob header: ' + path)
|
|
size = int(header[2])
|
|
committed = blob_stream[end + 1:end + 1 + size]
|
|
cursor = end + size + 2
|
|
captured = gate_zip.read(path)
|
|
check(committed == captured and sha(captured) == gate_files[path]['sha256'] and len(captured) == gate_files[path]['bytes'], 'committed raw input: ' + path)
|
|
working = root / path
|
|
if not working.exists() or working.read_bytes() != captured:
|
|
current_deltas.append({'path': path, 'archivedInputSha256': sha(captured), 'currentWorktreeSha256': sha(working.read_bytes()) if working.exists() else None})
|
|
check(cursor == len(blob_stream), 'complete Git byte stream')
|
|
check(sum(e['bytes'] for e in gate_files.values()) == binding['inputBytes'], 'input byte total')
|
|
summary = json.loads(payload['SUMMARY.json'])
|
|
check(summary['externalInputsSha256'] == binding['archiveSha256'] == sha((gate / 'inputs.zip').read_bytes()), 'archive external input pin')
|
|
check(summary['closedUtc'] == closed['finished'] and summary['commit'] == binding['commit'] and summary['binarySha256'] == launch['binarySha256'], 'summary closure/source binding')
|
|
|
|
result = {'status': 'PASS' if not failures else 'FAIL', 'checks': checks, 'archiveSha256': sha(raw), 'archiveBytes': len(raw), 'members': len(names), 'originalReadbacks': len(originals), 'physicalTrees': trees, 'finalPhysicalFiles': 41, 'oracleReceipts': verification_results, 'metadataCount': len(metadata_names), 'includedJpeg': len(jpeg_names), 'excludedImage': excluded[0], 'closedUtc': closed['finished'], 'pid': closed['pid'], 'exitCode': closed['exitCode'], 'committedScope': binding['commit'], 'committedRawInputsVerified': len(gate_files), 'currentWorktreeDifferencesFromHistoricalInputs': current_deltas, 'failures': failures, 'scope': 'Independent archive/receipt/raw committed source verification only; no GUI/Cargo/oracle/helper execution. Current worktree differences are expected later work, not historical archive failures.'}
|
|
(folder / 'independent-review.json').write_text(json.dumps(result, indent=2), encoding='utf-8')
|
|
print(json.dumps(result, indent=2))
|