/**
 * ASI-04 (OWASP Agentic Top 10) — Agentic Supply Chain: AI Bill of Materials.
 *
 * Microsoft AGT parity for the supply-chain gap. The existing SupplyChain module
 * audits npm dependencies; this produces the AI-BOM — a structured, tamper-evident
 * inventory of everything that composes an agent (its models, tools, MCP servers,
 * compliance packs, and dependencies) anchored by a `bomHash`, plus a minimal
 * SLSA-style provenance statement that binds that hash as its subject. A relying
 * org verifies the AI-BOM to trust the agent's supply chain.
 *
 * Invariants: pure in-process compute (I-10). `verifyAiBom` NEVER throws (I-2/I-3).
 *
 * @connexum/ai-governance
 */
import * as crypto from 'node:crypto';
// ---------------------------------------------------------------------------
// Canonical JSON (deterministic, key-order independent)
// ---------------------------------------------------------------------------
export function canonicalBomJson(value) {
    if (value === null || typeof value !== 'object')
        return JSON.stringify(value) ?? 'null';
    if (Array.isArray(value))
        return '[' + value.map((v) => canonicalBomJson(v)).join(',') + ']';
    const obj = value;
    const keys = Object.keys(obj).sort();
    return '{' + keys.map((k) => JSON.stringify(k) + ':' + canonicalBomJson(obj[k])).join(',') + '}';
}
/** Stable component sort: by type, then id, then version — so order in never affects the hash. */
function sortComponents(components) {
    return [...components].sort((a, b) => a.type.localeCompare(b.type) || a.id.localeCompare(b.id) || (a.version ?? '').localeCompare(b.version ?? ''));
}
function hashComponents(components) {
    return crypto.createHash('sha256').update(canonicalBomJson(sortComponents(components)), 'utf-8').digest('hex');
}
// ---------------------------------------------------------------------------
// Generate / verify
// ---------------------------------------------------------------------------
/** Build the AI-BOM for an agent from its composed parts. */
export function generateAiBom(input) {
    const components = [];
    for (const m of input.models ?? []) {
        components.push({ type: 'model', id: m.id, version: m.version, supplier: m.provider, hash: m.hash });
    }
    for (const t of input.tools ?? []) {
        components.push({ type: 'tool', id: t, name: t });
    }
    for (const s of input.mcpServers ?? []) {
        components.push({ type: 'mcp-server', id: s });
    }
    for (const p of input.compliancePacks ?? []) {
        components.push({ type: 'compliance-pack', id: p });
    }
    for (const d of input.dependencies ?? []) {
        components.push({ type: 'dependency', id: d.name, name: d.name, version: d.version, hash: d.hash });
    }
    const sorted = sortComponents(components);
    return {
        bomFormat: 'connexum-ai-bom',
        specVersion: '1.0',
        agentId: input.agentId,
        generatedAt: input.generatedAt ?? new Date().toISOString(),
        components: sorted,
        bomHash: hashComponents(sorted),
    };
}
/**
 * Verify an AI-BOM: recompute the hash over its components and compare. NEVER
 * throws — malformed input returns false.
 */
export function verifyAiBom(bom) {
    try {
        if (!bom || typeof bom !== 'object' || !Array.isArray(bom.components) || typeof bom.bomHash !== 'string') {
            return false;
        }
        const expected = hashComponents(bom.components);
        if (expected.length !== bom.bomHash.length)
            return false;
        return crypto.timingSafeEqual(Buffer.from(expected, 'utf-8'), Buffer.from(bom.bomHash, 'utf-8'));
    }
    catch {
        return false;
    }
}
/**
 * Produce a minimal SLSA provenance statement binding the AI-BOM's `bomHash` as
 * the signed subject. A registration pipeline emits this so a relying org can
 * verify WHO built the agent's supply-chain inventory and WHAT it covered.
 */
export function generateSlsaProvenance(bom, opts) {
    return {
        _type: 'https://in-toto.io/Statement/v1',
        predicateType: 'https://slsa.dev/provenance/v1',
        subject: [{ name: `ai-bom:${bom.agentId}`, digest: { sha256: bom.bomHash } }],
        predicate: {
            buildType: opts.buildType,
            builder: { id: opts.builder },
            metadata: {
                invocationId: opts.invocationId ?? bom.bomHash.slice(0, 16),
                startedOn: bom.generatedAt,
            },
        },
    };
}
//# sourceMappingURL=ai-bom.js.map