120 lines
6.7 KiB
Python
120 lines
6.7 KiB
Python
"""Read-only public donor retrieval; never executes archive contents."""
|
|
from pathlib import Path, PurePosixPath
|
|
import hashlib
|
|
import io
|
|
import json
|
|
import stat
|
|
import urllib.request
|
|
import zipfile
|
|
import zlib
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
COMMIT = '206f3b969482241e01e2dfc6be955caf31747b89'
|
|
BASE = ('http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/raw/commit/' + COMMIT
|
|
+ '/harness/prerelease37-windows-gates-20260924/final04-complete/')
|
|
|
|
|
|
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
|
def redirect_request(self, *args, **kwargs):
|
|
raise RuntimeError('Redirect refused')
|
|
|
|
|
|
def require(value, message):
|
|
if not value:
|
|
raise RuntimeError(message)
|
|
|
|
|
|
def safe(name):
|
|
p = PurePosixPath(name)
|
|
require(name and not p.is_absolute() and '\\' not in name and ':' not in name
|
|
and all(x not in ('', '.', '..') for x in name.split('/')),
|
|
'Unsafe archive path')
|
|
require(all(x.rstrip(' .') == x and x.split('.')[0].upper() not in
|
|
{'CON', 'PRN', 'AUX', 'NUL', *('COM' + str(i) for i in range(10)),
|
|
*('LPT' + str(i) for i in range(10))} for x in p.parts), 'Unsafe Windows path')
|
|
return name
|
|
|
|
|
|
def main():
|
|
data = (ROOT / 'manifest.json').read_bytes()
|
|
require(len(data) == 103364 and hashlib.sha1(b'blob 103364\0' + data).hexdigest()
|
|
== '5d246cc2ca69c6aaaffaa5dc70ec1be2bdae4262', 'Manifest Git blob mismatch')
|
|
m = json.loads(data)
|
|
require(m['source_commit'] == '715036e132677dd2bc2fb37ed316d93dc9987aec', 'Source mismatch')
|
|
require(m['archive'] == 'final04-complete-715036e.zip', 'Unexpected archive name')
|
|
expected_size = 6588421
|
|
expected_hash = '7cead0b6aa36a6af8fc9f8987186bfe0decd8fa2ebab094fc45387ae9daea632'
|
|
require(m['archive_bytes'] == expected_size and m['archive_sha256'] == expected_hash,
|
|
'Archive manifest mismatch')
|
|
with urllib.request.build_opener(NoRedirect).open(BASE + m['archive'], timeout=45) as response:
|
|
require(response.status == 200, 'Unexpected HTTP status')
|
|
archive = response.read(expected_size + 1)
|
|
require(len(archive) == expected_size and hashlib.sha256(archive).hexdigest() == expected_hash,
|
|
'Archive bytes mismatch')
|
|
members = m['members']
|
|
require(len(members) == 462, 'Manifest count mismatch')
|
|
expected = {safe(f['path']): f for f in members}
|
|
require(len(expected) == len(members) and len({p.casefold() for p in expected}) == len(expected),
|
|
'Duplicate manifest paths')
|
|
require(sum(f['bytes'] for f in members) <= 45000000, 'Expanded size cap exceeded')
|
|
selected = {'final-04/' + name for name in
|
|
['summary.json', 'inputs-before.json', 'environment.json', 'artifact-relocation.json']}
|
|
selected.update(p for p in expected if p.startswith(('final-04/arcadia-server-build/',
|
|
'final-04/arcadia-studio-build/')))
|
|
payloads = {}
|
|
with zipfile.ZipFile(io.BytesIO(archive)) as z:
|
|
infos = z.infolist()
|
|
require(len(infos) == 462 and len({f.filename.casefold() for f in infos}) == 462,
|
|
'Archive duplicate/count mismatch')
|
|
require({safe(f.filename) for f in infos} == set(expected), 'Archive set mismatch')
|
|
# Verify every member before reading any payload as structured data or
|
|
# writing selected portable files. CRC is also checked by ZipFile.read.
|
|
for info in infos:
|
|
require(not info.is_dir() and not stat.S_ISLNK(info.external_attr >> 16)
|
|
and not (info.flag_bits & 1), 'Directory, symlink or encrypted member refused')
|
|
f = expected[info.filename]
|
|
require(info.file_size == f['bytes'] and info.file_size <= 5000000, 'Member size mismatch/cap')
|
|
require(f'{info.CRC:08x}' == f['crc32'], 'Member header CRC mismatch')
|
|
payload = z.read(info)
|
|
require(len(payload) == f['bytes'] and hashlib.sha256(payload).hexdigest() == f['sha256']
|
|
and f'{zlib.crc32(payload):08x}' == f['crc32'], 'Member integrity mismatch')
|
|
if info.filename in selected:
|
|
payloads[info.filename] = payload
|
|
|
|
assertions = json.loads((ROOT.parent.parent / 'harness/experiments/save-as-diagnostics-20261001/donor/build-evidence.json').read_text())['binary_origins']['arcadia-server.exe']['original_build_evidence']
|
|
recovered = {}
|
|
for key, name in [('mandatory_gate', 'summary.json'), ('source_manifest', 'inputs-before.json'),
|
|
('environment', 'environment.json'), ('artifact_relocation', 'artifact-relocation.json')]:
|
|
member = 'final-04/' + name
|
|
f = expected[member]
|
|
require(all(f[k] == assertions[key][k] for k in ['bytes', 'sha256']), 'Original assertion mismatch: ' + key)
|
|
recovered[key] = {'member': member, 'portable_path': 'selected/' + member,
|
|
'bytes': f['bytes'], 'sha256': f['sha256'], 'original_assertion': assertions[key]}
|
|
require(not (ROOT / 'selected').exists() and not (ROOT / 'receipt.json').exists(), 'Recovery already exists')
|
|
with (ROOT / m['archive']).open('xb') as stream:
|
|
stream.write(archive)
|
|
for name, payload in payloads.items():
|
|
dest = ROOT / 'selected' / name
|
|
require(dest.resolve().is_relative_to((ROOT / 'selected').resolve()), 'Extraction escape')
|
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
with dest.open('xb') as stream:
|
|
stream.write(payload)
|
|
result = {'publication_commit': COMMIT, 'source_commit': m['source_commit'],
|
|
'manifest_url': BASE + 'manifest.json', 'manifest_git_blob': '5d246cc2ca69c6aaaffaa5dc70ec1be2bdae4262',
|
|
'manifest_bytes': len(data), 'manifest_sha256': hashlib.sha256(data).hexdigest(),
|
|
'archive_url': BASE + m['archive'], 'archive_bytes': len(archive), 'archive_sha256': expected_hash,
|
|
'verified_members': len(members), 'verified_expanded_bytes': sum(f['bytes'] for f in members),
|
|
'all_member_checks': 'Safe unique relative paths, exact set/count, size, CRC32 and SHA256; no archive code executed.',
|
|
'recovered_original_receipts': recovered,
|
|
'selected_files': {name: expected[name] for name in sorted(payloads)},
|
|
'scope': 'Public immutable receipt recovery only; historical donor claims retained; not new build/native/package acceptance.'}
|
|
with (ROOT / 'receipt.json').open('x', encoding='utf-8') as stream:
|
|
json.dump(result, stream, indent=2)
|
|
stream.write('\n')
|
|
print(json.dumps({'archive_sha256': expected_hash, 'verified_members': len(members),
|
|
'selected_count': len(payloads), 'recovered_keys': list(recovered)}, indent=2))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|