rezabarkhordary commited on
Commit
8523ebd
·
verified ·
1 Parent(s): bde98bf

Create sovereign_evidence_packager.py

Browse files
Files changed (1) hide show
  1. sovereign_evidence_packager.py +221 -0
sovereign_evidence_packager.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # sovereign_evidence_packager.py
3
+ import os
4
+ import json
5
+ import hashlib
6
+ import zipfile
7
+ from datetime import datetime, timezone
8
+ from typing import Any, Dict, Optional, List, Tuple
9
+
10
+
11
+ BUILD_FINGERPRINT_FILE = os.getenv("SOVEREIGN_BUILD_FINGERPRINT_FILE", "build_fingerprint.json")
12
+ SBOM_LITE_FILE = os.getenv("SOVEREIGN_SBOM_LITE_FILE", "sbom_lite.json")
13
+ POLICY_MANIFEST_DIR = os.getenv("SOVEREIGN_POLICY_MANIFEST_DIR", "policy_manifests")
14
+ EVIDENCE_PACKAGES_DIR = os.getenv("SOVEREIGN_EVIDENCE_PACKAGES_DIR", "evidence_packages")
15
+
16
+ AUDIT_LOG_FILE = os.getenv("SOVEREIGN_AUDIT_LOG_FILE", "sovereign_audit_log.jsonl")
17
+
18
+
19
+ def _utc_now_iso() -> str:
20
+ return datetime.now(timezone.utc).isoformat()
21
+
22
+
23
+ def _sha256_bytes(b: bytes) -> str:
24
+ return hashlib.sha256(b).hexdigest()
25
+
26
+
27
+ def _stable_json_bytes(obj: Any) -> bytes:
28
+ return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
29
+
30
+
31
+ def _file_sha256(path: str) -> Optional[str]:
32
+ try:
33
+ h = hashlib.sha256()
34
+ with open(path, "rb") as f:
35
+ for chunk in iter(lambda: f.read(1024 * 1024), b""):
36
+ h.update(chunk)
37
+ return h.hexdigest()
38
+ except Exception:
39
+ return None
40
+
41
+
42
+ def _safe_write_json(path: str, data: Any) -> None:
43
+ os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
44
+ tmp = path + ".tmp"
45
+ with open(tmp, "w", encoding="utf-8") as f:
46
+ json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True)
47
+ os.replace(tmp, path)
48
+
49
+
50
+ def ensure_build_fingerprint() -> None:
51
+ """
52
+ Capability #9:
53
+ - Build/Release fingerprint (hashes of key files)
54
+ - SBOM-lite (requirements snapshot)
55
+ Runs once at startup (safe to call multiple times).
56
+ """
57
+ # If already exists, don't overwrite unless forced
58
+ if os.path.exists(BUILD_FINGERPRINT_FILE) and os.path.exists(SBOM_LITE_FILE):
59
+ return
60
+
61
+ # Collect hashes of key project files if present
62
+ candidates = [
63
+ "app.py",
64
+ "sovereign_core.py",
65
+ "sovereign_ultra_layer.py",
66
+ "sovereign_infinity_layer.py",
67
+ "pilot_suite.py",
68
+ "report_generator.py",
69
+ "dockerfile",
70
+ "Dockerfile",
71
+ "requirements.txt",
72
+ ]
73
+
74
+ files_hashed = []
75
+ for name in candidates:
76
+ if os.path.exists(name) and os.path.isfile(name):
77
+ files_hashed.append({
78
+ "file": name,
79
+ "sha256": _file_sha256(name),
80
+ })
81
+
82
+ build_fp = {
83
+ "issued_at": _utc_now_iso(),
84
+ "build_id": os.getenv("SOVEREIGN_BUILD_ID", "UNSPECIFIED"),
85
+ "version": os.getenv("SOVEREIGN_VERSION", "UNSPECIFIED"),
86
+ "files": files_hashed,
87
+ }
88
+ _safe_write_json(BUILD_FINGERPRINT_FILE, build_fp)
89
+
90
+ # SBOM-lite: requirements snapshot
91
+ req_lines: List[str] = []
92
+ if os.path.exists("requirements.txt"):
93
+ try:
94
+ with open("requirements.txt", "r", encoding="utf-8") as f:
95
+ req_lines = [ln.strip() for ln in f.readlines() if ln.strip() and not ln.strip().startswith("#")]
96
+ except Exception:
97
+ req_lines = []
98
+
99
+ sbom_lite = {
100
+ "issued_at": _utc_now_iso(),
101
+ "type": "sbom-lite",
102
+ "requirements": req_lines,
103
+ "notes": "Lightweight dependency snapshot for procurement and assurance packs.",
104
+ }
105
+ _safe_write_json(SBOM_LITE_FILE, sbom_lite)
106
+
107
+
108
+ def write_policy_manifest(result: Dict[str, Any]) -> str:
109
+ """
110
+ Capability #8:
111
+ - Policy manifest snapshot (sealed hash)
112
+ """
113
+ os.makedirs(POLICY_MANIFEST_DIR, exist_ok=True)
114
+
115
+ decision_id = str(result.get("decision_id") or "unknown")
116
+ ts = str(result.get("timestamp") or _utc_now_iso()).replace(":", "-")
117
+ path = os.path.join(POLICY_MANIFEST_DIR, f"policy_manifest__{ts}__{decision_id}.json")
118
+
119
+ # Keep it minimal & procurement-friendly
120
+ manifest = {
121
+ "issued_at": _utc_now_iso(),
122
+ "decision_id": decision_id,
123
+ "authority_id": (result.get("authority") or {}).get("authority_id", "UNKNOWN"),
124
+ "root_authority_id": (result.get("authority") or {}).get("root_authority_id", "UNKNOWN"),
125
+ "jurisdiction_id": (result.get("authority") or {}).get("jurisdiction_id", "UNKNOWN"),
126
+ "license_id": (result.get("authority") or {}).get("license_id", "UNKNOWN"),
127
+ "enforcement": {
128
+ "authority_enforce_boundaries": os.getenv("SOVEREIGN_AUTHORITY_ENFORCE", "0") == "1",
129
+ },
130
+ "environment_policy": {
131
+ "allowed_envs": ["dev", "staging", "production", "restricted"],
132
+ "prod_pii_highrisk_action": "freeze_if_enforced",
133
+ },
134
+ "governance_states": result.get("governance_states", ["allow", "freeze", "block"]),
135
+ }
136
+
137
+ manifest_hash = _sha256_bytes(_stable_json_bytes(manifest))
138
+ manifest["manifest_hash_sha256"] = manifest_hash
139
+
140
+ _safe_write_json(path, manifest)
141
+ return path
142
+
143
+
144
+ def _read_audit_tail(max_lines: int = 200) -> Optional[str]:
145
+ """
146
+ Best-effort: include a small audit tail for the evidence pack.
147
+ """
148
+ if not os.path.exists(AUDIT_LOG_FILE):
149
+ return None
150
+ try:
151
+ with open(AUDIT_LOG_FILE, "r", encoding="utf-8") as f:
152
+ lines = f.readlines()[-max_lines:]
153
+ return "".join(lines)
154
+ except Exception:
155
+ return None
156
+
157
+
158
+ def export_evidence_package(result: Dict[str, Any], policy_manifest_path: Optional[str] = None) -> None:
159
+ """
160
+ Capability #10:
161
+ - Evidence package export (folder + zip)
162
+ """
163
+ os.makedirs(EVIDENCE_PACKAGES_DIR, exist_ok=True)
164
+
165
+ decision_id = str(result.get("decision_id") or "unknown")
166
+ ts = str(result.get("timestamp") or _utc_now_iso()).replace(":", "-")
167
+ pack_dir = os.path.join(EVIDENCE_PACKAGES_DIR, f"evidence__{ts}__{decision_id}")
168
+ os.makedirs(pack_dir, exist_ok=True)
169
+
170
+ # Write core artifacts
171
+ result_path = os.path.join(pack_dir, "result.json")
172
+ _safe_write_json(result_path, result)
173
+
174
+ # Copy policy manifest if provided
175
+ if policy_manifest_path and os.path.exists(policy_manifest_path):
176
+ try:
177
+ with open(policy_manifest_path, "r", encoding="utf-8") as f:
178
+ manifest = json.load(f)
179
+ _safe_write_json(os.path.join(pack_dir, "policy_manifest.json"), manifest)
180
+ except Exception:
181
+ pass
182
+
183
+ # Include build fingerprint and sbom-lite (if exists)
184
+ if os.path.exists(BUILD_FINGERPRINT_FILE):
185
+ try:
186
+ with open(BUILD_FINGERPRINT_FILE, "r", encoding="utf-8") as f:
187
+ build_fp = json.load(f)
188
+ _safe_write_json(os.path.join(pack_dir, "build_fingerprint.json"), build_fp)
189
+ except Exception:
190
+ pass
191
+
192
+ if os.path.exists(SBOM_LITE_FILE):
193
+ try:
194
+ with open(SBOM_LITE_FILE, "r", encoding="utf-8") as f:
195
+ sbom = json.load(f)
196
+ _safe_write_json(os.path.join(pack_dir, "sbom_lite.json"), sbom)
197
+ except Exception:
198
+ pass
199
+
200
+ # Best-effort include tail of audit log
201
+ audit_tail = _read_audit_tail()
202
+ if audit_tail:
203
+ try:
204
+ with open(os.path.join(pack_dir, "audit_tail.jsonl"), "w", encoding="utf-8") as f:
205
+ f.write(audit_tail)
206
+ except Exception:
207
+ pass
208
+
209
+ # Create zip
210
+ zip_path = pack_dir + ".zip"
211
+ try:
212
+ with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as z:
213
+ for root, _, files in os.walk(pack_dir):
214
+ for fn in files:
215
+ full = os.path.join(root, fn)
216
+ rel = os.path.relpath(full, pack_dir)
217
+ z.write(full, arcname=rel)
218
+ except Exception:
219
+ # If zip fails, folder still exists
220
+ pass
221
+