230 lines
15 KiB
Python
230 lines
15 KiB
Python
"""In-memory SFTP / temporary local files only; no real server access."""
|
|
import io
|
|
import json
|
|
from pathlib import Path
|
|
import stat
|
|
import tempfile
|
|
import types
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
import fetch_phase4an as f
|
|
|
|
|
|
def data(value):
|
|
return (json.dumps(value, sort_keys=True, allow_nan=False) + '\n').encode()
|
|
|
|
|
|
def row(name, raw):
|
|
return dict(path=name, bytes=len(raw), sha256=f.sha(raw))
|
|
|
|
|
|
class FakeSFTP:
|
|
def __init__(self, files):
|
|
self.files, self.opened, self.symlinks, self.hook = files, [], set(), None
|
|
|
|
def lstat(self, name):
|
|
if name in self.symlinks: return types.SimpleNamespace(st_mode=stat.S_IFLNK | 0o777, st_size=1)
|
|
if name in self.files: return types.SimpleNamespace(st_mode=stat.S_IFREG | 0o644, st_size=len(self.files[name]))
|
|
if any(x.startswith(name.rstrip('/') + '/') for x in self.files): return types.SimpleNamespace(st_mode=stat.S_IFDIR | 0o755, st_size=0)
|
|
raise FileNotFoundError(name)
|
|
|
|
def open(self, name, mode):
|
|
if mode != 'rb': raise AssertionError('Remote writes are forbidden')
|
|
self.opened.append(name)
|
|
if self.hook: self.hook(name)
|
|
return io.BytesIO(self.files[name])
|
|
|
|
|
|
def fixture(status=f.PASS):
|
|
payload, sources, inputs, identities = {}, {}, {}, []
|
|
for name in f.SOURCE_FILES:
|
|
content = ('# synthetic ' + name).encode(); path = 'protocol/source-snapshot/' + name
|
|
payload[path] = content
|
|
sources[f.REMOTE_ROOT + '/' + f.MODEL_REL + '/' + name] = f.sha(content)
|
|
inputs[f.REMOTE_ROOT + '/' + f.EXP_REL + '/' + path] = f.sha(content)
|
|
identities.append(dict(model_source=f.MODEL_REL + '/' + name, sealed_source=f.EXP_REL + '/' + path,
|
|
copy='source-snapshot/' + name, bytes=len(content), sha256=f.sha(content)))
|
|
payload['protocol/preregistered.json'] = data(dict(source_sha256=sources, input_sha256=inputs))
|
|
prereg = f.sha(payload['protocol/preregistered.json'])
|
|
numerical, covered = status != 'FAIL-NUMERICAL', status == f.PASS
|
|
analysis = dict(status=status, registration_sha256=prereg, source_sha256=sources, expected_calls=32,
|
|
analyzed_calls=32, executed_ticks=11074, numerical_exact=numerical, nonvacuous_temporal_coverage=covered,
|
|
pass_all=numerical and covered, training=False, candidate_generated=False, learning_pass_claimed=False)
|
|
payload['audit/engineering-analysis.json'] = data(analysis)
|
|
analysis_sha = f.sha(payload['audit/engineering-analysis.json'])
|
|
final = dict(status=status, analysis_sha256=analysis_sha, registration_sha256=prereg, analysis_recomputed_exactly=True,
|
|
completed_calls=32, executed_ticks=11074, training=False, learned_candidate=False, learning_pass_claimed=False)
|
|
payload['audit/final-status.json'] = data(final)
|
|
payload.update({'runs/example/state.npz': b'bulk', 'runs/example/queue.jsonl': b'bulk queue',
|
|
'extension-build/build.log': b'not selected', 'logs/readable.log': b'small log'})
|
|
manifest_rows = [row(name, raw) for name, raw in sorted(payload.items())]
|
|
manifest = data(dict(root=f.REMOTE_ROOT + '/' + f.EXP_REL, files=manifest_rows))
|
|
checksums = (''.join(r['sha256'] + ' ' + r['path'] + '\n' for r in manifest_rows) + f.sha(manifest) + ' manifest.json\n').encode()
|
|
controls = {'manifest.json': manifest, 'checksums.sha256': checksums}
|
|
registry, evidence_index, omitted = {}, [], []
|
|
for name, raw in sorted({**payload, **controls}.items()):
|
|
r = row(name, raw); target, reason = f.selected(r)
|
|
if target:
|
|
registry[target] = raw
|
|
evidence_index.append(dict(source=f.EXP_REL + '/' + name, copy=target, bytes=len(raw), sha256=f.sha(raw)))
|
|
else: omitted.append(dict(r, reason=reason))
|
|
proof = dict(pass_all=True, root=f.REMOTE_ROOT + '/' + f.EXP_REL, manifest_sha256=f.sha(manifest), checksums_sha256=f.sha(checksums),
|
|
payload_file_count=len(manifest_rows), payload_bytes=sum(r['bytes'] for r in manifest_rows))
|
|
registry['seal-verification.json'] = data(proof)
|
|
registry['evidence-index.json'] = data(evidence_index)
|
|
registry['source-identity.json'] = data(identities)
|
|
receipt = dict(schema='phase4an-engineering-publication-v1', publication_complete=True, project_root=f.REMOTE_ROOT,
|
|
registry=f.REGISTRY_REL, declared_status=status, used_sources_verified_before_and_after=True,
|
|
complete_checksums_reference_bulk_not_copied=True, publication_success_is_not_engineering_or_learning_pass=True,
|
|
model_directory_sealed=False, model_files_copied=False, complete_bulk_backup=False, numerical_gate_rerun_by_publisher=False,
|
|
training=False, candidate_generated=False, learning_pass_claimed=False, existing_files_modified=False, helper_globals_modified=False,
|
|
selection_policy=dict(max_file_bytes=f.MAX_FILE_BYTES, suffixes=sorted(f.SUFFIXES), source_snapshot_names=list(f.SOURCE_FILES),
|
|
excluded_tree='extension-build', all_jsonl_excluded=True),
|
|
used_source_count=21, completed_calls=32, executed_ticks=11074, preregistration_sha256=prereg,
|
|
source_identity='source-identity.json', bulk_remains_at=f.EXP_REL, experiment_manifest_sha256=f.sha(manifest),
|
|
experiment_checksums_sha256=f.sha(checksums), final_sha256=f.sha(payload['audit/final-status.json']), analysis_sha256=analysis_sha,
|
|
publisher_sha256='a'*64, helper_sha256='b'*64, files=[row(name, raw) for name, raw in sorted(registry.items())],
|
|
experiment_payload_not_copied=omitted, copied_evidence_files=len(evidence_index),
|
|
copied_evidence_bytes=sum(r['bytes'] for r in evidence_index), declared_numerical_exact=numerical,
|
|
declared_live_coverage=covered, declared_engineering_pass=numerical and covered)
|
|
registry['publication-receipt.json'] = data(receipt)
|
|
return FakeSFTP({f.REGISTRY_ROOT + '/' + name: raw for name, raw in registry.items()}), prereg, f.sha(registry['publication-receipt.json'])
|
|
|
|
|
|
class FetchTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory(prefix='phase4an-fetch-test-'); self.addCleanup(self.temp.cleanup)
|
|
self.destination = Path(self.temp.name).resolve() / 'report'
|
|
self.sftp, self.prereg, self.pin = fixture()
|
|
|
|
def fetch(self, **kwargs):
|
|
with patch.object(f, 'PREREG_SHA', self.prereg):
|
|
return f.fetch(self.sftp, self.destination, self.pin, **kwargs)
|
|
|
|
def receipt(self): return f.json_bytes(self.sftp.files[f.REGISTRY_ROOT + '/publication-receipt.json'])
|
|
|
|
def resign(self, receipt):
|
|
raw = data(receipt); self.sftp.files[f.REGISTRY_ROOT + '/publication-receipt.json'] = raw; self.pin = f.sha(raw)
|
|
|
|
def change_selected(self, name, content):
|
|
self.sftp.files[f.REGISTRY_ROOT + '/' + name] = content
|
|
receipt = self.receipt(); next(r for r in receipt['files'] if r['path'] == name).update(bytes=len(content), sha256=f.sha(content))
|
|
self.resign(receipt)
|
|
|
|
def test_exact_small_registry_mirror_and_receipt_hashes_no_model_or_bulk(self):
|
|
result = self.fetch()
|
|
self.assertTrue(result['fetch_complete']); self.assertTrue(result['exact_receipt_inventory_downloaded'])
|
|
self.assertFalse(result['engineering_gate_executed']); self.assertFalse(result['learning_pass_claimed'])
|
|
self.assertTrue((self.destination / 'evidence/audit/final-status.json').is_file())
|
|
self.assertTrue((self.destination / 'source-snapshot/runtime_light.py').is_file())
|
|
self.assertEqual(len(result['files']), len(self.receipt()['files']) + 1)
|
|
self.assertEqual(result['sftp_bytes_read_including_anchor_reread'], result['downloaded_bytes'] + len(self.sftp.files[f.REGISTRY_ROOT + '/publication-receipt.json']))
|
|
for r in result['files']:
|
|
self.assertEqual(f.sha((self.destination / r['path']).read_bytes()), r['sha256'])
|
|
self.assertTrue(all(name.startswith(f.REGISTRY_ROOT + '/') for name in self.sftp.opened))
|
|
self.assertFalse(any(name.endswith(('.npz', '.npy', '.jsonl')) or '/extension-build/' in name for name in self.sftp.opened))
|
|
|
|
def test_failure_and_incomplete_declarations_never_promoted(self):
|
|
for status in ('FAIL-NUMERICAL', 'INCOMPLETE-COVERAGE'):
|
|
with self.subTest(status=status), tempfile.TemporaryDirectory() as directory:
|
|
sftp, prereg, pin = fixture(status)
|
|
with patch.object(f, 'PREREG_SHA', prereg): result = f.fetch(sftp, Path(directory).resolve() / 'report', pin)
|
|
self.assertEqual(result['declared_status'], status)
|
|
self.assertTrue(result['fetch_success_is_not_engineering_or_learning_pass'])
|
|
|
|
def test_external_receipt_pin_and_fixed_prereg_required_before_payload(self):
|
|
for pin in ('', 'x'*64, '0'*64):
|
|
self.sftp.opened.clear()
|
|
with self.assertRaises(ValueError): f.fetch(self.sftp, self.destination, pin)
|
|
self.assertFalse(self.destination.exists())
|
|
self.assertTrue(all(name.endswith('/publication-receipt.json') for name in self.sftp.opened))
|
|
with self.assertRaisesRegex(ValueError, 'authority/scope'): f.fetch(self.sftp, self.destination, self.pin)
|
|
|
|
def test_forbidden_paths_and_binary_or_extra_source_fail_before_more_reads(self):
|
|
for name in ('../bad.json', 'evidence/x/../bad.json', 'evidence/C:/bad.json', 'evidence/CON.log',
|
|
'source-snapshot/unregistered.py', 'evidence/state.npz', 'evidence/queue.jsonl', 'evidence/extension-build/info.json'):
|
|
with self.subTest(name=name):
|
|
self.sftp, self.prereg, self.pin = fixture(); receipt = self.receipt()
|
|
receipt['files'].append(dict(path=name, bytes=1, sha256='a'*64)); self.resign(receipt)
|
|
with self.assertRaises(ValueError): self.fetch()
|
|
self.assertFalse(self.destination.exists()); self.assertEqual(len(self.sftp.opened), 1)
|
|
|
|
def test_case_prefix_collision_and_receipt_self_inventory_rejected(self):
|
|
for name in ('EVIDENCE/audit/final-status.json', 'evidence/audit', 'publication-receipt.json', 'fetch-verification.json'):
|
|
with self.subTest(name=name):
|
|
self.sftp, self.prereg, self.pin = fixture(); receipt = self.receipt()
|
|
receipt['files'].append(dict(path=name, bytes=1, sha256='a'*64)); self.resign(receipt)
|
|
with self.assertRaises(ValueError): self.fetch()
|
|
self.assertFalse(self.destination.exists())
|
|
|
|
def test_budget_is_hard_cap_and_cannot_be_raised(self):
|
|
with self.assertRaises(ValueError): self.fetch(max_total_bytes=f.MAX_TOTAL_BYTES + 1)
|
|
self.assertFalse(self.sftp.opened)
|
|
with self.assertRaisesRegex(ValueError, 'budget'): self.fetch(max_total_bytes=1)
|
|
self.assertFalse(self.destination.exists())
|
|
|
|
def test_manifest_checksum_link_checked_even_after_receipt_resigned(self):
|
|
name = 'evidence/checksums.sha256'; raw = self.sftp.files[f.REGISTRY_ROOT + '/' + name]
|
|
lines = raw.decode().splitlines(); lines[-1] = '0'*64 + ' manifest.json'; changed = ('\n'.join(lines)+'\n').encode()
|
|
self.change_selected(name, changed); receipt = self.receipt(); receipt['experiment_checksums_sha256'] = f.sha(changed); self.resign(receipt)
|
|
with self.assertRaisesRegex(ValueError, 'self-SHA'): self.fetch()
|
|
self.assertFalse(self.destination.exists())
|
|
|
|
def test_source_identity_and_index_crosslinks_not_just_receipt_flags(self):
|
|
for name, mutation in (('source-identity.json', 'model_source'), ('evidence-index.json', 'source')):
|
|
with self.subTest(name=name):
|
|
self.sftp, self.prereg, self.pin = fixture(); values = f.json_bytes(self.sftp.files[f.REGISTRY_ROOT + '/' + name])
|
|
values[0][mutation] = 'wrong/source.py'; self.change_selected(name, data(values))
|
|
with self.assertRaises(ValueError): self.fetch()
|
|
self.assertFalse(self.destination.exists())
|
|
|
|
def test_exact_omitted_and_selection_inventory_cannot_drop_evidence(self):
|
|
receipt = self.receipt(); receipt['experiment_payload_not_copied'].pop(); self.resign(receipt)
|
|
with self.assertRaisesRegex(ValueError, 'Omitted'): self.fetch()
|
|
self.assertFalse(self.destination.exists())
|
|
self.sftp, self.prereg, self.pin = fixture(); receipt = self.receipt()
|
|
receipt['files'] = [r for r in receipt['files'] if r['path'] != 'evidence/logs/readable.log']; self.resign(receipt)
|
|
with self.assertRaisesRegex(ValueError, 'exact sealed policy'): self.fetch()
|
|
|
|
def test_existing_or_alias_destination_denied_without_remote_reads(self):
|
|
self.destination.mkdir()
|
|
with self.assertRaisesRegex(ValueError, 'exists'): self.fetch()
|
|
self.assertFalse(self.sftp.opened)
|
|
with self.assertRaises(ValueError): f.check_destination(self.destination / '..' / 'other')
|
|
|
|
def test_remote_symlink_directory_and_file_denied(self):
|
|
for remote in (f.REGISTRY_ROOT, f.REGISTRY_ROOT + '/publication-receipt.json'):
|
|
self.sftp.symlinks.add(remote)
|
|
with self.assertRaisesRegex(ValueError, 'symlink'): self.fetch()
|
|
self.assertFalse(self.sftp.opened); self.sftp.symlinks.clear()
|
|
|
|
def test_payload_corruption_preserves_partial_and_prevents_retry(self):
|
|
remote = f.REGISTRY_ROOT + '/evidence/logs/readable.log'; self.sftp.files[remote] = b'x' * len(self.sftp.files[remote])
|
|
with self.assertRaisesRegex(ValueError, 'SHA'): self.fetch()
|
|
self.assertTrue((self.destination / 'fetch-failure.json').is_file())
|
|
self.assertFalse((self.destination / 'fetch-verification.json').exists())
|
|
with self.assertRaisesRegex(ValueError, 'exists'): self.fetch()
|
|
|
|
def test_remote_receipt_changed_during_download_fails_final_anchor(self):
|
|
def change(name):
|
|
if name.endswith('/source-snapshot/runtime_light.py'):
|
|
self.sftp.files[f.REGISTRY_ROOT + '/publication-receipt.json'] += b' '
|
|
self.sftp.hook = change
|
|
with self.assertRaises(ValueError): self.fetch()
|
|
self.assertTrue((self.destination / 'fetch-failure.json').exists())
|
|
self.assertFalse((self.destination / 'fetch-verification.json').exists())
|
|
|
|
def test_cli_host_trust_credentials_and_default_destination_are_explicit(self):
|
|
source = Path(f.__file__).read_text(encoding='utf-8')
|
|
self.assertIn('paramiko.RejectPolicy()', source)
|
|
self.assertIn('client.load_system_host_keys(str(args.known_hosts))', source)
|
|
self.assertIn('allow_agent=False, look_for_keys=False', source)
|
|
self.assertNotIn('AutoAddPolicy', source)
|
|
self.assertNotIn("add_argument('--password'", source)
|
|
self.assertEqual(f.DEFAULT_DESTINATION, Path(f.__file__).resolve().parents[2] / 'reports/phase4an-engineering-validation')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|