33 lines
1.4 KiB
Python
33 lines
1.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify immutable historical manifests after publication wording changes.
|
|
|
|
WHY: documentation snapshots preserve evidence without rewriting sealed hashes.
|
|
CONTRACT: explicit mappings replace only files recorded in the source manifest.
|
|
INVARIANT: verification is read-only; every original digest must still match.
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument('lab', type=Path)
|
|
p.add_argument('--snapshot-map', type=Path, required=True)
|
|
a = p.parse_args()
|
|
mapping = json.loads(a.snapshot_map.read_text())
|
|
seen = set()
|
|
for kind, count, expected in [
|
|
('source', 1892, '5ad64eaf0aec2290689c46b7754eb4563980396ba2a06e852d1c8e903086f138'),
|
|
('evidence', 143, '85d034f4075436bbdda302afdfa94955365aa51adcfb7e8b04ed6bb28377f649')]:
|
|
manifest = a.lab/'results'/(kind+'-sha256.txt')
|
|
assert hashlib.sha256(manifest.read_bytes()).hexdigest() == expected
|
|
lines = manifest.read_text().splitlines()
|
|
assert len(lines) == count
|
|
for line in lines:
|
|
digest, name = line.split(' ', 1)
|
|
if kind == 'source' and name in mapping:
|
|
seen.add(name)
|
|
name = mapping[name]
|
|
assert hashlib.sha256(Path(name).read_bytes()).hexdigest() == digest, name
|
|
print(kind, count, expected, 'PASS')
|
|
assert seen == set(mapping), 'mapping must contain only sealed source entries'
|