170 lines
12 KiB
Python
170 lines
12 KiB
Python
"""Read-only, Python-standard-library verification. Never executes archived code."""
|
|
from pathlib import Path, PurePosixPath
|
|
from datetime import datetime
|
|
import hashlib
|
|
import io
|
|
import json
|
|
import re
|
|
import zipfile
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
|
|
def require(ok, message):
|
|
if not ok:
|
|
raise ValueError(message)
|
|
|
|
def digest(data):
|
|
return {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
|
|
|
def members(z, expected):
|
|
names = z.namelist()
|
|
require(len(names) == len(set(names)), 'duplicate ZIP members')
|
|
require(set(names) == set(expected), 'archive membership mismatch')
|
|
require(all(not PurePosixPath(n).is_absolute() and '..' not in PurePosixPath(n).parts
|
|
and '\\' not in n and ':' not in n for n in names), 'unsafe member')
|
|
|
|
def main():
|
|
inventory = json.loads((ROOT / 'files.json').read_text())
|
|
for name, expected in inventory.items():
|
|
require(digest((ROOT / name).read_bytes()) == expected, 'external file hash: ' + name)
|
|
manifest = json.loads((ROOT / 'manifest.json').read_text())
|
|
with zipfile.ZipFile(ROOT / 'evidence.zip') as z:
|
|
members(z, manifest['members'])
|
|
for name, expected in manifest['members'].items():
|
|
require(digest(z.read(name)) == expected, 'member hash: ' + name)
|
|
def raw(name): return z.read(name)
|
|
def obj(name): return json.loads(raw(name))
|
|
checked = set()
|
|
for run, spec in manifest['runs'].items():
|
|
if spec['kind'] != 'cargo':
|
|
continue
|
|
prefix = 'runs/' + run + '/'
|
|
start, result, process = (obj(prefix + n + '.json') for n in ('started', 'result', 'process'))
|
|
require(all(result[k] == v for k, v in start.items()), run + ': start/result relationship')
|
|
require(result['command'] == spec['command'] == process['command'], run + ': command')
|
|
require(result['head'] == spec['head'], run + ': HEAD')
|
|
require(result['exitCode'] == spec['exitCode'] and result['inputsUnchanged'] is True, run + ': outcome')
|
|
require(datetime.fromisoformat(result['finish']) >= datetime.fromisoformat(start['start']), run + ': times')
|
|
require(result['seconds'] >= 0, run + ': duration')
|
|
require(result['helper'] == digest(raw('helpers/gate-union.py')), run + ': helper')
|
|
for field, file in [('inputs', spec['inputs']), ('archive', spec['archive']), ('stdout', prefix+'stdout.log'), ('stderr', prefix+'stderr.log')]:
|
|
require(result[field] == digest(raw(file)), run + ': receipt ' + field)
|
|
inputs = obj(spec['inputs'])
|
|
require(len(inputs) == result['inputCount'] == spec['inputCount'] == 707, run + ': input count')
|
|
require(sum(p.startswith('crates/vendor/iced_widget/') for p in inputs) == 71, run + ': vendor count')
|
|
pair = (spec['inputs'], spec['archive'])
|
|
if pair not in checked:
|
|
with zipfile.ZipFile(io.BytesIO(raw(spec['archive']))) as source:
|
|
members(source, inputs)
|
|
for name, expected in inputs.items():
|
|
require(digest(source.read(name)) == expected, run + ': source hash ' + name)
|
|
checked.add(pair)
|
|
stdout = raw(prefix+'stdout.log').decode('utf-8', errors='replace')
|
|
rows = [[int(x) for x in row] for row in re.findall(r'test result: (?:ok|FAILED)\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;', stdout)]
|
|
require(rows == spec['testRows'], run + ': exact test summaries')
|
|
require([sum(row[i] for row in rows) for i in range(5)] == spec['testTotals'], run + ': test totals')
|
|
artifacts = []
|
|
for line in stdout.splitlines():
|
|
try: item = json.loads(line)
|
|
except json.JSONDecodeError: continue
|
|
if isinstance(item, dict) and item.get('reason') == 'compiler-artifact' and item.get('executable'):
|
|
artifacts.append(item)
|
|
require(artifacts == [a['cargo'] for a in result['artifacts']], run + ': artifact receipt relationship')
|
|
print(run, 'exit', result['exitCode'], 'test totals', spec['testTotals'])
|
|
admission(z, manifest)
|
|
print('PASS:', len(manifest['runs']), 'closed captures;', len(manifest['members']), 'members;', len(checked), 'input manifest/archive pairs')
|
|
print('Integrity and recorded outcomes only; no rerun, binary verification, or native GUI claim.')
|
|
|
|
def admission(z, manifest):
|
|
def obj(p): return json.loads(z.read(p))
|
|
report = obj('helpers/desktop-admission.json')
|
|
checklist = obj('helpers/required-desktop-tests.json')
|
|
require(report['status'] == 'PASS', 'admission status')
|
|
require({k:report['checklist'][k] for k in ('bytes','sha256')} == digest(z.read('helpers/required-desktop-tests.json')), 'checklist receipt')
|
|
require(report['checklist']['sourceCommit'] == checklist['sourceCommit'], 'checklist source pin')
|
|
for run, receipt in report['runs'].items():
|
|
result = obj('runs/'+run+'/result.json')
|
|
for field in ('head','command','exitCode','inputsUnchanged','inputCount'):
|
|
require(receipt[field] == result[field], 'admission run relationship '+field)
|
|
require(receipt['inputManifest'] == result['inputs'] and receipt['stdout'] == result['stdout'], 'admission inputs/log')
|
|
require(receipt['resultReceipt'] == digest(z.read('runs/'+run+'/result.json')), 'admission result hash')
|
|
listing = z.read('runs/desktop-list-1/stdout.log').decode('utf-8')
|
|
execution = z.read('runs/desktop-1/stdout.log').decode('utf-8')
|
|
names = re.findall(r'^([A-Za-z0-9_:]+): test$', listing, re.M)
|
|
outcomes = re.findall(r'^test ([A-Za-z0-9_:]+) \.\.\. (ok|FAILED|ignored)(?:, (.*))?$', execution, re.M)
|
|
require(len(names) == len(set(names)) == 1185, 'listing names/count/uniqueness')
|
|
require(len(outcomes) == len({r[0] for r in outcomes}) == 1185, 'execution names/count/uniqueness')
|
|
require(set(names) == {r[0] for r in outcomes}, 'listing/execution set equality')
|
|
actual = {n:(status, reason or None) for n,status,reason in outcomes}
|
|
require(actual == {r['name']:(r['status'],r['reason']) for r in report['allActualNamesAndOutcomes']}, 'admission raw outcomes')
|
|
require(len(report['allActualNamesAndOutcomes']) == len(actual), 'admission duplicate outcomes')
|
|
required = []
|
|
for spec in checklist['required']:
|
|
matches = [n for n in names if n.endswith(spec['match']['suffix'])]
|
|
require(len(matches) == spec['match']['expectedOccurrences'] == 1, 'required unique match '+spec['name'])
|
|
n = matches[0]
|
|
require(actual[n][0] == ('ignored' if spec['expectedIgnored'] else 'ok'), 'required outcome '+n)
|
|
required.append({'sourcePath':spec['sourcePath'],'expectedBasename':spec['name'],'actualName':n,'expectedIgnored':spec['expectedIgnored'],'status':actual[n][0],'reason':actual[n][1]})
|
|
require(required == report['requiredOutcomes'], 'required admission relationships')
|
|
removed=[]
|
|
for spec in checklist['removedOrReplaced']:
|
|
require(not any(n.endswith('::'+spec['name']) for n in names), 'removed test present')
|
|
replacements=[n for replacement in spec.get('replacementNames',[]) for n in names if n.endswith('::'+replacement)]
|
|
removed.append({'name':spec['name'],'absent':True,'replacementActualNames':replacements})
|
|
require(removed == report['removedOrReplaced'], 'removed admission relationship')
|
|
retained=[]
|
|
for spec in checklist['duplicateDeparture']:
|
|
require(actual[spec['requiredFullName']][0] == 'ok' and spec['removedFullName'] not in names, 'retained duplicate admission')
|
|
require(sum(n.endswith('::'+spec['name']) for n in names) == spec['expectedTotalOccurrences'] == 1, 'retained duplicate count')
|
|
retained.append({'name':spec['name'],'retainedName':spec['requiredFullName'],'retainedStatus':'ok','removedParentNameAbsent':True})
|
|
require(retained == report['retainedDepartureCases'], 'retained admission relationship')
|
|
before = obj(manifest['runs']['desktop-1']['inputs'])
|
|
require(before == obj(manifest['runs']['desktop-list-1']['inputs']), 'listing/execution source equality')
|
|
require(before == obj(manifest['runs']['core-2']['inputs']), 'core/desktop full-suite source equality')
|
|
with zipfile.ZipFile(io.BytesIO(z.read(manifest['runs']['desktop-1']['archive']))) as source:
|
|
bindings=[]
|
|
for spec in checklist['sourceBindings']:
|
|
b=source.read(spec['path'])
|
|
blob=hashlib.sha1(b'blob '+str(len(b)).encode()+b'\0'+b).hexdigest()
|
|
require(blob == spec['gitBlob'], 'original checklist source blob '+spec['path'])
|
|
bindings.append({**spec,'capturedAndCurrentDigest':digest(b),'matchesOriginalChecklistBlob':True})
|
|
require(bindings == report['sourceBindings'], 'source binding admission')
|
|
summary={'listed':len(names),'executed':len(actual),'exactNameSetEquality':True,'duplicateListedNames':0,'duplicateExecutedNames':0,
|
|
'passed':sum(v[0]=='ok' for v in actual.values()),'failed':sum(v[0]=='FAILED' for v in actual.values()),'ignored':sum(v[0]=='ignored' for v in actual.values()),
|
|
'required':len(required),'requiredPassed':sum(r['status']=='ok' for r in required),'requiredIgnored':sum(r['status']=='ignored' for r in required),
|
|
'removedNamesAbsent':len(checklist['removedOrReplaced']),'retainedDuplicateCasesPassExactlyOnce':len(checklist['duplicateDeparture']),
|
|
'originalChecklistSourceFilesVerified':len(checklist['sourceBindings']),'listingAndExecutionInputManifestsEqual':True}
|
|
require(summary == report['summary'], 'admission summary')
|
|
after = obj(manifest['runs']['clippy-3']['inputs'])
|
|
changed={p:{'before':before.get(p),'after':after.get(p)} for p in sorted(set(before)|set(after)) if before.get(p)!=after.get(p)}
|
|
require(changed == manifest['fullSuiteToFinalInputChanges'] and len(changed)==4, 'four-file source delta')
|
|
require(set(changed) == {'crates/oa-desktop/src/source_navigation/controller.rs','crates/oa-desktop/src/app/source_symbols.rs','crates/oa-desktop/src/app/source_symbols/tests.rs','crates/oa-desktop/src/views.rs'}, 'reviewed four-file scope')
|
|
readback=obj('helpers/final-input-commit-readback.json')
|
|
require(readback['status']=='PASS' and readback['commit']==manifest['reviewedFixCommit'], 'secondary readback summary')
|
|
require(readback['inputCount']==len(after) and readback['inputManifestSha256']==digest(z.read(manifest['runs']['clippy-3']['inputs']))['sha256'], 'secondary readback input relationship')
|
|
for run in ('desktop-affected-2','windows-build-1'):
|
|
if run in manifest['runs']:
|
|
require(obj(manifest['runs'][run]['inputs']) == after, run+': final source equality')
|
|
if 'windows-build-1' in manifest['runs']:
|
|
result=obj('runs/windows-build-1/result.json')
|
|
require(result['exitCode']==0 and len(result['artifacts'])==3, 'final build outcome/artifacts')
|
|
require({a['cargo']['target']['name'] for a in result['artifacts']}=={'arcadia','arcadia-server','arcadia-studio'}, 'final build target membership')
|
|
if 'desktop-affected-2' in manifest['runs']:
|
|
spec=manifest['runs']['desktop-affected-2']
|
|
args=spec['command'][spec['command'].index('--')+1:]
|
|
filters=[arg for arg in args if not arg.startswith('--')]
|
|
require(filters==['source_symbols::tests','text_workspace::','views::tests','palette_union'], 'affected OR filters')
|
|
selected={n for n in names if any(f in n for f in filters)}
|
|
log=z.read('runs/desktop-affected-2/stdout.log').decode('utf-8')
|
|
affected=re.findall(r'^test ([A-Za-z0-9_:]+) \.\.\. (ok|FAILED|ignored)(?:, (.*))?$',log,re.M)
|
|
require(len(affected)==len({r[0] for r in affected})==len(selected)==84, 'affected count/unique names')
|
|
require({n for n,s,r in affected}==selected, 'affected exact OR-filter selection from prior listing')
|
|
require(sum(s=='ok' for n,s,r in affected)==83 and sum(s=='FAILED' for n,s,r in affected)==0, 'affected enabled passes')
|
|
require([n for n,s,r in affected if s=='ignored']==['app::source_symbols::tests::source_symbols_100_modules_1000_definitions_10000_uses'], 'affected known ignored probe')
|
|
require(spec['testTotals']==[83,0,1,0,len(names)-len(selected)], 'affected summary and filtered count')
|
|
print('Affected recheck: exact prior-list OR selection; 83 passed, 1 known ignored, 1101 filtered')
|
|
print('Desktop admission:', summary['passed'], 'passed,', summary['ignored'], 'ignored;',len(required),'required; four final changed input files')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|