docs: publish saved-view group selection verification and native evidence
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"""Compare captured input maps to exact named Git trees; no builds or checkouts."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import zipfile
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
COMMITS = {"desktop-4": "12a7352ddef5f69a144ead8219a50916233d63a9",
|
||||
"desktop-5": "f6a6010baed9e6164a1fcc7ecd58256a562827d7"}
|
||||
COMPARISON = "Exact committed Git blobs equal every captured input; same complete filtered inventory"
|
||||
FILES = {".gitattributes", "verify.py", "README.md", "verify-committed-inputs.py",
|
||||
"committed-desktop-4.json", "committed-desktop-5.json"}
|
||||
|
||||
|
||||
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: " + 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 in name and ":" not in name
|
||||
and not PurePosixPath(name).is_absolute()
|
||||
and all(part not in ("", ".", "..") for part in name.split("/")))
|
||||
|
||||
|
||||
def relevant(path):
|
||||
return path in ("Cargo.toml", "Cargo.lock", "rust-toolchain.toml") or path.startswith(
|
||||
("crates/", "build/", ".cargo/", "scripts/", "docs/", "examples/", "harness/fixtures/", "harness/prototypes/"))
|
||||
|
||||
|
||||
def git(repo, *arguments, data=None):
|
||||
environment = dict(os.environ, GIT_NO_LAZY_FETCH="1")
|
||||
return subprocess.run(["git", *arguments], cwd=repo, input=data, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, check=True, env=environment).stdout
|
||||
|
||||
|
||||
def compare_commit(repo, commit, inputs):
|
||||
need(git(repo, "rev-parse", "--verify", commit + "^{commit}").decode().strip() == commit, "named commit unavailable")
|
||||
entries = git(repo, "ls-tree", "-r", "--full-tree", "-z", commit).split(b"\0")
|
||||
blobs = {}
|
||||
for entry in filter(None, entries):
|
||||
header, path_bytes = entry.split(b"\t", 1)
|
||||
path = path_bytes.decode("utf-8")
|
||||
if relevant(path):
|
||||
_mode, kind, oid = header.split()
|
||||
need(kind == b"blob", "relevant non-blob tree entry: " + path)
|
||||
need(path not in blobs, "duplicate tree path")
|
||||
blobs[path] = oid
|
||||
need(set(blobs) == set(inputs), "complete filtered Git inventory differs; missing="
|
||||
+ repr(sorted(set(inputs) - set(blobs))) + "; additional=" + repr(sorted(set(blobs) - set(inputs))))
|
||||
# Object IDs avoid path quoting and commit:path interpretation in batch input.
|
||||
paths = sorted(blobs)
|
||||
data = git(repo, "cat-file", "--batch", data=b"".join(blobs[path] + b"\n" for path in paths))
|
||||
offset, actual = 0, {}
|
||||
for path in paths:
|
||||
end = data.index(b"\n", offset)
|
||||
oid, kind, size_bytes = data[offset:end].split()
|
||||
size = int(size_bytes)
|
||||
need(oid == blobs[path] and kind == b"blob" and size >= 0, "Git batch object relationship")
|
||||
content = data[end + 1:end + 1 + size]
|
||||
need(len(content) == size and data[end + 1 + size:end + 2 + size] == b"\n", "Git batch framing")
|
||||
actual[path] = pin(content)
|
||||
offset = end + 2 + size
|
||||
need(offset == len(data), "trailing Git batch bytes")
|
||||
different = [path for path in paths if actual[path] != inputs[path]]
|
||||
need(not different, "captured bytes differ from named commit: " + repr(different))
|
||||
|
||||
|
||||
def packaged(checks, runs):
|
||||
files = parsed((checks / "files.json").read_bytes())
|
||||
need(set(files) == {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}, "checks outer inventory")
|
||||
for name, expected in files.items():
|
||||
need(pin((checks / name).read_bytes()) == expected, "checks outer pin: " + name)
|
||||
manifest = parsed((checks / "manifest.json").read_bytes())
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "multiselect-cargo-gates", "checks manifest kind")
|
||||
captured = {}
|
||||
with zipfile.ZipFile(checks / "evidence.zip") as archive:
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(manifest["members"])
|
||||
and all(safe(name) for name in names), "checks exact safe members")
|
||||
def raw(logical):
|
||||
member = manifest["logicalFiles"][logical]
|
||||
data = archive.read(member)
|
||||
need(pin(data) == manifest["members"][member], "checks member pin: " + logical)
|
||||
return data
|
||||
for run in runs:
|
||||
prefix = "cargo/" + run + "/"
|
||||
result = parsed(raw(prefix + "result.json"))
|
||||
spec = manifest["runs"][run]
|
||||
need(all(result[key] == spec[key] for key in ("command", "head", "exitCode", "inputCount")), run + ": checks run relationships")
|
||||
captured[run] = (raw(prefix + "inputs.json"), result)
|
||||
return captured
|
||||
|
||||
|
||||
def verify(repo, captured):
|
||||
outer = parsed((HERE / "files.json").read_bytes())
|
||||
need(set(outer) == FILES, "source evidence file inventory")
|
||||
for name, expected in outer.items():
|
||||
need(pin((HERE / name).read_bytes()) == expected, "source evidence pin: " + name)
|
||||
for run, commit in COMMITS.items():
|
||||
receipt = parsed((HERE / ("committed-" + run + ".json")).read_bytes())
|
||||
need(receipt["gate"] == run and receipt["commit"] == commit
|
||||
and receipt["comparison"] == COMPARISON, run + ": historical identity relationship")
|
||||
map_bytes, result = captured[run]
|
||||
inputs = parsed(map_bytes)
|
||||
need(pin(map_bytes) == receipt["inputMap"] == result["inputs"], run + ": exact captured input map")
|
||||
need(result["exitCode"] == 0 and result["inputsUnchanged"] is True, run + ": accepted unchanged gate")
|
||||
need(len(inputs) == receipt["inputCount"] == result["inputCount"], run + ": input counts")
|
||||
need(all(safe(path) and relevant(path) for path in inputs), run + ": input scope")
|
||||
need(sum(path.startswith("crates/vendor/iced_widget/") for path in inputs) == 71, run + ": vendor count")
|
||||
compare_commit(repo, commit, inputs)
|
||||
print("PASS exact Git tree/blobs:", run, commit, len(inputs), "captured inputs")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--repo", type=Path, required=True, help="repository with both named commits already present")
|
||||
source = parser.add_mutually_exclusive_group(required=True)
|
||||
source.add_argument("--checks", type=Path, help="portable checks package directory")
|
||||
source.add_argument("--raw-gates", type=Path, help="optional preparation mode: raw gate directory, not a packaged-evidence verification")
|
||||
args = parser.parse_args()
|
||||
if args.checks:
|
||||
captured = packaged(args.checks, COMMITS)
|
||||
else:
|
||||
captured = {run: ((args.raw_gates / run / "inputs.json").read_bytes(),
|
||||
parsed((args.raw_gates / run / "result.json").read_bytes())) for run in COMMITS}
|
||||
verify(args.repo, captured)
|
||||
print("Source identity only; no build, checkout, helper execution or native acceptance claim.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user