TheAiCollectiveART commited on
Commit
39eaa18
·
verified ·
1 Parent(s): c377925

fix(crypto/ci): repair Keccak-256 KAT, purge claims_audit pseudo-inferences, enforce boolean release gate, package zymatica_cli

Browse files
tools/ten_out_of_ten/evidence_manifest.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright © 2026 Zymatica
3
+ # SPDX-License-Identifier: LicenseRef-Zymatica-Covenant-2.0
4
+ # See LICENSE for terms.
5
+ """Create a cryptographic manifest for a Zymatica evidence directory with source tree binding."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import hashlib
11
+ import json
12
+ import os
13
+ import platform
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ from datetime import datetime, timezone
18
+ from pathlib import Path
19
+
20
+
21
+ def sha256_file(path: Path) -> str:
22
+ h = hashlib.sha256()
23
+ with path.open("rb") as f:
24
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
25
+ h.update(chunk)
26
+ return h.hexdigest()
27
+
28
+
29
+ def compute_source_tree_hash(root: Path) -> str:
30
+ """Compute deterministic SHA-256 tree hash over tracked versioned source files using git ls-files."""
31
+ h = hashlib.sha256()
32
+ try:
33
+ res = subprocess.run(["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True)
34
+ tracked_files = res.stdout.splitlines()
35
+ except Exception:
36
+ # Fallback if git not available
37
+ tracked_files = []
38
+
39
+ skip_prefixes = ("evidence/", "target/", "node_modules/", "build/", ".git/")
40
+ for rel_str in sorted(tracked_files):
41
+ if rel_str.startswith(skip_prefixes):
42
+ continue
43
+ p = root / rel_str
44
+ if p.is_file():
45
+ h.update(rel_str.encode("utf-8"))
46
+ h.update(sha256_file(p).encode("utf-8"))
47
+ return h.hexdigest()
48
+
49
+
50
+ def command_output(command: list[str], cwd: Path) -> str | None:
51
+ env = os.environ.copy()
52
+ cargo_bin = os.path.expanduser("~/.cargo/bin")
53
+ if os.path.isdir(cargo_bin) and cargo_bin not in env.get("PATH", ""):
54
+ env["PATH"] = f"{cargo_bin}{os.pathsep}{env.get('PATH', '')}"
55
+ cmd = list(command)
56
+ exe = shutil.which(cmd[0], path=env.get("PATH"))
57
+ if exe:
58
+ cmd[0] = exe
59
+ try:
60
+ result = subprocess.run(cmd, cwd=cwd, env=env, text=True, capture_output=True, check=True)
61
+ return result.stdout.strip()
62
+ except Exception:
63
+ return None
64
+
65
+
66
+ def main() -> int:
67
+ parser = argparse.ArgumentParser()
68
+ parser.add_argument("evidence_dir", type=Path)
69
+ parser.add_argument("--repo", type=Path, default=Path.cwd())
70
+ parser.add_argument("--output", type=Path)
71
+ args = parser.parse_args()
72
+ repo = args.repo.resolve()
73
+ evidence_dir = args.evidence_dir.resolve()
74
+ if not evidence_dir.is_dir():
75
+ raise SystemExit(f"not a directory: {evidence_dir}")
76
+
77
+ files = []
78
+ for path in sorted(p for p in evidence_dir.rglob("*") if p.is_file()):
79
+ if args.output and path.resolve() == args.output.resolve():
80
+ continue
81
+ if path.name in {"MANIFEST.json", "SHA256SUMS", "release_attestation.json"}:
82
+ continue
83
+ files.append(
84
+ {
85
+ "path": path.relative_to(evidence_dir).as_posix(),
86
+ "size": path.stat().st_size,
87
+ "sha256": sha256_file(path),
88
+ }
89
+ )
90
+
91
+ source_tree_sha = compute_source_tree_hash(repo)
92
+ git_head = command_output(["git", "rev-parse", "HEAD"], repo)
93
+ git_tree = command_output(["git", "rev-parse", "HEAD^{tree}"], repo)
94
+
95
+ payload = {
96
+ "schema": "zymatica.evidence-manifest.v2",
97
+ "created_utc": datetime.now(timezone.utc).isoformat(),
98
+ "source_commit_sha": git_head,
99
+ "source_git_tree_sha": git_tree,
100
+ "source_tree_sha256": source_tree_sha,
101
+ "git_head": git_head,
102
+ "git_tree": git_tree,
103
+ "git_status_porcelain": command_output(["git", "status", "--porcelain"], repo),
104
+ "rustc": command_output(["rustc", "--version"], repo),
105
+ "cargo": command_output(["cargo", "--version"], repo),
106
+ "python": sys.version,
107
+ "platform": platform.platform(),
108
+ "files": files,
109
+ }
110
+ output = args.output or (evidence_dir / "MANIFEST.json")
111
+ output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
112
+ print(output)
113
+ return 0
114
+
115
+
116
+ if __name__ == "__main__":
117
+ raise SystemExit(main())