"use strict";
/**
 * RED-GREEN tests for F-NEW-PRODUCT-OWASP-AGENTIC-SCANNER (W1.7 of
 * F-NEW-PRODUCT-AGENT-VISIBILITY-CLUSTER-2026-05-12).
 *
 * @connexum/ai-governance  F-NEW-PRODUCT-OWASP-AGENTIC-SCANNER
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const node_test_1 = require("node:test");
const assert = __importStar(require("node:assert/strict"));
const owasp_agentic_scanner_1 = require("./owasp-agentic-scanner");
const capability_classes_1 = require("./capability-classes");
const baseManifest = (over = {}) => ({
    agentId: 'agent-test',
    name: 'Test Agent',
    tools: [],
    hooks: { preToolUse: true, postToolUse: true, postResponse: true },
    llmProvider: 'connexum-proxy/anthropic',
    pluginIntegrityVerified: true,
    rateLimitsConfigured: true,
    // ASI-08: a fully-compliant manifest has the circuit breaker wired.
    circuitBreakerConfigured: true,
    // ASI-10: a fully-compliant manifest has kill-switch + revocation controls.
    // proxyRouted=true satisfies the F-5 check (proxy path covers the gap).
    // Alternatively sdkKillSwitchEnabled=true would also satisfy it.
    killSwitchCapable: true,
    revocationCapable: true,
    proxyRouted: true,
    // ASI-04: a fully-compliant baseline has an AI Bill of Materials (without it the
    // A05 AI-BOM finding fires — the clean-manifest baseline must satisfy it).
    aiBomGenerated: true,
    declaredApprovalGates: [],
    declaredForbiddenCapabilities: [],
    ...over,
});
(0, node_test_1.describe)('F-NEW-PRODUCT-OWASP-AGENTIC-SCANNER', () => {
    (0, node_test_1.it)('clean manifest produces an EXCELLENT verdict with a high score', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({
            declaredForbiddenCapabilities: [capability_classes_1.CAPABILITY_CLASS.MCP],
        }));
        assert.equal(report.findings.length, 0);
        assert.equal(report.buildStructureScore, 100);
        assert.equal(report.verdict, 'EXCELLENT');
    });
    (0, node_test_1.it)('A01 Lethal Trifecta: WebFetch + Bash -> CRIT finding + CRITICAL verdict', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ tools: ['WebFetch', 'Bash'] }));
        const a01 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A01_PROMPT_INJECTION);
        assert.ok(a01.some((f) => f.severity === 'CRIT' && /Lethal Trifecta/.test(f.title)));
        assert.equal(report.verdict, 'CRITICAL');
        assert.equal(report.countsBySeverity.CRIT, 1);
    });
    (0, node_test_1.it)('A01 missing PreToolUse hook (agent uses tools) -> HIGH finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(
        // tools present → PreToolUse enforcement is required for this agent.
        baseManifest({ tools: ['Read'], hooks: { preToolUse: false, postToolUse: true, postResponse: true } }));
        const a01 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A01_PROMPT_INJECTION && /PreToolUse/.test(f.title));
        assert.equal(a01.length, 1);
        assert.equal(a01[0].severity, 'HIGH');
    });
    (0, node_test_1.it)('capability-scoped: a NO-TOOL agent is NOT flagged for a missing PreToolUse hook', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ tools: [], hooks: { preToolUse: false, postToolUse: true, postResponse: true } }));
        const a01 = report.findings.filter((f) => /PreToolUse/.test(f.title));
        assert.equal(a01.length, 0, 'an agent that uses no tools must not be penalized for PreToolUse');
    });
    (0, node_test_1.it)('A02 missing PostResponse hook -> HIGH finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ hooks: { preToolUse: true, postToolUse: true, postResponse: false } }));
        const a02 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A02_INSECURE_OUTPUT);
        assert.equal(a02.length, 1);
        assert.equal(a02[0].severity, 'HIGH');
    });
    (0, node_test_1.it)('A03 retrieval without PostToolUse -> MED finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({
            tools: ['WebFetch'],
            hooks: { preToolUse: true, postToolUse: false, postResponse: true },
        }));
        const a03 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A03_RAG_POISONING);
        assert.equal(a03.length, 1);
        assert.equal(a03[0].severity, 'MED');
    });
    (0, node_test_1.it)('A04 no rate limits (agent uses tools) -> MED finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ tools: ['Read'], rateLimitsConfigured: false }));
        const a04 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A04_DOS);
        assert.equal(a04.length, 1);
        assert.equal(a04[0].severity, 'MED');
    });
    (0, node_test_1.it)('A05 plugin integrity not verified (agent uses MCP) -> HIGH finding', () => {
        // MCP present → plugin/adapter integrity is required for this agent.
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ mcpServers: ['stdio:/x'], pluginIntegrityVerified: false }));
        const a05 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A05_SUPPLY_CHAIN && /integrity/i.test(f.title));
        assert.equal(a05.length, 1);
        assert.equal(a05[0].severity, 'HIGH');
    });
    (0, node_test_1.it)('capability-scoped: a NO-PLUGIN agent is NOT flagged for plugin integrity', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ mcpServers: [], pluginIntegrityVerified: false }));
        const a05 = report.findings.filter((f) => /Plugin \/ adapter integrity/.test(f.title));
        assert.equal(a05.length, 0, 'an agent with no plugins must not be penalized for plugin integrity');
    });
    (0, node_test_1.it)('A06 LLM not via proxy -> MED finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ llmProvider: 'anthropic/claude-sonnet-4-6' }));
        const a06 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A06_SENSITIVE_DISCLOSURE);
        assert.equal(a06.length, 1);
        assert.equal(a06[0].severity, 'MED');
    });
    (0, node_test_1.it)('A07 MCP in use without integrity attestation -> HIGH finding + suggested rule', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({
            mcpServers: ['stdio:/usr/bin/mcp-server'],
            pluginIntegrityVerified: false,
        }));
        const a07 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A07_INSECURE_PLUGIN);
        assert.equal(a07.length, 1);
        assert.equal(a07[0].severity, 'HIGH');
        assert.equal(a07[0].suggestedRule?.capability, capability_classes_1.CAPABILITY_CLASS.MCP);
        assert.ok(report.suggestedRules.some((r) => r.capability === capability_classes_1.CAPABILITY_CLASS.MCP));
    });
    (0, node_test_1.it)('A08 ungated shell + DB write -> two HIGH findings + suggested rules', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ tools: ['Bash', 'postgres_query'] }));
        const a08 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A08_EXCESSIVE_AGENCY);
        assert.equal(a08.length, 2);
        assert.ok(a08.every((f) => f.severity === 'HIGH'));
        assert.ok(report.suggestedRules.some((r) => r.capability === capability_classes_1.CAPABILITY_CLASS.SHELL_EXEC));
        assert.ok(report.suggestedRules.some((r) => r.capability === capability_classes_1.CAPABILITY_CLASS.EXTERNAL_DB_WRITE));
    });
    (0, node_test_1.it)('A08 ungated cap with declared approval -> NO finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({
            tools: ['Bash'],
            declaredApprovalGates: [{ capability: capability_classes_1.CAPABILITY_CLASS.SHELL_EXEC, reviewer: 'sre-lead' }],
        }));
        const a08 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A08_EXCESSIVE_AGENCY);
        assert.equal(a08.length, 0);
    });
    (0, node_test_1.it)('A09 zero gates configured -> MED finding', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest());
        const a09 = report.findings.filter((f) => f.risk === owasp_agentic_scanner_1.OWASP_AGENTIC_RISK.A09_OVERRELIANCE);
        assert.equal(a09.length, 1);
        assert.equal(a09[0].severity, 'MED');
    });
    (0, node_test_1.it)('build-structure score reflects severity penalties (CRIT = -50)', () => {
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({ tools: ['WebFetch', 'Bash'] }));
        // CRIT (Lethal Trifecta) + HIGH (A08 shell ungated) = -50 + -32 = -82 -> 18
        assert.ok(report.buildStructureScore <= 30);
        assert.equal(report.verdict, 'CRITICAL');
    });
    (0, node_test_1.it)('suggestedRules are deduplicated by capability+action', () => {
        // A07 + A08 both touch MCP if both are triggered for the same agent.
        const report = (0, owasp_agentic_scanner_1.scanAgent)(baseManifest({
            tools: ['mcp__list_resources', 'Bash'],
            mcpServers: ['stdio:/x'],
            pluginIntegrityVerified: false,
        }));
        const mcpRules = report.suggestedRules.filter((r) => r.capability === capability_classes_1.CAPABILITY_CLASS.MCP);
        assert.equal(mcpRules.length, 1, 'MCP rule must be deduplicated');
    });
});
//# sourceMappingURL=owasp-agentic-scanner.test.js.map