98 lines
5.9 KiB
Python
98 lines
5.9 KiB
Python
"""Verify a preserved failed preparation; never run its executable or helpers."""
|
|
from pathlib import Path, PurePosixPath
|
|
from datetime import datetime
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import stat
|
|
import zipfile
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
COMMIT = "b743a10d51032dc1655b94fed5e49fbbbcee83f4"
|
|
FILES = {".gitattributes", "README.md", "verify.py", "generation.json", "generation.stdout.log", "generation.stderr.log"}
|
|
|
|
|
|
def need(ok, message):
|
|
if not ok:
|
|
raise ValueError(message)
|
|
|
|
|
|
def pin(data):
|
|
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
|
|
|
|
|
def parsed(data):
|
|
def unique(items):
|
|
result = {}
|
|
for key, value in items:
|
|
need(key not in result, "duplicate JSON key")
|
|
result[key] = value
|
|
return result
|
|
return json.loads(data, object_pairs_hook=unique)
|
|
|
|
|
|
def safe(name):
|
|
return isinstance(name, str) and bool(name) and not PurePosixPath(name).is_absolute() and all(c not in name for c in ("\\", ":")) and all(ord(c) >= 32 for c in name) and all(p not in ("", ".", "..") for p in name.split("/"))
|
|
|
|
|
|
def verify(root, checks, source):
|
|
pins = parsed((root / "files.json").read_bytes())
|
|
need(set(pins) == FILES and {p.name for p in root.iterdir()} == FILES | {"files.json"}, "exact failed-preparation files")
|
|
for name, expected in pins.items():
|
|
need((root / name).is_file() and not (root / name).is_symlink() and pin((root / name).read_bytes()) == expected, "preserved pin: " + name)
|
|
generation = parsed((root / "generation.json").read_bytes())
|
|
need(generation["exitCode"] == 3221225781, "original missing-DLL status")
|
|
for stream in ("stdout", "stderr"):
|
|
data = (root / ("generation." + stream + ".log")).read_bytes()
|
|
need(not data and pin(data) == generation[stream], "original empty generation stream")
|
|
recorded = generation["checker"]
|
|
outer = parsed((checks / "files.json").read_bytes())
|
|
need(set(outer) == {"README.md", "prepare.py", "verify.py", "manifest.json", "evidence.zip"}, "checks package inventory")
|
|
for name, expected in outer.items():
|
|
need(pin((checks / name).read_bytes()) == expected, "checks outer pin")
|
|
manifest = parsed((checks / "manifest.json").read_bytes())
|
|
need(manifest["kind"] == "multiselect-cargo-gates", "checks kind")
|
|
with zipfile.ZipFile(checks / "evidence.zip") as archive:
|
|
need(len(archive.namelist()) == len(set(archive.namelist())) and set(archive.namelist()) == set(manifest["members"]), "checks archive inventory")
|
|
for row in archive.infolist():
|
|
need(safe(row.filename) and not row.is_dir() and not stat.S_ISLNK(row.external_attr >> 16), "unsafe checks archive member")
|
|
def raw(name):
|
|
member = manifest["logicalFiles"]["cargo/native-build-2/" + name]
|
|
data = archive.read(member)
|
|
need(pin(data) == manifest["members"][member], "checks member pin")
|
|
return data
|
|
result_bytes, input_bytes = raw("result.json"), raw("inputs.json")
|
|
result = parsed(result_bytes)
|
|
need(recorded["receiptPin"] == pin(result_bytes), "exact checker build receipt")
|
|
need(recorded["inputs"] == result["inputs"] == pin(input_bytes), "same exact checker input map")
|
|
need(len(parsed(input_bytes)) == result["inputCount"] == 742, "complete Process source count")
|
|
need(result["head"] == COMMIT and result["exitCode"] == 0 and result["inputsUnchanged"] is True, "successful b743 checker build")
|
|
need(recorded["receipt"].replace("\\", "/") == result["cwd"].replace("\\", "/") + "/.local/process-group-gates/native-build-2/result.json", "original receipt path")
|
|
rows = [row for row in result["artifacts"] if row["cargo"]["target"]["name"] == "process_multiselect_native"]
|
|
need(len(rows) == 1 and rows[0]["readback"] == recorded["pin"] and rows[0]["cargo"]["executable"] == recorded["binary"] and rows[0]["cargo"]["profile"]["test"] is False, "exact native checker artifact")
|
|
stdout = raw("stdout.log")
|
|
need(pin(stdout) == result["stdout"], "checker build log pin")
|
|
cargo_rows = [parsed(line) for line in stdout.splitlines() if line.startswith(b"{")]
|
|
need(rows[0]["cargo"] in cargo_rows, "actual Cargo output artifact")
|
|
command = generation["command"]
|
|
need(len(command) == 3 and command[:2] == [recorded["binary"], "generate"] and command[2].replace("\\", "/") == result["cwd"].replace("\\", "/") + "/.local/process-multiselect-native/run-2/fixture", "exact failed generation command")
|
|
times = [datetime.fromisoformat(t) for t in (result["start"], result["finish"], generation["start"], generation["finish"])]
|
|
need(all(t.utcoffset() is not None for t in times) and times == sorted(times), "build then failed generation chronology")
|
|
source_pins = parsed((source / "files.json").read_bytes())
|
|
for name in ("manifest.json", "source-core-3.json"):
|
|
need(pin((source / name).read_bytes()) == source_pins[name], "source proof pin")
|
|
spec = parsed((source / "manifest.json").read_bytes())["runs"]["core-3"]
|
|
proof_bytes = (source / "source-core-3.json").read_bytes()
|
|
proof = parsed(proof_bytes)
|
|
need(spec["receiptPin"] == pin(proof_bytes) and spec["commit"] == proof["commit"] == COMMIT and spec["inputMap"] == proof["inputMap"] == recorded["inputs"], "same b743 source proof")
|
|
return {"preparationExit": generation["exitCode"], "hexExit": hex(generation["exitCode"]), "checkerBuild": "native-build-2", "sourceCommit": COMMIT, "guiLaunch": False}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("package", nargs="?", type=Path, default=HERE)
|
|
parser.add_argument("--checks", type=Path)
|
|
parser.add_argument("--source", type=Path)
|
|
args = parser.parse_args()
|
|
print("PASS preserved failed-preparation consistency:", verify(args.package, args.checks or args.package.parent / "checks", args.source or args.package.parent / "source"))
|