AI-Sovereign-sentinel / sovereign_core.py
rezabarkhordary's picture
Update sovereign_core.py
de79e93 verified
Raw
History Blame
19.3 kB
import os
import json
import time
import uuid
import hashlib
from typing import Optional, List, Dict, Any, Tuple
# ---------------------------------------------------------------------
# Core identifiers
# ---------------------------------------------------------------------
ENGINE_NAME = "AI_Sovereign_Sentinel_Core_v1"
AUTHORITY_NAME = "DataClear Sovereign Authority"
SOVEREIGN_VERSION = "1.2-gov-ready"
# Centralised audit log path (JSONL)
AUDIT_LOG_FILE = "sovereign_audit_log.jsonl"
# ---------------------------------------------------------------------
# Fingerprinting
# ---------------------------------------------------------------------
class SovereignFingerprint:
"""
Minimal fingerprint generator for AI Sovereign Sentinel.
Creates a JSON file with engine, fingerprint and issued_at.
"""
def __init__(self, engine: str, output: str = "sovereign_fingerprint.json"):
self.engine = engine
self.output = output
def _build(self) -> Dict[str, Any]:
issued_at = int(time.time())
base = f"{self.engine}|{issued_at}"
fp_hash = hashlib.sha256(base.encode()).hexdigest()
return {
"engine": self.engine,
"fingerprint": fp_hash,
"issued_at": issued_at,
"version": "1.0",
}
def export(self) -> Tuple[str, str]:
fp = self._build()
with open(self.output, "w", encoding="utf-8") as f:
json.dump(fp, f, indent=4)
return self.output, fp["fingerprint"]
# ---------------------------------------------------------------------
# Engine registry / validator
# ---------------------------------------------------------------------
class SovereignValidator:
"""
Simple registry to bind engine name → fingerprint string.
Stored in sovereign_registry.json
"""
def __init__(self, path: str = "sovereign_registry.json"):
self.path = path
if os.path.exists(self.path):
with open(self.path, "r", encoding="utf-8") as f:
self.registry = json.load(f)
else:
self.registry = {}
def register(self, engine: str, fingerprint: str) -> bool:
self.registry[engine] = {
"fingerprint": fingerprint,
"registered_at": int(time.time()),
}
with open(self.path, "w", encoding="utf-8") as f:
json.dump(self.registry, f, indent=4)
return True
def validate(self, engine: str, fingerprint: str) -> bool:
entry = self.registry.get(engine)
if not entry:
return False
return entry.get("fingerprint") == fingerprint
# ---------------------------------------------------------------------
# Attestation
# ---------------------------------------------------------------------
def attestation_signature(att: dict) -> str:
"""
Unified, stable signature generation for attestation objects.
"""
base = (
f"{att['attestation_id']}"
f"|{att['issued_at']}"
f"|{att['fingerprint']}"
f"|{att['issuer']}"
f"|{att['version']}"
)
return hashlib.sha256(base.encode()).hexdigest()
def generate_attestation(fp) -> Dict[str, Any]:
"""
Create a signed attestation for a given fingerprint (dict or string).
"""
if isinstance(fp, dict):
fp_value = fp.get("fingerprint", "")
else:
fp_value = fp
att: Dict[str, Any] = {
"attestation_id": str(uuid.uuid4()),
"issued_at": int(time.time()),
"fingerprint": fp_value,
"issuer": AUTHORITY_NAME,
"version": "1.0",
}
att["signature"] = attestation_signature(att)
return att
def verify_attestation(att: dict) -> bool:
expected = attestation_signature(att)
return expected == att.get("signature")
# ---------------------------------------------------------------------
# Lineage + Deployment binding (multi-model metadata ready)
# ---------------------------------------------------------------------
def build_lineage_record(
engine: str,
fingerprint: str,
parent_model: Optional[str] = None,
model_version: str = "1.0",
data_tags: Optional[List[str]] = None,
notes: Optional[str] = None,
provider: Optional[str] = None,
model_family: Optional[str] = None,
) -> Dict[str, Any]:
"""
Create a cryptographically-bound lineage record for a model.
Hash formula (ثابت و قابل بازتولید):
payload = f"{engine}|{fingerprint}|{model_version}|{parent_model or ''}|{created_at}"
"""
created_at = int(time.time())
core_parent = parent_model or ""
payload = f"{engine}|{fingerprint}|{model_version}|{core_parent}|{created_at}"
lineage_hash = hashlib.sha256(payload.encode()).hexdigest()
return {
"lineage_id": str(uuid.uuid4()),
"engine": engine,
"fingerprint": fingerprint,
"parent_model": parent_model,
"model_version": model_version,
"data_tags": data_tags or [],
"notes": notes,
"provider": provider,
"model_family": model_family,
"created_at": created_at,
"lineage_hash": lineage_hash,
}
# ---------------------------------------------------------------------
# Simple risk profile (env + data tags)
# ---------------------------------------------------------------------
def compute_risk_profile(
data_tags: Optional[List[str]] = None,
deployment_env: Optional[str] = None,
) -> Dict[str, Any]:
"""
Very simple, rule-based risk scoring so that certificate
carries a usable risk_level and risk_score.
"""
tags = [t.lower() for t in (data_tags or [])]
env = (deployment_env or "").lower().strip()
score = 0
threat_flags: List[str] = []
# Data-sensitivity
sensitive_keywords = ["pii", "health", "healthcare", "finance", "bank", "payments"]
for kw in sensitive_keywords:
if any(kw in t for t in tags):
score += 25
threat_flags.append(f"sensitive_data:{kw}")
break
# Internal / confidential
if any("internal" in t or "confidential" in t for t in tags):
score += 15
threat_flags.append("internal_confidential")
# Environment-based
if env in ["prod", "production", "live"]:
score += 40
threat_flags.append("environment:production")
elif env in ["staging", "preprod", "test"]:
score += 20
threat_flags.append("environment:staging")
elif env:
score += 10
threat_flags.append(f"environment:{env}")
# Cap score at 100
score = max(0, min(score, 100))
if score >= 70:
level = "HIGH"
elif score >= 40:
level = "MEDIUM"
else:
level = "LOW"
return {
"level": level,
"score": score,
"threat_flags": threat_flags,
}
# ---------------------------------------------------------------------
# Compute-efficiency scoring (approximate)
# ---------------------------------------------------------------------
def estimate_compute_cost(
model_name: str,
tokens: int,
provider: Optional[str] = None,
) -> Dict[str, Any]:
"""
Tiny heuristic for 'compute efficiency' the government is obsessed with.
We approximate per-token cost by model family.
"""
name = (model_name or "").lower()
provider_norm = (provider or "").lower()
if any(k in name for k in ["gpt-4", "claude-3-opus", "gemini-1.5-pro"]) or "frontier" in name:
base_cost = 3.0
family = "frontier"
elif any(k in name for k in ["gpt-4o-mini", "gpt-3.5", "sonnet", "llama-3", "mistral"]):
base_cost = 1.0
family = "general"
else:
base_cost = 0.2
family = "narrow"
estimated_units = base_cost * tokens
if family == "narrow":
band = "HIGH_EFFICIENCY"
elif family == "general":
band = "BALANCED"
else:
band = "HIGH_COMPUTE"
return {
"provider": provider_norm or None,
"model_name": model_name,
"family": family,
"estimated_compute_units": estimated_units,
"efficiency_band": band,
"tokens": tokens,
}
# ---------------------------------------------------------------------
# Policy Engine (standard & custom)
# ---------------------------------------------------------------------
class PolicyEngine:
"""
Simple, pluggable policy engine.
Provides:
- standard safety templates (LLM safety / restriction)
- rule-based enforcement
- support for custom organisation policies
"""
def __init__(self):
self.policies: Dict[str, Dict[str, Any]] = {}
self._load_default_policies()
def _load_default_policies(self):
self.policies["SAFE_OUTPUT_BASELINE"] = {
"id": "SAFE_OUTPUT_BASELINE",
"description": "Disallow obvious harmful, illegal or highly sensitive content.",
"rules": {
"blocked_keywords": [
"terrorist",
"child sexual",
"explosive instructions",
"bioweapon",
"financial fraud",
]
},
}
self.policies["NO_PII_LEAK"] = {
"id": "NO_PII_LEAK",
"description": "Reduce risk of leaking obvious PII markers in logs.",
"rules": {
"blocked_markers": ["national insurance number", "passport number", "nhs number"]
},
}
def add_policy(self, policy: Dict[str, Any]) -> None:
pid = policy.get("id")
if not pid:
raise ValueError("Policy must have an 'id'")
self.policies[pid] = policy
def list_policies(self) -> List[Dict[str, Any]]:
return list(self.policies.values())
def evaluate_output(
self,
output_text: str,
active_policy_ids: Optional[List[str]] = None,
) -> Dict[str, Any]:
text = (output_text or "").lower()
active_policy_ids = active_policy_ids or list(self.policies.keys())
violations: List[Dict[str, Any]] = []
for pid in active_policy_ids:
policy = self.policies.get(pid)
if not policy:
continue
rules = policy.get("rules", {})
for kw in rules.get("blocked_keywords", []):
if kw in text:
violations.append(
{"policy_id": pid, "type": "blocked_keyword", "value": kw}
)
for marker in rules.get("blocked_markers", []):
if marker in text:
violations.append(
{"policy_id": pid, "type": "blocked_marker", "value": marker}
)
is_allowed = len(violations) == 0
return {"is_allowed": is_allowed, "violations": violations}
# ---------------------------------------------------------------------
# Integrity certificate
# ---------------------------------------------------------------------
def generate_integrity_certificate(
fp,
att,
output: str = "sovereign_certificate.json",
parent_model: Optional[str] = None,
model_version: str = "1.0",
data_tags: Optional[List[str]] = None,
notes: Optional[str] = None,
deployment_env: Optional[str] = None,
provider: Optional[str] = None,
model_family: Optional[str] = None,
) -> Dict[str, Any]:
"""
Bundle fingerprint + attestation + lineage + risk profile
into a single integrity certificate file.
"""
if isinstance(fp, dict):
fp_value = fp.get("fingerprint", "")
else:
fp_value = fp
lineage = build_lineage_record(
engine=ENGINE_NAME,
fingerprint=fp_value,
parent_model=parent_model,
model_version=model_version,
data_tags=data_tags,
notes=notes,
provider=provider,
model_family=model_family,
)
risk = compute_risk_profile(data_tags=data_tags, deployment_env=deployment_env)
issued_at = att["issued_at"]
cert: Dict[str, Any] = {
"engine": ENGINE_NAME,
"sovereign_version": SOVEREIGN_VERSION,
"fingerprint": fp_value,
"issued_at": issued_at,
"attestation": att,
"lineage": lineage,
"risk_level": risk["level"],
"risk_score": risk["score"],
"threat_flags": risk["threat_flags"],
"environment": deployment_env or "unspecified",
"audit_id": str(uuid.uuid4()),
}
with open(output, "w", encoding="utf-8") as f:
json.dump(cert, f, indent=4)
return cert
# ---------------------------------------------------------------------
# Centralised Audit Log Layer (JSONL + CSV/JSON export)
# ---------------------------------------------------------------------
def _ensure_audit_log_dir(path: str) -> None:
directory = os.path.dirname(path)
if directory and not os.path.exists(directory):
os.makedirs(directory, exist_ok=True)
def write_audit_event(
event_type: str,
engine: str,
fingerprint: str,
parent_model: Optional[str],
model_version: str,
data_tags: Optional[List[str]],
risk_level: Optional[str],
risk_score: Optional[int],
deployment_env: Optional[str],
extra: Optional[Dict[str, Any]] = None,
path: str = AUDIT_LOG_FILE,
) -> Dict[str, Any]:
"""
Append a single audit event as JSON line into the centralised audit journal.
Designed to be reusable / exportable for G-Cloud / NCSC type evidence.
"""
_ensure_audit_log_dir(path)
entry = {
"event_id": str(uuid.uuid4()),
"event_type": event_type,
"engine": engine,
"fingerprint": fingerprint,
"parent_model": parent_model,
"model_version": model_version,
"data_tags": data_tags or [],
"risk_level": risk_level,
"risk_score": risk_score,
"deployment_env": deployment_env,
"timestamp": int(time.time()),
"extra": extra or {},
}
with open(path, "a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")
return entry
def export_audit_log_json(path: str = AUDIT_LOG_FILE) -> List[Dict[str, Any]]:
"""
Load the audit log as a list of JSON objects.
"""
if not os.path.exists(path):
return []
entries: List[Dict[str, Any]] = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
entries.append(obj)
except Exception:
# skip corrupted lines
continue
return entries
def export_audit_log_csv(path: str = AUDIT_LOG_FILE) -> str:
"""
Export the audit log as a simple CSV string
(for G-Cloud / NCSC style evidence export).
"""
entries = export_audit_log_json(path=path)
if not entries:
return ""
headers = list(entries[0].keys())
lines: List[str] = []
# header row
lines.append(",".join(headers))
# data rows
for row in entries:
row_values: List[str] = []
for h in headers:
value = row.get(h, "")
if isinstance(value, list):
value = ";".join(str(v) for v in value)
elif isinstance(value, dict):
value = json.dumps(value)
row_values.append(str(value))
lines.append(",".join(row_values))
return "\n".join(lines)
# ---------------------------------------------------------------------
# Higher-level helper: log model execution with policy & efficiency info
# ---------------------------------------------------------------------
def log_model_execution(
model_name: str,
provider: Optional[str],
deployment_env: str,
data_tags: Optional[List[str]],
output_text: str,
tokens: int,
parent_model: Optional[str] = None,
model_version: str = "1.0",
active_policy_ids: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""
Convenience helper that:
- computes risk profile
- evaluates policy engine
- estimates compute efficiency
- writes a single unified audit event
"""
# risk
risk = compute_risk_profile(data_tags=data_tags, deployment_env=deployment_env)
# policy
pe = PolicyEngine()
policy_result = pe.evaluate_output(output_text, active_policy_ids=active_policy_ids)
# efficiency
efficiency = estimate_compute_cost(
model_name=model_name,
tokens=tokens,
provider=provider,
)
extra = {
"model_name": model_name,
"provider": provider,
"policy_result": policy_result,
"efficiency": efficiency,
}
entry = write_audit_event(
event_type="model_execution",
engine=ENGINE_NAME,
fingerprint=efficiency["family"], # simple placeholder binding
parent_model=parent_model,
model_version=model_version,
data_tags=data_tags,
risk_level=risk["level"],
risk_score=risk["score"],
deployment_env=deployment_env,
extra=extra,
)
return entry
# ------------------------------------------------------------
# Certificate Verification Wrapper (Fix for app.py import)
# ------------------------------------------------------------
def verify_certificate(cert: dict) -> bool:
"""
Verification wrapper so that app.py can import it.
A valid certificate requires:
- attestation to verify successfully
- fingerprint / engine to be consistent across cert + lineage + attestation
- lineage_hash to match a recomputed hash from core fields
"""
try:
# 1) Attestation must be valid
att = cert.get("attestation") or {}
if not verify_attestation(att):
return False
# 2) Basic consistency checks
cert_fp = cert.get("fingerprint")
cert_engine = cert.get("engine")
lineage = cert.get("lineage") or {}
line_fp = lineage.get("fingerprint")
line_engine = lineage.get("engine")
if not cert_fp:
return False
# fingerprint باید همه‌جا یکی باشد
if cert_fp != att.get("fingerprint"):
return False
if cert_fp != line_fp:
return False
# engine اگر در هر دو جا هست باید یکی باشد
if cert_engine and line_engine and cert_engine != line_engine:
return False
# 3) Recompute lineage_hash exactly like build_lineage_record
engine_val = line_engine or ""
fp_val = line_fp or ""
model_ver = (lineage.get("model_version") or "")
parent_val = (lineage.get("parent_model") or "")
created_val = lineage.get("created_at") or ""
payload = f"{engine_val}|{fp_val}|{model_ver}|{parent_val}|{created_val}"
expected_hash = hashlib.sha256(payload.encode()).hexdigest()
if lineage.get("lineage_hash") != expected_hash:
return False
# اگر به اینجا برسیم، همه چیز ok است
return True
except Exception:
return False