|
|
| |
| import os |
| import json |
| import hashlib |
| import zipfile |
| from datetime import datetime, timezone |
| from typing import Any, Dict, Optional, List, Tuple |
|
|
|
|
| BUILD_FINGERPRINT_FILE = os.getenv("SOVEREIGN_BUILD_FINGERPRINT_FILE", "build_fingerprint.json") |
| SBOM_LITE_FILE = os.getenv("SOVEREIGN_SBOM_LITE_FILE", "sbom_lite.json") |
| POLICY_MANIFEST_DIR = os.getenv("SOVEREIGN_POLICY_MANIFEST_DIR", "policy_manifests") |
| EVIDENCE_PACKAGES_DIR = os.getenv("SOVEREIGN_EVIDENCE_PACKAGES_DIR", "evidence_packages") |
|
|
| AUDIT_LOG_FILE = os.getenv("SOVEREIGN_AUDIT_LOG_FILE", "sovereign_audit_log.jsonl") |
|
|
|
|
| def _utc_now_iso() -> str: |
| return datetime.now(timezone.utc).isoformat() |
|
|
|
|
| def _sha256_bytes(b: bytes) -> str: |
| return hashlib.sha256(b).hexdigest() |
|
|
|
|
| def _stable_json_bytes(obj: Any) -> bytes: |
| return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") |
|
|
|
|
| def _file_sha256(path: str) -> Optional[str]: |
| try: |
| h = hashlib.sha256() |
| with open(path, "rb") as f: |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): |
| h.update(chunk) |
| return h.hexdigest() |
| except Exception: |
| return None |
|
|
|
|
| def _safe_write_json(path: str, data: Any) -> None: |
| os.makedirs(os.path.dirname(path) or ".", exist_ok=True) |
| tmp = path + ".tmp" |
| with open(tmp, "w", encoding="utf-8") as f: |
| json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True) |
| os.replace(tmp, path) |
|
|
|
|
| def ensure_build_fingerprint() -> None: |
| """ |
| Capability #9: |
| - Build/Release fingerprint (hashes of key files) |
| - SBOM-lite (requirements snapshot) |
| Runs once at startup (safe to call multiple times). |
| """ |
| |
| if os.path.exists(BUILD_FINGERPRINT_FILE) and os.path.exists(SBOM_LITE_FILE): |
| return |
|
|
| |
| candidates = [ |
| "app.py", |
| "sovereign_core.py", |
| "sovereign_ultra_layer.py", |
| "sovereign_infinity_layer.py", |
| "pilot_suite.py", |
| "report_generator.py", |
| "dockerfile", |
| "Dockerfile", |
| "requirements.txt", |
| ] |
|
|
| files_hashed = [] |
| for name in candidates: |
| if os.path.exists(name) and os.path.isfile(name): |
| files_hashed.append({ |
| "file": name, |
| "sha256": _file_sha256(name), |
| }) |
|
|
| build_fp = { |
| "issued_at": _utc_now_iso(), |
| "build_id": os.getenv("SOVEREIGN_BUILD_ID", "UNSPECIFIED"), |
| "version": os.getenv("SOVEREIGN_VERSION", "UNSPECIFIED"), |
| "files": files_hashed, |
| } |
| _safe_write_json(BUILD_FINGERPRINT_FILE, build_fp) |
|
|
| |
| req_lines: List[str] = [] |
| if os.path.exists("requirements.txt"): |
| try: |
| with open("requirements.txt", "r", encoding="utf-8") as f: |
| req_lines = [ln.strip() for ln in f.readlines() if ln.strip() and not ln.strip().startswith("#")] |
| except Exception: |
| req_lines = [] |
|
|
| sbom_lite = { |
| "issued_at": _utc_now_iso(), |
| "type": "sbom-lite", |
| "requirements": req_lines, |
| "notes": "Lightweight dependency snapshot for procurement and assurance packs.", |
| } |
| _safe_write_json(SBOM_LITE_FILE, sbom_lite) |
|
|
|
|
| def write_policy_manifest(result: Dict[str, Any]) -> str: |
| """ |
| Capability #8: |
| - Policy manifest snapshot (sealed hash) |
| """ |
| os.makedirs(POLICY_MANIFEST_DIR, exist_ok=True) |
|
|
| decision_id = str(result.get("decision_id") or "unknown") |
| ts = str(result.get("timestamp") or _utc_now_iso()).replace(":", "-") |
| path = os.path.join(POLICY_MANIFEST_DIR, f"policy_manifest__{ts}__{decision_id}.json") |
|
|
| |
| manifest = { |
| "issued_at": _utc_now_iso(), |
| "decision_id": decision_id, |
| "authority_id": (result.get("authority") or {}).get("authority_id", "UNKNOWN"), |
| "root_authority_id": (result.get("authority") or {}).get("root_authority_id", "UNKNOWN"), |
| "jurisdiction_id": (result.get("authority") or {}).get("jurisdiction_id", "UNKNOWN"), |
| "license_id": (result.get("authority") or {}).get("license_id", "UNKNOWN"), |
| "enforcement": { |
| "authority_enforce_boundaries": os.getenv("SOVEREIGN_AUTHORITY_ENFORCE", "0") == "1", |
| }, |
| "environment_policy": { |
| "allowed_envs": ["dev", "staging", "production", "restricted"], |
| "prod_pii_highrisk_action": "freeze_if_enforced", |
| }, |
| "governance_states": result.get("governance_states", ["allow", "freeze", "block"]), |
| } |
|
|
| manifest_hash = _sha256_bytes(_stable_json_bytes(manifest)) |
| manifest["manifest_hash_sha256"] = manifest_hash |
|
|
| _safe_write_json(path, manifest) |
| return path |
|
|
|
|
| def _read_audit_tail(max_lines: int = 200) -> Optional[str]: |
| """ |
| Best-effort: include a small audit tail for the evidence pack. |
| """ |
| if not os.path.exists(AUDIT_LOG_FILE): |
| return None |
| try: |
| with open(AUDIT_LOG_FILE, "r", encoding="utf-8") as f: |
| lines = f.readlines()[-max_lines:] |
| return "".join(lines) |
| except Exception: |
| return None |
|
|
|
|
| def export_evidence_package(result: Dict[str, Any], policy_manifest_path: Optional[str] = None) -> None: |
| """ |
| Capability #10: |
| - Evidence package export (folder + zip) |
| """ |
| os.makedirs(EVIDENCE_PACKAGES_DIR, exist_ok=True) |
|
|
| decision_id = str(result.get("decision_id") or "unknown") |
| ts = str(result.get("timestamp") or _utc_now_iso()).replace(":", "-") |
| pack_dir = os.path.join(EVIDENCE_PACKAGES_DIR, f"evidence__{ts}__{decision_id}") |
| os.makedirs(pack_dir, exist_ok=True) |
|
|
| |
| result_path = os.path.join(pack_dir, "result.json") |
| _safe_write_json(result_path, result) |
|
|
| |
| if policy_manifest_path and os.path.exists(policy_manifest_path): |
| try: |
| with open(policy_manifest_path, "r", encoding="utf-8") as f: |
| manifest = json.load(f) |
| _safe_write_json(os.path.join(pack_dir, "policy_manifest.json"), manifest) |
| except Exception: |
| pass |
|
|
| |
| if os.path.exists(BUILD_FINGERPRINT_FILE): |
| try: |
| with open(BUILD_FINGERPRINT_FILE, "r", encoding="utf-8") as f: |
| build_fp = json.load(f) |
| _safe_write_json(os.path.join(pack_dir, "build_fingerprint.json"), build_fp) |
| except Exception: |
| pass |
|
|
| if os.path.exists(SBOM_LITE_FILE): |
| try: |
| with open(SBOM_LITE_FILE, "r", encoding="utf-8") as f: |
| sbom = json.load(f) |
| _safe_write_json(os.path.join(pack_dir, "sbom_lite.json"), sbom) |
| except Exception: |
| pass |
|
|
| |
| audit_tail = _read_audit_tail() |
| if audit_tail: |
| try: |
| with open(os.path.join(pack_dir, "audit_tail.jsonl"), "w", encoding="utf-8") as f: |
| f.write(audit_tail) |
| except Exception: |
| pass |
|
|
| |
| zip_path = pack_dir + ".zip" |
| try: |
| with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as z: |
| for root, _, files in os.walk(pack_dir): |
| for fn in files: |
| full = os.path.join(root, fn) |
| rel = os.path.relpath(full, pack_dir) |
| z.write(full, arcname=rel) |
| except Exception: |
| |
| pass |
|
|
|
|