/**
 * DAST (Dynamic Application Security Testing) Scanner
 *
 * Performs runtime vulnerability scanning using Nuclei
 * for web applications.
 *
 * @module scanners/dast
 */
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
export async function checkNucleiAvailable() {
    try {
        const { stdout } = await execAsync("nuclei -version", { timeout: 10000 });
        const version = stdout.match(/v[\d.]+/)?.[0] || stdout.trim();
        return {
            scanner: "nuclei",
            available: true,
            version,
        };
    }
    catch {
        return {
            scanner: "nuclei",
            available: false,
            error: "Nuclei not found. Install from: https://github.com/projectdiscovery/nuclei",
        };
    }
}
function mapSeverity(severity) {
    switch (severity.toLowerCase()) {
        case "critical":
            return "critical";
        case "high":
            return "high";
        case "medium":
            return "medium";
        case "low":
            return "low";
        default:
            return "info";
    }
}
export async function runNuclei(targetUrl, options) {
    const startTime = Date.now();
    try {
        const availability = await checkNucleiAvailable();
        if (!availability.available) {
            return {
                scanner: "nuclei",
                findings: [],
                duration: Date.now() - startTime,
                success: false,
                error: availability.error,
            };
        }
        // Build command
        let command = `nuclei -u "${targetUrl}" -jsonl -silent`;
        // Add template filters
        if (options?.templates && options.templates.length > 0) {
            command += ` -t ${options.templates.join(",")}`;
        }
        else {
            // Default to common security templates
            command += " -tags cve,exposure,misconfiguration,default-login";
        }
        if (options?.severity && options.severity.length > 0) {
            command += ` -severity ${options.severity.join(",")}`;
        }
        else {
            // Default to medium and above
            command += " -severity medium,high,critical";
        }
        if (options?.tags && options.tags.length > 0) {
            command += ` -tags ${options.tags.join(",")}`;
        }
        if (options?.excludeTags && options.excludeTags.length > 0) {
            command += ` -etags ${options.excludeTags.join(",")}`;
        }
        if (options?.rateLimit) {
            command += ` -rl ${options.rateLimit}`;
        }
        else {
            command += " -rl 50"; // Default rate limit
        }
        if (options?.concurrency) {
            command += ` -c ${options.concurrency}`;
        }
        if (options?.headers) {
            for (const [key, value] of Object.entries(options.headers)) {
                command += ` -H "${key}: ${value}"`;
            }
        }
        if (options?.followRedirects === false) {
            command += " -no-redirect";
        }
        const { stdout, stderr } = await execAsync(command, {
            timeout: options?.timeout || 300000, // 5 minutes default
            maxBuffer: 50 * 1024 * 1024,
        }).catch((error) => {
            if (error.stdout) {
                return { stdout: error.stdout, stderr: error.stderr || "" };
            }
            throw error;
        });
        const findings = [];
        const lines = stdout.split("\n").filter((l) => l.trim());
        for (const line of lines) {
            try {
                const result = JSON.parse(line);
                findings.push({
                    scanner: "nuclei",
                    ruleId: `nuclei:${result["template-id"]}`,
                    file: result.matched || result.host,
                    line: 1,
                    message: `${result.info.name}: ${result.info.description || result.matched}`,
                    severity: mapSeverity(result.info.severity),
                    confidence: 100,
                    cweIds: result.info.classification?.["cwe-id"],
                    metadata: {
                        host: result.host,
                        matchedAt: result["matched-at"],
                        template: result.template,
                        tags: result.info.tags,
                        references: result.info.reference,
                        cveIds: result.info.classification?.["cve-id"],
                        cvssScore: result.info.classification?.cvss?.score,
                        curlCommand: result["curl-command"],
                        extractedResults: result["extracted-results"],
                    },
                });
            }
            catch {
                // Skip non-JSON lines
            }
        }
        return {
            scanner: "nuclei",
            findings,
            duration: Date.now() - startTime,
            success: true,
            version: availability.version,
        };
    }
    catch (error) {
        return {
            scanner: "nuclei",
            findings: [],
            duration: Date.now() - startTime,
            success: false,
            error: error instanceof Error ? error.message : "Unknown error",
        };
    }
}
export async function runDASTScan(targetUrl, options) {
    return runNuclei(targetUrl, options);
}
export async function runQuickDASTScan(targetUrl) {
    return runNuclei(targetUrl, {
        timeout: 60000,
        severity: ["high", "critical"],
        tags: ["cve", "exposure", "default-login"],
        rateLimit: 100,
    });
}
export function generateDASTReport(targetUrl, result) {
    const summary = {
        total: result.findings.length,
        critical: result.findings.filter((f) => f.severity === "critical").length,
        high: result.findings.filter((f) => f.severity === "high").length,
        medium: result.findings.filter((f) => f.severity === "medium").length,
        low: result.findings.filter((f) => f.severity === "low").length,
    };
    return {
        target: targetUrl,
        scanTime: new Date().toISOString(),
        duration: result.duration,
        findings: result.findings,
        summary,
    };
}
export function formatDASTReport(report) {
    const lines = [];
    lines.push("# DAST Scan Report");
    lines.push("");
    lines.push(`**Target:** ${report.target}`);
    lines.push(`**Scan Time:** ${report.scanTime}`);
    lines.push(`**Duration:** ${report.duration}ms`);
    lines.push("");
    lines.push("## Summary");
    lines.push("");
    lines.push("| Severity | Count |");
    lines.push("|----------|-------|");
    lines.push(`| Critical | ${report.summary.critical} |`);
    lines.push(`| High | ${report.summary.high} |`);
    lines.push(`| Medium | ${report.summary.medium} |`);
    lines.push(`| Low | ${report.summary.low} |`);
    lines.push(`| **Total** | **${report.summary.total}** |`);
    lines.push("");
    if (report.findings.length > 0) {
        lines.push("## Findings");
        lines.push("");
        for (const finding of report.findings) {
            lines.push(`### [${finding.severity.toUpperCase()}] ${finding.ruleId}`);
            lines.push("");
            lines.push(finding.message);
            lines.push("");
            const curlCommand = finding.metadata?.curlCommand;
            if (curlCommand) {
                lines.push("**Reproduce:**");
                lines.push("```bash");
                lines.push(curlCommand);
                lines.push("```");
                lines.push("");
            }
            const references = finding.metadata?.references;
            if (references) {
                lines.push("**References:**");
                for (const ref of references) {
                    lines.push(`- ${ref}`);
                }
                lines.push("");
            }
        }
    }
    return lines.join("\n");
}
//# sourceMappingURL=dast.js.map