# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Subtree 13 -- AI governance & compliance posture (Linux / Unix / macOS).

The foundational AI-governance control is knowing what AI you run and having a
policy for it. This subtree affirms the presence of two on-host governance
artifacts and flags their absence as a governance gap:

  - an AI Bill of Materials (AI-BOM / CycloneDX ML-BOM) or model-card
    inventory -- the AI analogue of the CBOM control in espquantum;
  - a written AI acceptable-use / governance policy artifact.

These map directly to the cross-framework controls SigmaAICompliance reports
against: NIST AI RMF (GOVERN / MAP -- AI system inventory + policy), ISO/IEC
42001 (AI management system + documented information), the EU AI Act
technical-documentation duty, Forrester AEGIS (agentic guardrails), and
NIST 800-53/800-171. This is the on-host evidence layer; org-wide roll-up and
framework cross-mapping are server-side (see espnistairmf2023, the GRC sibling
of this detection pack).

Affirm-or-gap (never a hard violation), mirroring quantum.governance.
Read-only.
"""

from ai.common import is_unix as _is_unix

# AI Bill of Materials / model-inventory artifacts (bounded roots).
const AI_BOM_GLOBS = {
    "/etc/aibom/*.json", "/var/lib/aibom/*.json", "/usr/share/aibom/*.json",
    "/opt/*/aibom*.json", "/opt/*/*.aibom.json", "/opt/*/mlbom*.json",
    "/opt/*/*.cdx.json", "/srv/*/*.aibom.json", "/srv/*/*.cdx.json",
    "/home/*/*.aibom.json", "/home/*/*.cdx.json", "/etc/aibom/*.cdx.json"
}

# Model-card documentation (bounded roots).
const MODEL_CARD_GLOBS = {
    "/opt/*/MODEL_CARD*.md", "/opt/*/model-card*.md", "/opt/*/modelcard*.md",
    "/srv/*/MODEL_CARD*.md", "/srv/*/model-card*.md",
    "/home/*/*/MODEL_CARD*.md", "/home/*/models/*/README.md",
    "/usr/share/ai/model-card*.md"
}

# AI acceptable-use / governance policy artifacts (bounded roots).
const AI_POLICY_GLOBS = {
    "/etc/ai-policy*", "/etc/ai/policy*", "/etc/policies/ai*",
    "/opt/*/ai-policy*", "/opt/*/AI-USE-POLICY*", "/opt/*/ai-acceptable-use*",
    "/srv/*/ai-policy*", "/srv/*/ai-acceptable-use*",
    "/home/*/ai-policy*", "/usr/share/policy/ai*", "/usr/share/doc/ai-policy*"
}


func _file_has(host: Host, path: str, selector: str): bool:
    """True iff a regular file has at least one line matching `selector`."""
    let f = host.file(path)
    if not f.exists or f.ftype != "file":
        return false
    return len(host.editfile(path).extract(selector, ".*")) > 0


proc check_ai_bom_present(host: Host):
    """Affirm an AI-BOM / model-card inventory on the host; flag its absence as
    a governance gap (NIST AI RMF MAP-1 / ISO 42001 / EU AI Act inventory
    duty).
    """
    if not _is_unix(host):
        host.leave(0, "AI-BOM check is Unix/macOS-only")
    let n = host.integer()
    n.setvalue(0)
    for pat in AI_BOM_GLOBS:
        for f in host.glob({pat}):
            if f.exists and f.ftype == "file":
                if _file_has(host, f.filename,
                        r"(?i)(machine-learning|\x22bomFormat\x22|modelcard|mlbom|ai-?bom)"):
                    host.compliance(0, "AI-BOM / model inventory artifact present: %s",
                                    f.filename)
                    n.setvalue(n.getvalue() + 1)
    for pat in MODEL_CARD_GLOBS:
        for f in host.glob({pat}):
            if f.exists and f.ftype == "file":
                host.compliance(0, "Model-card artifact present: %s", f.filename)
                n.setvalue(n.getvalue() + 1)
    if n.getvalue() == 0:
        host.report("No AI-BOM / model-inventory / model-card artifact found on host (the foundational AI-governance control -- NIST AI RMF MAP-1 / ISO 42001 / EU AI Act technical documentation)")


proc check_ai_policy_present(host: Host):
    """Affirm a written AI acceptable-use / governance policy artifact on the
    host; flag its absence as a governance gap.
    """
    if not _is_unix(host):
        host.leave(0, "AI-policy check is Unix/macOS-only")
    let n = host.integer()
    n.setvalue(0)
    for pat in AI_POLICY_GLOBS:
        for f in host.glob({pat}):
            if f.exists and f.ftype == "file":
                if _file_has(host, f.filename,
                        r"(?i)(acceptable use|artificial intelligence|generative ai|large language model|\bLLM\b)"):
                    host.compliance(0, "AI acceptable-use / governance policy artifact present: %s",
                                    f.filename)
                    n.setvalue(n.getvalue() + 1)
    if n.getvalue() == 0:
        host.report("No on-host AI acceptable-use / governance policy artifact found (NIST AI RMF GOVERN / ISO 42001 policy requirement)")
