/**
 * Agent & MCP Security Scanners
 *
 * Orchestrates agent-specific security scanners for MCP servers and AI tool chains.
 * These scanners detect prompt injection vulnerabilities, exfiltration paths,
 * manifest security issues, and permission overprovisioning.
 *
 * @module scanners/agent
 */
import { AGENT_SCANNER_TYPES, DEFAULT_AGENT_SCANNER_OPTIONS, } from "./types.js";
// Re-export types
export * from "./types.js";
// ============================================================================
// Scanner Imports
// ============================================================================
// Implemented scanners
import { runManifestAudit, checkManifestAuditAvailable, getAuditSummary, } from "./manifest-audit.js";
import { runToolDriftScanner, checkToolDriftAvailable, createBaseline, loadBaseline, saveBaseline, detectDrift, getDriftSummary, } from "./tool-description-drift.js";
import { runPromptInjectionFuzzer, checkPromptInjectionFuzzerAvailable, getFuzzerSummary, generateFuzzerDiagram, } from "./prompt-injection-fuzzer.js";
import { runExfilPathScanner, checkExfilPathAvailable, getExfilGraph, getExfilSummary, } from "./exfil-path-graph.js";
import { runPermissionMinimiser, checkPermissionMinimiserAvailable, getPermissionProposals, getMinimiserSummary, } from "./permission-minimiser.js";
import { runSupplyChainMCP, checkSupplyChainMCPAvailable, getSupplyChainSummary, } from "./supply-chain-mcp.js";
import { runSandboxAudit, checkSandboxAuditAvailable, getSandboxAuditSummary, } from "./sandbox-audit.js";
import { runCredentialScopeAudit, checkCredentialScopeAuditAvailable, getCredentialAuditSummary, } from "./credential-scope-audit.js";
// Re-export scanner functions
export { 
// Manifest audit
runManifestAudit, checkManifestAuditAvailable, getAuditSummary, 
// Tool drift
runToolDriftScanner, checkToolDriftAvailable, createBaseline, loadBaseline, saveBaseline, detectDrift, getDriftSummary, 
// Prompt injection fuzzer
runPromptInjectionFuzzer, checkPromptInjectionFuzzerAvailable, getFuzzerSummary, generateFuzzerDiagram, 
// Exfil path graph
runExfilPathScanner, checkExfilPathAvailable, getExfilGraph, getExfilSummary, 
// Permission minimiser
runPermissionMinimiser, checkPermissionMinimiserAvailable, getPermissionProposals, getMinimiserSummary, 
// Supply chain
runSupplyChainMCP, checkSupplyChainMCPAvailable, getSupplyChainSummary, 
// Sandbox audit
runSandboxAudit, checkSandboxAuditAvailable, getSandboxAuditSummary, 
// Credential scope audit
runCredentialScopeAudit, checkCredentialScopeAuditAvailable, getCredentialAuditSummary, };
// Payload utilities
export { loadPayloads, getPayloadStats, getRandomPayloads, getPayloadsByTags, CORPUS_SIZES } from "./payloads/index.js";
// ============================================================================
// Manifest Discovery
// ============================================================================
/**
 * Discover MCP manifest from various sources
 */
export async function discoverManifest(target) {
    // If manifest is already provided, return it
    if (target.manifest) {
        return target.manifest;
    }
    // Try config file
    if (target.configFile) {
        return loadManifestFromFile(target.configFile);
    }
    // Try npm package
    if (target.npmPackage) {
        return loadManifestFromPackage(target.npmPackage);
    }
    // Try URL (MCP server)
    if (target.url) {
        return loadManifestFromServer(target.url);
    }
    // Try source path
    if (target.sourcePath) {
        return loadManifestFromSource(target.sourcePath);
    }
    return null;
}
/**
 * Load manifest from a config file (server.json, mcp.json)
 */
async function loadManifestFromFile(filePath) {
    const { readFile } = await import("fs/promises");
    const { join, dirname } = await import("path");
    try {
        const content = await readFile(filePath, "utf-8");
        const config = JSON.parse(content);
        // Handle different config formats
        if (config.mcpServers) {
            // Claude Desktop format - return first server
            const serverName = Object.keys(config.mcpServers)[0];
            if (serverName) {
                const server = config.mcpServers[serverName];
                return {
                    name: serverName,
                    version: server.version || "unknown",
                    description: server.description,
                    tools: server.tools || [],
                    resources: server.resources,
                    prompts: server.prompts,
                    capabilities: server.capabilities,
                };
            }
        }
        // Direct manifest format
        if (config.name && config.tools) {
            return config;
        }
        // server.json format - try to find package.json for metadata
        if (config.command || config.args) {
            const pkgPath = join(dirname(filePath), "package.json");
            try {
                const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
                return {
                    name: pkg.name || "unknown",
                    version: pkg.version || "unknown",
                    description: pkg.description,
                    tools: [], // Would need to introspect server
                    capabilities: {
                        tools: true,
                    },
                };
            }
            catch {
                return {
                    name: "unknown",
                    version: "unknown",
                    tools: [],
                };
            }
        }
        return null;
    }
    catch {
        return null;
    }
}
/**
 * Load manifest from an npm package
 */
async function loadManifestFromPackage(packageName) {
    // For now, return null - would need to resolve and inspect package
    // This would be implemented to:
    // 1. Resolve package path
    // 2. Read package.json
    // 3. Look for MCP manifest in package
    console.warn(`Package manifest loading not yet implemented: ${packageName}`);
    return null;
}
/**
 * Load manifest from a running MCP server
 */
async function loadManifestFromServer(url) {
    // For now, return null - would need MCP client
    // This would be implemented to:
    // 1. Connect to server
    // 2. Call list_tools, list_resources, list_prompts
    // 3. Build manifest from responses
    console.warn(`Server manifest loading not yet implemented: ${url}`);
    return null;
}
/**
 * Load manifest from source code
 */
async function loadManifestFromSource(sourcePath) {
    const { readFile } = await import("fs/promises");
    const { join } = await import("path");
    const { glob } = await import("glob");
    try {
        // Look for package.json
        const pkgPath = join(sourcePath, "package.json");
        const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
        // Look for manifest files
        const manifestFiles = await glob("**/server.json", {
            cwd: sourcePath,
            ignore: ["**/node_modules/**"],
        });
        if (manifestFiles.length > 0) {
            const manifestPath = join(sourcePath, manifestFiles[0]);
            const manifestContent = JSON.parse(await readFile(manifestPath, "utf-8"));
            return {
                name: pkg.name || manifestContent.name || "unknown",
                version: pkg.version || manifestContent.version || "unknown",
                description: pkg.description || manifestContent.description,
                tools: manifestContent.tools || [],
                resources: manifestContent.resources,
                prompts: manifestContent.prompts,
            };
        }
        // Return basic manifest from package.json
        return {
            name: pkg.name || "unknown",
            version: pkg.version || "unknown",
            description: pkg.description,
            tools: [], // Would need AST analysis to discover tools
        };
    }
    catch {
        return null;
    }
}
// ============================================================================
// Scanner Availability Checks
// ============================================================================
/**
 * Check which agent scanners are available
 */
export async function checkAgentScannersAvailable() {
    const results = [];
    for (const scanner of AGENT_SCANNER_TYPES) {
        // Most agent scanners are built-in and always available
        // Some may require external tools or configuration
        const availability = await checkScannerAvailable(scanner);
        results.push(availability);
    }
    return results;
}
/**
 * Check if a specific agent scanner is available
 */
async function checkScannerAvailable(scanner) {
    switch (scanner) {
        case "manifest-audit":
        case "tool-description-drift":
        case "exfil-path-graph":
        case "permission-minimiser":
        case "sandbox-audit":
            // Built-in, always available
            return {
                scanner,
                available: true,
                version: "1.0.0",
            };
        case "prompt-injection-fuzzer":
            // Available, but needs payload corpus
            return {
                scanner,
                available: true,
                version: "1.0.0",
            };
        case "supply-chain-mcp":
            // Check if npm/osv-scanner available
            return {
                scanner,
                available: true,
                version: "1.0.0",
            };
        case "credential-scope-audit":
            // Built-in, always available
            return {
                scanner,
                available: true,
                version: "1.0.0",
            };
        default:
            return {
                scanner,
                available: false,
                error: `Unknown scanner: ${scanner}`,
            };
    }
}
// ============================================================================
// Scanner Orchestration
// ============================================================================
/**
 * Run all enabled agent scanners against a target
 */
export async function runAgentScanners(options) {
    const startTime = Date.now();
    const timestamp = new Date().toISOString();
    // Validate authorization
    if (!options.authorized) {
        throw new Error("Agent scanning requires explicit authorization. Set authorized: true to confirm you have permission to scan this target.");
    }
    // Merge with defaults
    const opts = {
        ...DEFAULT_AGENT_SCANNER_OPTIONS,
        ...options,
        scanners: {
            ...DEFAULT_AGENT_SCANNER_OPTIONS.scanners,
            ...options.scanners,
        },
    };
    // Discover manifest
    const manifest = await discoverManifest(options.target);
    if (!manifest) {
        throw new Error("Could not discover MCP manifest from target. Provide a config file, URL, or npm package.");
    }
    // Calculate manifest hash
    const manifestHash = await hashManifest(manifest);
    // Run enabled scanners
    const scannerResults = [];
    const failedScanners = [];
    // Collect scanners to run
    const scannersToRun = [];
    if (opts.scanners?.manifestAudit) {
        scannersToRun.push({
            type: "manifest-audit",
            runner: () => runManifestAuditScanner(manifest, opts),
        });
    }
    if (opts.scanners?.toolDrift) {
        scannersToRun.push({
            type: "tool-description-drift",
            runner: () => runToolDriftScannerWrapper(manifest, opts),
        });
    }
    if (opts.scanners?.promptInjection) {
        scannersToRun.push({
            type: "prompt-injection-fuzzer",
            runner: () => runPromptInjectionScanner(manifest, opts),
        });
    }
    if (opts.scanners?.exfilPath) {
        scannersToRun.push({
            type: "exfil-path-graph",
            runner: () => runExfilPathScannerWrapper(manifest, opts),
        });
    }
    if (opts.scanners?.permissionMinimiser) {
        scannersToRun.push({
            type: "permission-minimiser",
            runner: () => runPermissionMinimiserScanner(manifest, opts),
        });
    }
    if (opts.scanners?.supplyChain) {
        scannersToRun.push({
            type: "supply-chain-mcp",
            runner: () => runSupplyChainScanner(manifest, opts),
        });
    }
    if (opts.scanners?.sandboxAudit) {
        scannersToRun.push({
            type: "sandbox-audit",
            runner: () => runSandboxAuditScanner(manifest, opts),
        });
    }
    if (opts.scanners?.credentialScope) {
        scannersToRun.push({
            type: "credential-scope-audit",
            runner: () => runCredentialScopeScanner(manifest, opts),
        });
    }
    // Run scanners with concurrency control
    const concurrency = opts.concurrency || 2;
    const batches = [];
    for (let i = 0; i < scannersToRun.length; i += concurrency) {
        batches.push(scannersToRun.slice(i, i + concurrency));
    }
    for (const batch of batches) {
        const results = await Promise.allSettled(batch.map((s) => s.runner()));
        for (let i = 0; i < results.length; i++) {
            const result = results[i];
            const scannerType = batch[i].type;
            if (result.status === "fulfilled") {
                scannerResults.push(result.value);
                if (!result.value.success) {
                    failedScanners.push(scannerType);
                }
            }
            else {
                failedScanners.push(scannerType);
                scannerResults.push({
                    scanner: scannerType,
                    findings: [],
                    duration: 0,
                    success: false,
                    error: result.reason?.message || "Unknown error",
                });
            }
        }
    }
    // Aggregate results
    const totalFindings = scannerResults.reduce((sum, r) => sum + r.findings.length, 0);
    const bySeverity = {
        critical: 0,
        high: 0,
        medium: 0,
        low: 0,
        info: 0,
    };
    const byScanner = {};
    for (const result of scannerResults) {
        byScanner[result.scanner] = result.findings.length;
        for (const finding of result.findings) {
            bySeverity[finding.severity]++;
        }
    }
    const totalDuration = Date.now() - startTime;
    const allSucceeded = failedScanners.length === 0;
    // Calculate risk score
    const riskScore = calculateRiskScore(bySeverity, totalFindings);
    // Determine certification readiness
    const certificationReadiness = determineCertificationReadiness(bySeverity, riskScore);
    return {
        timestamp,
        target: options.target,
        manifest,
        manifestHash,
        scanners: scannerResults,
        totalFindings,
        bySeverity,
        byScanner,
        totalDuration,
        allSucceeded,
        failedScanners,
        riskScore,
        certificationReadiness,
    };
}
// ============================================================================
// Individual Scanner Runners
// ============================================================================
async function runManifestAuditScanner(manifest, opts) {
    return runManifestAudit(manifest, {
        skipChecks: opts.manifestAuditSkipChecks,
        onlyChecks: opts.manifestAuditOnlyChecks,
    });
}
async function runToolDriftScannerWrapper(manifest, opts) {
    return runToolDriftScanner(manifest, {
        baselinesDir: opts.baselinesDir,
        createIfMissing: opts.createBaselineIfMissing,
        forceNewBaseline: opts.forceNewBaseline,
        skipTypes: opts.driftSkipTypes,
    });
}
async function runPromptInjectionScanner(manifest, opts) {
    return runPromptInjectionFuzzer(manifest, {
        corpus: opts.fuzzerCorpus,
        customCorpus: opts.customPayloadsDir,
        categories: opts.fuzzerCategories,
        tools: opts.fuzzerTools,
        timeout: opts.fuzzerTimeout,
        concurrency: opts.concurrency,
        failFast: opts.fuzzerFailFast,
        redactResponses: true,
    });
}
async function runExfilPathScannerWrapper(manifest, opts) {
    return runExfilPathScanner(manifest, {
        includeAllEdges: opts.exfilIncludeAllEdges,
        maxPathLength: opts.exfilMaxPathLength,
    });
}
async function runPermissionMinimiserScanner(manifest, opts) {
    return runPermissionMinimiser(manifest, {
        tracesDir: opts.tracesDir,
        outputFile: opts.permissionProposalsOutput,
        minConfidence: opts.permissionMinConfidence,
        minRiskReduction: opts.permissionMinRiskReduction,
    });
}
async function runSupplyChainScanner(manifest, opts) {
    return runSupplyChainMCP(manifest, {
        packagePath: opts.supplyChainPackagePath,
        skipVulnScan: opts.supplyChainSkipVulnScan,
        skipLicenseCheck: opts.supplyChainSkipLicenseCheck,
        skipTyposquatCheck: opts.supplyChainSkipTyposquatCheck,
        skipSigstoreCheck: opts.supplyChainSkipSigstoreCheck,
    });
}
async function runSandboxAuditScanner(manifest, opts) {
    return runSandboxAudit(manifest, {
        sourcePath: opts.sandboxSourcePath,
        maxDepth: opts.sandboxMaxDepth,
        exclude: opts.sandboxExclude,
        tools: opts.sandboxTools,
    });
}
async function runCredentialScopeScanner(manifest, opts) {
    return runCredentialScopeAudit(manifest, {
        scanPath: opts.credentialScanPath,
        scanEnvironment: opts.credentialScanEnvironment,
        additionalFiles: opts.credentialAdditionalFiles,
    });
}
// ============================================================================
// Helper Functions
// ============================================================================
/**
 * Calculate SHA256 hash of manifest
 */
async function hashManifest(manifest) {
    const { createHash } = await import("crypto");
    const content = JSON.stringify(manifest, null, 0);
    return createHash("sha256").update(content).digest("hex");
}
/**
 * Calculate overall risk score from findings
 */
function calculateRiskScore(bySeverity, _totalFindings) {
    // Weighted severity scoring
    const weights = {
        critical: 40,
        high: 25,
        medium: 10,
        low: 3,
        info: 0,
    };
    let totalPenalty = 0;
    for (const [severity, count] of Object.entries(bySeverity)) {
        totalPenalty += count * weights[severity];
    }
    // Cap at 100, with exponential decay
    const rawScore = Math.min(100, totalPenalty);
    // Risk score: 100 = maximum risk, 0 = no risk
    return rawScore;
}
/**
 * Determine certification readiness based on findings
 */
function determineCertificationReadiness(bySeverity, riskScore) {
    // Blocked: any critical findings
    if (bySeverity.critical > 0) {
        return "blocked";
    }
    // Needs review: high findings or risk score > 30
    if (bySeverity.high > 0 || riskScore > 30) {
        return "needs-review";
    }
    // Ready: no critical/high, low risk score
    return "ready";
}
// ============================================================================
// Conversion to Certification Findings
// ============================================================================
/**
 * Convert agent scanner findings to certification Finding format
 */
export function agentFindingsToCertificationFindings(result) {
    const findings = [];
    let index = 0;
    for (const scannerResult of result.scanners) {
        for (const finding of scannerResult.findings) {
            findings.push({
                scanner: "semgrep", // Map to existing scanner type for compatibility
                ruleId: `agent:${scannerResult.scanner}:${finding.ruleId}`,
                file: finding.file || "mcp-manifest",
                line: finding.line || 0,
                message: finding.message,
                severity: finding.severity,
                confidence: 100,
                cweIds: finding.cweIds,
                evidence: finding.evidence,
                metadata: {
                    agentScanner: scannerResult.scanner,
                    mcpServer: result.manifest?.name,
                    originalRuleId: finding.ruleId,
                },
            });
            index++;
        }
    }
    return findings;
}
// ============================================================================
// Summary Generation
// ============================================================================
/**
 * Generate a human-readable summary of agent scan results
 */
export function generateAgentScannerSummary(result) {
    const lines = [];
    lines.push("## Agent Security Scan Summary");
    lines.push("");
    // Server info
    if (result.manifest) {
        lines.push(`**MCP Server**: ${result.manifest.name} v${result.manifest.version}`);
    }
    lines.push(`**Scan Time**: ${result.timestamp}`);
    lines.push(`**Duration**: ${(result.totalDuration / 1000).toFixed(2)}s`);
    lines.push("");
    // Overall status
    const statusEmoji = {
        ready: "✅",
        "needs-review": "⚠️",
        blocked: "🚫",
    };
    lines.push(`### Status: ${statusEmoji[result.certificationReadiness]} ${result.certificationReadiness.toUpperCase()}`);
    lines.push(`**Risk Score**: ${result.riskScore}/100`);
    lines.push("");
    // Findings summary
    lines.push("### Findings by Severity");
    lines.push(`- 🔴 Critical: ${result.bySeverity.critical}`);
    lines.push(`- 🟠 High: ${result.bySeverity.high}`);
    lines.push(`- 🟡 Medium: ${result.bySeverity.medium}`);
    lines.push(`- 🟢 Low: ${result.bySeverity.low}`);
    lines.push(`- ℹ️ Info: ${result.bySeverity.info}`);
    lines.push("");
    // Scanners run
    lines.push("### Scanners Run");
    for (const scanner of result.scanners) {
        const status = scanner.success ? "✓" : "✗";
        const count = scanner.findings.length;
        lines.push(`- ${status} ${scanner.scanner}: ${count} finding(s)`);
    }
    lines.push("");
    // Failed scanners
    if (result.failedScanners.length > 0) {
        lines.push("### Failed Scanners");
        for (const scanner of result.failedScanners) {
            lines.push(`- ${scanner}`);
        }
        lines.push("");
    }
    // Recommendations
    lines.push("### Recommendations");
    if (result.bySeverity.critical > 0) {
        lines.push("- ❌ Address all critical findings before certification");
    }
    if (result.bySeverity.high > 0) {
        lines.push("- ⚠️ Review and remediate high-severity findings");
    }
    if (result.certificationReadiness === "ready") {
        lines.push("- ✅ System is ready for certification");
    }
    return lines.join("\n");
}
//# sourceMappingURL=index.js.map