/**
 * Exploit Chain Analyzer
 *
 * Automatically chains multiple vulnerabilities into attack paths.
 * Identifies how individual findings can be combined for greater impact.
 *
 * Chain types:
 * - Read → RCE: Info disclosure enables code injection
 * - Auth bypass → Data: Skip auth to access database
 * - SSRF → Internal: External request to internal API
 * - XSS → Session: Script injection to cookie theft
 *
 * @module agents/exploit-chain
 */
// ============================================================================
// Chain Detection Rules
// ============================================================================
const CHAIN_RULES = [
    // Information Disclosure → Code Execution
    {
        id: "chain-info-rce",
        name: "Information Disclosure to RCE",
        entry: {
            categories: ["sensitive-disclosure", "path-traversal"],
        },
        target: {
            categories: ["injection", "command-injection", "code-injection"],
        },
        escalation: "critical",
        reason: "Information leak (config, source) enables targeted code execution",
        mitreIds: ["T1005", "T1059"],
        difficulty: "moderate",
    },
    // SSRF → Internal Access
    {
        id: "chain-ssrf-internal",
        name: "SSRF to Internal Network Access",
        entry: {
            categories: ["ssrf"],
        },
        target: {
            categories: ["authentication", "authorization", "api-security"],
        },
        escalation: "critical",
        reason: "Server-side request enables access to internal services and APIs",
        mitreIds: ["T1090", "T1552"],
        difficulty: "easy",
    },
    // XSS → Session Hijack
    {
        id: "chain-xss-session",
        name: "XSS to Session Hijacking",
        entry: {
            categories: ["xss", "injection"],
        },
        target: {
            categories: ["session-management", "authentication"],
        },
        escalation: "high",
        reason: "Client-side script injection enables session token theft",
        mitreIds: ["T1185", "T1539"],
        difficulty: "easy",
    },
    // Auth Bypass → Data Access
    {
        id: "chain-auth-data",
        name: "Authentication Bypass to Data Exfiltration",
        entry: {
            categories: ["authentication", "authorization"],
        },
        target: {
            categories: ["sensitive-disclosure", "pii-exposure", "api-security"],
        },
        escalation: "critical",
        reason: "Bypassing authentication enables access to protected data",
        mitreIds: ["T1078", "T1530"],
        difficulty: "moderate",
    },
    // SQL Injection → Full Compromise
    {
        id: "chain-sqli-rce",
        name: "SQL Injection to Database Takeover",
        entry: {
            categories: ["injection", "sql-injection"],
        },
        target: {
            categories: ["authentication", "sensitive-disclosure"],
        },
        escalation: "critical",
        reason: "SQL injection can dump credentials and enable further attacks",
        mitreIds: ["T1190", "T1003"],
        difficulty: "moderate",
    },
    // Path Traversal → Sensitive Data
    {
        id: "chain-lfi-secrets",
        name: "Path Traversal to Secret Exposure",
        entry: {
            categories: ["path-traversal"],
        },
        target: {
            categories: ["cryptography", "secret-management", "sensitive-disclosure"],
        },
        escalation: "critical",
        reason: "File read vulnerability exposes configuration and secrets",
        mitreIds: ["T1005", "T1552.001"],
        difficulty: "easy",
    },
    // Insecure Deserialization → RCE
    {
        id: "chain-deser-rce",
        name: "Insecure Deserialization to RCE",
        entry: {
            categories: ["deserialization", "input-validation"],
        },
        target: {
            categories: ["code-injection", "command-injection"],
        },
        escalation: "critical",
        reason: "Deserialization of untrusted data enables arbitrary code execution",
        mitreIds: ["T1059", "T1027"],
        difficulty: "hard",
    },
    // Race Condition → Privilege Escalation
    {
        id: "chain-race-privesc",
        name: "Race Condition to Privilege Escalation",
        entry: {
            categories: ["race-condition", "logic-flaw"],
        },
        target: {
            categories: ["authorization", "authentication"],
        },
        escalation: "high",
        reason: "Race condition in access checks enables privilege escalation",
        mitreIds: ["T1068", "T1548"],
        difficulty: "hard",
    },
    // Open Redirect → Phishing/Token Theft
    {
        id: "chain-redirect-phish",
        name: "Open Redirect to Token Theft",
        entry: {
            categories: ["open-redirect"],
        },
        target: {
            categories: ["session-management", "authentication"],
        },
        escalation: "medium",
        reason: "Open redirect can steal OAuth tokens via redirect_uri manipulation",
        mitreIds: ["T1566", "T1528"],
        difficulty: "easy",
    },
    // Memory Safety → Code Execution
    {
        id: "chain-memory-rce",
        name: "Memory Corruption to RCE",
        entry: {
            categories: ["memory-safety", "binary-security"],
        },
        target: {
            categories: ["code-injection"],
        },
        escalation: "critical",
        reason: "Buffer overflow or use-after-free enables arbitrary code execution",
        mitreIds: ["T1203", "T1211"],
        difficulty: "expert",
    },
];
// ============================================================================
// Severity Utilities
// ============================================================================
const SEVERITY_ORDER = {
    critical: 4,
    high: 3,
    medium: 2,
    low: 1,
    info: 0,
};
/**
 * Get numeric value for severity
 */
function severityValue(severity) {
    return SEVERITY_ORDER[severity] || 0;
}
/**
 * Calculate chain severity based on escalation
 */
function calculateChainSeverity(steps, rule) {
    // Get max severity from steps
    const maxStepSeverity = Math.max(...steps.map(s => severityValue(s.finding.severity)));
    // Apply escalation
    const escalatedValue = severityValue(rule.escalation);
    // Chain severity is the higher of max step or escalation
    const finalValue = Math.max(maxStepSeverity, escalatedValue);
    // Map back to severity
    for (const [sev, val] of Object.entries(SEVERITY_ORDER)) {
        if (val === finalValue)
            return sev;
    }
    return "critical";
}
/**
 * Calculate confidence based on finding confidences
 */
function calculateChainConfidence(steps) {
    if (steps.length === 0)
        return 0;
    // Chain confidence is product of individual confidences
    const product = steps.reduce((acc, step) => acc * (step.finding.confidence / 100), 1);
    // Apply a boost for multi-step chains (they're more certain if all steps exist)
    const boost = steps.length >= 2 ? 1.1 : 1;
    return Math.min(Math.round(product * 100 * boost), 99);
}
function buildFindingGraph(findings) {
    const nodes = findings.map(f => ({
        finding: f,
        category: f.category,
        file: f.evidence?.match(/File:\s*([^\s:]+)/)?.[1],
        connections: [],
    }));
    // Connect findings that are related (same file, complementary categories)
    for (let i = 0; i < nodes.length; i++) {
        for (let j = 0; j < nodes.length; j++) {
            if (i === j)
                continue;
            const a = nodes[i];
            const b = nodes[j];
            // Same file = potentially related
            if (a.file && a.file === b.file) {
                a.connections.push(b);
            }
            // Check if categories can chain
            for (const rule of CHAIN_RULES) {
                const aIsEntry = rule.entry.categories.includes(a.category);
                const bIsTarget = rule.target.categories.includes(b.category);
                if (aIsEntry && bIsTarget) {
                    if (!a.connections.includes(b)) {
                        a.connections.push(b);
                    }
                }
            }
        }
    }
    return nodes;
}
function findChainablePairs(nodes) {
    const candidates = [];
    for (const rule of CHAIN_RULES) {
        // Find entry points
        const entryNodes = nodes.filter(n => rule.entry.categories.includes(n.category) &&
            (!rule.entry.severityMin || severityValue(n.finding.severity) >= severityValue(rule.entry.severityMin)));
        // Find targets
        const targetNodes = nodes.filter(n => rule.target.categories.includes(n.category));
        // Find pivots (if specified)
        const pivotNodes = rule.pivot
            ? nodes.filter(n => rule.pivot.categories.includes(n.category))
            : [];
        // Generate candidates
        for (const entry of entryNodes) {
            for (const target of targetNodes) {
                // Direct chain (entry → target)
                if (entry.finding.id !== target.finding.id) {
                    candidates.push({
                        entry: entry.finding,
                        target: target.finding,
                        rule,
                    });
                }
                // Chain with pivot (entry → pivot → target)
                for (const pivot of pivotNodes) {
                    if (entry.finding.id !== pivot.finding.id &&
                        pivot.finding.id !== target.finding.id &&
                        entry.finding.id !== target.finding.id) {
                        candidates.push({
                            entry: entry.finding,
                            target: target.finding,
                            pivot: pivot.finding,
                            rule,
                        });
                    }
                }
            }
        }
    }
    // Deduplicate
    const seen = new Set();
    return candidates.filter(c => {
        const key = `${c.rule.id}:${c.entry.id}:${c.target.id}:${c.pivot?.id || ""}`;
        if (seen.has(key))
            return false;
        seen.add(key);
        return true;
    });
}
/**
 * Generate attack scenario for a chain
 */
function generateAttackScenario(candidate) {
    const { entry, target, pivot, rule } = candidate;
    let scenario = `1. Attacker exploits ${entry.category} vulnerability (${entry.id}) to gain initial foothold. `;
    if (pivot) {
        scenario += `2. Using this access, attacker leverages ${pivot.category} vulnerability (${pivot.id}) to escalate. `;
        scenario += `3. Finally, attacker exploits ${target.category} vulnerability (${target.id}) to achieve ${rule.reason.toLowerCase()}. `;
    }
    else {
        scenario += `2. Attacker then exploits ${target.category} vulnerability (${target.id}) to achieve ${rule.reason.toLowerCase()}. `;
    }
    return scenario;
}
/**
 * Build exploit chain from candidate
 */
function buildExploitChain(candidate, chainId) {
    const steps = [];
    // Entry step
    steps.push({
        findingId: candidate.entry.id,
        finding: candidate.entry,
        role: "entry",
        enables: candidate.pivot
            ? `Access for ${candidate.pivot.category}`
            : `Access for ${candidate.target.category}`,
        techniques: [candidate.rule.mitreIds[0] || "T1190"],
    });
    // Pivot step (if exists)
    if (candidate.pivot) {
        steps.push({
            findingId: candidate.pivot.id,
            finding: candidate.pivot,
            role: "pivot",
            prerequisite: `Requires ${candidate.entry.category}`,
            enables: `Access for ${candidate.target.category}`,
            techniques: [candidate.rule.mitreIds[1] || candidate.rule.mitreIds[0]],
        });
    }
    // Target step
    steps.push({
        findingId: candidate.target.id,
        finding: candidate.target,
        role: "target",
        prerequisite: candidate.pivot
            ? `Requires ${candidate.pivot.category}`
            : `Requires ${candidate.entry.category}`,
        techniques: [candidate.rule.mitreIds[candidate.rule.mitreIds.length - 1]],
    });
    const totalSeverity = calculateChainSeverity(steps, candidate.rule);
    const confidence = calculateChainConfidence(steps);
    const attackScenario = generateAttackScenario(candidate);
    return {
        id: `chain-${String(chainId).padStart(3, "0")}`,
        name: candidate.rule.name,
        steps,
        totalSeverity,
        originalSeverities: steps.map(s => s.finding.severity),
        confidence,
        attackScenario,
        mitreAttackIds: candidate.rule.mitreIds,
        impact: candidate.rule.reason,
        difficulty: candidate.rule.difficulty,
    };
}
// ============================================================================
// Main Entry Point
// ============================================================================
/**
 * Analyze findings for exploit chains
 */
export async function analyzeExploitChains(findings, _projectPath) {
    if (findings.length < 2) {
        return {
            totalFindings: findings.length,
            chainsDetected: 0,
            chains: [],
            riskScore: 0,
            recommendations: ["Insufficient findings to detect chains. Run more scanners."],
        };
    }
    // Build finding graph
    const nodes = buildFindingGraph(findings);
    // Find chainable pairs
    const candidates = findChainablePairs(nodes);
    // Build exploit chains
    const chains = [];
    let chainId = 1;
    for (const candidate of candidates) {
        const chain = buildExploitChain(candidate, chainId++);
        chains.push(chain);
    }
    // Sort by severity and confidence
    chains.sort((a, b) => {
        const sevDiff = severityValue(b.totalSeverity) - severityValue(a.totalSeverity);
        if (sevDiff !== 0)
            return sevDiff;
        return b.confidence - a.confidence;
    });
    // Limit to top chains to avoid noise
    const topChains = chains.slice(0, 20);
    // Calculate risk score (0-100)
    let riskScore = 0;
    for (const chain of topChains) {
        const sevScore = severityValue(chain.totalSeverity) * 10;
        const confScore = chain.confidence / 10;
        riskScore += sevScore + confScore;
    }
    riskScore = Math.min(Math.round(riskScore / Math.max(topChains.length, 1)), 100);
    // Generate recommendations
    const recommendations = [];
    const criticalChains = topChains.filter(c => c.totalSeverity === "critical");
    if (criticalChains.length > 0) {
        recommendations.push(`CRITICAL: ${criticalChains.length} exploit chains lead to critical impact. ` +
            `Prioritize fixing entry point vulnerabilities: ${[...new Set(criticalChains.flatMap(c => c.steps[0].finding.category))].join(", ")}`);
    }
    const easyChains = topChains.filter(c => c.difficulty === "easy" || c.difficulty === "trivial");
    if (easyChains.length > 0) {
        recommendations.push(`${easyChains.length} chains are easy to exploit. Focus on these first.`);
    }
    // Category-specific recommendations
    const entryCategories = [...new Set(topChains.map(c => c.steps[0].finding.category))];
    if (entryCategories.includes("injection")) {
        recommendations.push("Injection vulnerabilities are common entry points. Implement parameterized queries and input validation.");
    }
    if (entryCategories.includes("ssrf")) {
        recommendations.push("SSRF vulnerabilities enable internal network access. Implement URL allowlisting.");
    }
    return {
        totalFindings: findings.length,
        chainsDetected: topChains.length,
        chains: topChains,
        riskScore,
        recommendations,
    };
}
/**
 * Convert exploit chains to certification findings
 */
export function exploitChainsToFindings(result) {
    return result.chains.map((chain) => ({
        id: chain.id,
        severity: chain.totalSeverity,
        category: "exploit-chain",
        description: `${chain.name}: ${chain.attackScenario}`,
        evidence: chain.steps.map(s => `Step ${s.role}: ${s.finding.id} (${s.finding.category}) → ${s.enables || "final target"}`).join("\n"),
        confidence: chain.confidence,
        verifications: [],
        created_at: new Date().toISOString(),
        scanner_source: "exploit-chain-analyzer",
        metadata: {
            stepsCount: chain.steps.length,
            originalSeverities: chain.originalSeverities,
            mitreAttackIds: chain.mitreAttackIds,
            impact: chain.impact,
            difficulty: chain.difficulty,
            chainedFindingIds: chain.steps.map(s => s.findingId),
        },
    }));
}
/**
 * Get summary of exploit chain analysis
 */
export function getChainSummary(result) {
    if (result.chainsDetected === 0) {
        return "No exploit chains detected. Individual vulnerabilities may still pose risk.";
    }
    const criticalCount = result.chains.filter(c => c.totalSeverity === "critical").length;
    const highCount = result.chains.filter(c => c.totalSeverity === "high").length;
    let summary = `Detected ${result.chainsDetected} exploit chains. `;
    summary += `Risk score: ${result.riskScore}/100. `;
    if (criticalCount > 0) {
        summary += `${criticalCount} critical chains require immediate attention. `;
    }
    if (highCount > 0) {
        summary += `${highCount} high-severity chains should be prioritized. `;
    }
    return summary;
}
//# sourceMappingURL=exploit-chain.js.map