"use strict";
/**
 * RetrievalAllowlistEnforcer -- Per-agent + per-pack source allowlisting
 * for RAG / vector-store / external retrieval.
 *
 * When an agent's tool invokes a retrieval (RAG, vector-store lookup,
 * external search), the source URL/URI is checked against:
 *   1. Per-agent allowlist (explicit agent-level configuration)
 *   2. Per-pack allowlist (derived from the agent's bound compliance packs)
 *
 * Denial throws `UnauthorizedRetrievalSourceError` synchronously and emits
 * a CRITICAL `governance:retrieval-source-denied` audit event.
 *
 * Pack-binding specifics:
 *   - FCRA: consumer-report sources require an explicit `permissiblePurpose`
 *     tag in the request; requests without it are refused.
 *   - MRPC (legal): matter-scoped agents can only retrieve from their
 *     declared matter archive; any other source is refused.
 *
 * defaultPolicy:
 *   - 'deny': any source not in an allowlist throws (fail-closed, default).
 *   - 'allow-with-audit': unknown sources emit an audit event but proceed.
 *
 * P3-T1 coverage gap: Retrieval source allowlisting per agent.
 *
 * Compliance bindings:
 *   - FCRA 15 U.S.C. 1681b: permissible purpose requirement
 *   - MRPC 1.6: confidentiality of client information
 *   - SOC 2 CC6.1: logical access controls
 *   - HIPAA 164.312(a)(1): access control to PHI sources
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetrievalAllowlistEnforcer = exports.UnauthorizedRetrievalSourceError = void 0;
// ---------------------------------------------------------------------------
// Errors
// ---------------------------------------------------------------------------
class UnauthorizedRetrievalSourceError extends Error {
    agentId;
    source;
    denyReason;
    packBinding;
    constructor(opts) {
        super(`Unauthorized retrieval source: agent '${opts.agentId}' attempted to retrieve from '${opts.source}'. ` +
            `Reason: ${opts.denyReason}.`);
        this.name = 'UnauthorizedRetrievalSourceError';
        this.agentId = opts.agentId;
        this.source = opts.source;
        this.denyReason = opts.denyReason;
        this.packBinding = opts.packBinding ?? [];
    }
}
exports.UnauthorizedRetrievalSourceError = UnauthorizedRetrievalSourceError;
const DEFAULT_CONFIG = {
    defaultPolicy: 'deny',
    mrpcMatterArchives: {},
    fcraPermissiblePurposes: [
        'credit-transaction',
        'employment',
        'insurance',
        'government-license',
        'legitimate-business-need',
        'account-review',
    ],
};
// ---------------------------------------------------------------------------
// RetrievalAllowlistEnforcer
// ---------------------------------------------------------------------------
class RetrievalAllowlistEnforcer {
    config;
    auditSink;
    constructor(config, auditSink) {
        // Strip undefined values from config before merging so DEFAULT_CONFIG
        // fields are not silently overwritten by explicit `undefined` in the caller.
        const filtered = Object.fromEntries(Object.entries(config).filter(([, v]) => v !== undefined));
        this.config = { ...DEFAULT_CONFIG, ...filtered };
        this.auditSink = auditSink ?? (() => undefined);
    }
    /**
     * Enforce allowlist for a retrieval request. Throws synchronously on
     * denial. Returns void on success.
     *
     * Must be called before dispatching any RAG / vector-store / external
     * retrieval tool so that the block fires during PreToolUse.
     */
    enforce(request, activePackIds = []) {
        const { agentId, source } = request;
        // 1. FCRA check: permissible purpose required if FCRA pack is active.
        if (activePackIds.includes('fcra')) {
            const validPurposes = this.config.fcraPermissiblePurposes ?? [];
            if (!request.permissiblePurpose || !validPurposes.includes(request.permissiblePurpose)) {
                this.deny(agentId, source, 'fcra-missing-permissible-purpose', activePackIds);
            }
        }
        // 2. MRPC check: source must be declared matter archive.
        if (activePackIds.includes('mrpc')) {
            const allowedArchives = (this.config.mrpcMatterArchives ?? {})[agentId] ?? [];
            if (allowedArchives.length > 0) {
                const archiveId = request.matterArchiveId ?? '';
                if (!allowedArchives.includes(archiveId)) {
                    this.deny(agentId, source, 'mrpc-not-declared-matter-archive', activePackIds);
                }
                // MRPC agents may ONLY retrieve from their declared matter archive.
                // The source URI itself need not be in the general allowlist.
                this.allow(agentId, source, 'pack');
                return;
            }
        }
        // 3. Per-agent allowlist.
        const agentAllowlist = this.config.perAgentAllowlist[agentId];
        if (agentAllowlist) {
            if (this.matches(source, agentAllowlist)) {
                this.allow(agentId, source, 'agent');
                return;
            }
            // Agent has an explicit allowlist but source is not in it.
            this.deny(agentId, source, 'not-in-agent-allowlist', activePackIds);
        }
        // 4. Per-pack allowlist.
        for (const packId of activePackIds) {
            const packAllowlist = this.config.perPackAllowlist[packId];
            if (packAllowlist && this.matches(source, packAllowlist)) {
                this.allow(agentId, source, 'pack');
                return;
            }
        }
        // 5. Default policy.
        if (this.config.defaultPolicy === 'deny') {
            this.deny(agentId, source, 'default-deny', activePackIds);
        }
        else {
            // allow-with-audit
            this.allow(agentId, source, 'allow-with-audit');
        }
    }
    // -------------------------------------------------------------------------
    // Internal helpers
    // -------------------------------------------------------------------------
    /**
     * Check if `source` matches any prefix in `list`.
     * Prefix match is used (e.g. 'https://docs.example.com' matches
     * 'https://docs.example.com/anything').
     */
    matches(source, list) {
        return list.some((allowed) => source === allowed || source.startsWith(allowed));
    }
    allow(agentId, source, matchedList) {
        const event = {
            type: 'governance:retrieval-source-allowed',
            agentId,
            source,
            matchedList,
            timestamp: new Date().toISOString(),
        };
        this.auditSink(event);
    }
    deny(agentId, source, denyReason, activePackIds) {
        const event = {
            type: 'governance:retrieval-source-denied',
            severity: 'CRITICAL',
            agentId,
            source,
            denyReason,
            timestamp: new Date().toISOString(),
        };
        this.auditSink(event);
        throw new UnauthorizedRetrievalSourceError({
            agentId,
            source,
            denyReason,
            packBinding: activePackIds,
        });
    }
}
exports.RetrievalAllowlistEnforcer = RetrievalAllowlistEnforcer;
//# sourceMappingURL=retrieval-allowlist.js.map