/**
 * OWASP CLI Formatter — OWASP-B3
 *
 * Renders human-readable and JSON output for OWASP Agentic Top 10 scan reports.
 * Used by `src/cli/scan.ts` when outputting to a TTY (human mode) and when
 * --json is explicitly passed (JSON mode).
 *
 * Customer code path consumer:
 *   src/cli/scan.ts:runScanCommand() — called for every agent in the scan
 *
 * @connexum/ai-governance  OWASP-B3
 */
// ---------------------------------------------------------------------------
// Severity ordering and prefix rendering
// ---------------------------------------------------------------------------
const SEVERITY_ORDER = ['CRIT', 'HIGH', 'MED', 'LOW'];
const SEVERITY_LABEL = {
    CRIT: 'CRITICAL',
    HIGH: 'HIGH',
    MED: 'MEDIUM',
    LOW: 'LOW',
};
/**
 * Prefix character based on severity.
 *   [!] CRITICAL — immediate action required
 *   [~] HIGH     — urgent
 *   [-] MEDIUM   — important
 *   [i] LOW      — informational
 */
const SEVERITY_PREFIX = {
    CRIT: '[!]',
    HIGH: '[~]',
    MED: '[-]',
    LOW: '[i]',
};
// ---------------------------------------------------------------------------
// Risk code extraction (A01-A10 from 'A01-prompt-injection' etc.)
// ---------------------------------------------------------------------------
function extractRiskCode(riskId) {
    // 'A01-prompt-injection' -> 'A01'
    // 'A10-model-theft' -> 'A10'
    const m = riskId.match(/^(A\d{2})/i);
    return m ? m[1].toUpperCase() : riskId.toUpperCase();
}
// ---------------------------------------------------------------------------
// Default license-state remediation copy
// (Used when the finding does not carry a ControlFix.remediationByLicenseState)
// ---------------------------------------------------------------------------
const DEFAULT_REMEDIATION = {
    'not-purchased': 'Company Pack not purchased — purchase at app.my-cc.io/billing to enable this control.',
    'purchased-not-active': 'Company Pack purchased but not active on this agent — run: ai-governance activate --agent <id>',
    'active': 'Company Pack active — review configuration or check scanner settings for this control.',
};
// ---------------------------------------------------------------------------
// Single-finding renderer
// ---------------------------------------------------------------------------
/**
 * Format one OWASP finding into a multi-line human-readable block.
 *
 * Example output:
 *   [!] A01  CRITICAL  Prompt Injection Risk
 *       Agent has web-fetch + shell-exec without a prompt-injection guard.
 *       Remedy: Enable prompt-injection guard in PreToolUse hook.
 *       License (not-purchased): Company Pack not purchased — purchase at ...
 */
export function formatOwaspFinding(finding, licenseState = 'not-purchased') {
    const prefix = SEVERITY_PREFIX[finding.severity];
    const code = extractRiskCode(finding.risk);
    const severityLabel = SEVERITY_LABEL[finding.severity];
    const licenseNote = DEFAULT_REMEDIATION[licenseState];
    const lines = [
        `${prefix} ${code}  ${severityLabel}  ${finding.title}`,
        `    ${finding.detail}`,
        `    Remedy: ${finding.remediation}`,
        `    License (${licenseState}): ${licenseNote}`,
    ];
    return lines.join('\n');
}
// ---------------------------------------------------------------------------
// Full scan report renderer
// ---------------------------------------------------------------------------
/**
 * Render a complete scan report for one agent.
 *
 * In JSON mode (`opts.json = true`), returns the ScanReport serialised as
 * pretty-printed JSON — identical to the pre-B3 behaviour for machine consumers.
 *
 * In human mode (default), renders:
 *   ────────────────────────────────────────────────────────
 *   OWASP Agentic Top 10 Coverage — <agentName>
 *   ────────────────────────────────────────────────────────
 *   Verdict: EXCELLENT | GOOD | NEEDS_REVIEW | POOR | CRITICAL
 *   Build structure score: 85/100
 *
 *   [!] A01  CRITICAL  Prompt Injection Risk
 *       ...
 *
 *   Summary: 2 findings (1 CRITICAL, 1 HIGH)
 *   ────────────────────────────────────────────────────────
 */
export function formatOwaspScanOutput(agentName, report, opts = {}) {
    const licenseState = opts.licenseState ?? 'not-purchased';
    // JSON mode: return raw report as JSON
    if (opts.json) {
        return JSON.stringify(report, null, 2);
    }
    // Sort findings by severity (CRIT first)
    const sorted = [...report.findings].sort((a, b) => {
        return SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity);
    });
    const sep = '\u2500'.repeat(56);
    const header = `OWASP Agentic Top 10 Coverage \u2014 ${agentName}`;
    const lines = [
        sep,
        header,
        sep,
        `Verdict: ${report.verdict}`,
        `Build structure score: ${report.buildStructureScore}/100`,
        '',
    ];
    if (sorted.length === 0) {
        lines.push('  No OWASP findings. All controls satisfied.');
        lines.push('');
    }
    else {
        for (const finding of sorted) {
            lines.push(formatOwaspFinding(finding, licenseState));
            lines.push('');
        }
    }
    // Summary line — recompute from findings to stay in sync with sorted list
    const total = report.findings.length;
    const critCount = report.findings.filter((f) => f.severity === 'CRIT').length;
    const highCount = report.findings.filter((f) => f.severity === 'HIGH').length;
    const medCount = report.findings.filter((f) => f.severity === 'MED').length;
    const lowCount = report.findings.filter((f) => f.severity === 'LOW').length;
    const summaryParts = [];
    if (critCount > 0)
        summaryParts.push(`${critCount} CRITICAL`);
    if (highCount > 0)
        summaryParts.push(`${highCount} HIGH`);
    if (medCount > 0)
        summaryParts.push(`${medCount} MEDIUM`);
    if (lowCount > 0)
        summaryParts.push(`${lowCount} LOW`);
    const summaryDetail = summaryParts.length > 0 ? ` (${summaryParts.join(', ')})` : '';
    lines.push(`Summary: ${total} finding${total !== 1 ? 's' : ''}${summaryDetail}`);
    lines.push(sep);
    return lines.join('\n');
}
//# sourceMappingURL=owasp.js.map