import { debug } from "./core/logger.js";
import { safeJsonParse } from "./core/helpers.js";
/**
 * 依赖漏洞扫描器 — 解析 package.json/pom.xml/build.gradle，基于内置漏洞规则库
 * 检测已知漏洞和未锁定版本。纯规则驱动，零 AI 依赖，不调用外部 API。
 */
/** 内置漏洞规则库: 各语言生态的已知高风险依赖版本（包名 → 最低安全版本 → 漏洞描述） */
// ── 漏洞数据库（硬编码规则库，零 AI 依赖） ──
const VULN_DB = [
    // Java 生态
    { package: "log4j-core", minSafe: "2.17.0", severity: "critical", issue: "Log4Shell RCE (CVE-2021-44228)" },
    { package: "fastjson", minSafe: "1.2.83", severity: "critical", issue: "反序列化远程代码执行" },
    { package: "jackson-databind", minSafe: "2.13.0", severity: "critical", issue: "反序列化漏洞" },
    { package: "shiro-core", minSafe: "1.10.0", severity: "high", issue: "认证绕过漏洞" },
    // Node.js 生态
    { package: "lodash", minSafe: "4.17.21", severity: "high", issue: "原型污染 (CVE-2021-23337)" },
    { package: "express", minSafe: "4.18.0", severity: "medium", issue: "开放重定向" },
    { package: "axios", minSafe: "0.21.1", severity: "high", issue: "SSRF 漏洞" },
    { package: "node-fetch", minSafe: "2.6.7", severity: "medium", issue: "信息泄露" },
    { package: "jsonwebtoken", minSafe: "9.0.0", severity: "high", issue: "密钥验证绕过" },
    { package: "bcrypt", minSafe: "5.0.0", severity: "medium", issue: "轮次限制不足" },
    { package: "multer", minSafe: "1.4.4", severity: "medium", issue: "内存耗尽" },
    { package: "dompurify", minSafe: "2.3.3", severity: "high", issue: "XSS 绕过" },
    { package: "moment", minSafe: "2.29.4", severity: "low", issue: "ReDoS 拒绝服务" },
    { package: "yargs-parser", minSafe: "18.1.2", severity: "high", issue: "原型污染" },
    { package: "ua-parser-js", minSafe: "0.7.31", severity: "critical", issue: "供应链攻击" },
    { package: "event-source", minSafe: "2.0.1", severity: "high", issue: "CRLF 注入" },
];
/** 未锁定版本的正则匹配模式 — 匹配 "*"、"x"、"X" 或空字符串等无精确版本号的情况 */
// ── 未锁定版本标识 ──
const UNSCOPED_PATTERN = /^\s*[*xX]?\s*$/;
// ── 版本比较 ──
/**
 * 比较两个 semver 风格的版本号，判断 a 是否大于 b。
 * - 按点号分隔，逐段数值比较
 * - 段数不足时以 0 补齐
 * - 预发布后缀（如 -beta.1、-rc.2）在比较前被剥离
 * @param a - 版本号 a
 * @param b - 版本号 b
 * @returns 当 a > b 时返回 true
 */
function semverGt(a, b) {
    const normalize = (v) => v.replace(/-[\w.]+$/, "").split(".").map((seg) => {
        const n = parseInt(seg, 10);
        return Number.isNaN(n) ? 0 : n;
    });
    const segsA = normalize(a);
    const segsB = normalize(b);
    const maxLen = Math.max(segsA.length, segsB.length);
    for (let i = 0; i < maxLen; i++) {
        const pa = segsA[i] ?? 0;
        const pb = segsB[i] ?? 0;
        if (pa > pb)
            return true;
        if (pa < pb)
            return false;
    }
    return false;
}
/**
 * 检查版本字符串是否为未锁定版本（如 "*"、"latest"、"x"、空字符串）。
 * @param version - 版本字符串
 * @returns 未锁定时返回 true
 */
function isUnscoped(version) {
    const trimmed = version.trim().toLowerCase();
    return trimmed === "" || trimmed === "latest" || UNSCOPED_PATTERN.test(trimmed);
}
// ── 版本提取 ──
/**
 * 从常见版本说明符中提取版本号 — 支持 ^/~/>=/<=/ 前缀。
 * @param versionSpecifier - 版本说明符字符串
 * @returns 提取的纯版本号（如 "1.2.3"），URL/git 引用返回 null，无法解析时返回原始值
 */
function extractVersion(versionSpecifier) {
    const v = versionSpecifier.trim();
    // 跳过 URL 引用和 git 引用 — 这些不参与版本扫描
    if (/^(https?:|git[:+]|github:|file:|git@|ssh:|\/)/i.test(v)) {
        return null;
    }
    // 跳过 workspace/file/link 等本地协议依赖 — 这些在本地解析，不可扫描
    if (/^(workspace|file|link|portal|npm):/i.test(v)) {
        return null;
    }
    // 提取纯数字版本: 剥离前缀修饰符 ^, ~, >=, <=, >, <, =
    const match = v.match(/^[\^~>=<\s]*([\d][\d.]*)/);
    return match ? match[1] : v;
}
// ── 漏洞匹配 ──
/**
 * 检查指定包名和版本是否存在已知漏洞 — 遍历漏洞规则库进行匹配。
 * @param pkgName - 包名（如 "lodash"）
 * @param version - 已安装版本号
 * @returns 存在漏洞时返回漏洞详情，否则返回 undefined
 */
function checkVulnerability(pkgName, version) {
    for (const entry of VULN_DB) {
        if (pkgName === entry.package) {
            // 已安装版本低于最低安全版本时判定为漏洞
            if (!semverGt(version, entry.minSafe)) {
                // 预发布版本检查: 标准化版本等于 minSafe 但原始版本含预发布后缀时，
                // 按 semver 规范预发布版本 < 正式版本，因此仍视为漏洞
                const normalizedVersion = version.replace(/-[\w.]+$/, "");
                if (normalizedVersion === entry.minSafe && /-[\w.]+$/.test(version)) {
                    return {
                        package: pkgName,
                        installed_version: version,
                        severity: entry.severity,
                        issue: entry.issue,
                        fixed_in: entry.minSafe,
                    };
                }
                return {
                    package: pkgName,
                    installed_version: version,
                    severity: entry.severity,
                    issue: entry.issue,
                    fixed_in: entry.minSafe,
                };
            }
        }
    }
    return undefined;
}
/** 解析 package.json — 提取 dependencies 和 devDependencies 中的包名和版本 */
function parsePackageJson(content) {
    const deps = [];
    const pkg = safeJsonParse(content);
    if (!pkg)
        return deps;
    const extract = (obj, isDev) => {
        if (!obj)
            return;
        for (const [name, ver] of Object.entries(obj)) {
            deps.push({ name, version: ver, isDev });
        }
    };
    extract(pkg.dependencies, false);
    extract(pkg.devDependencies, true);
    return deps;
}
/** 解析 pom.xml — 匹配 groupId/artifactId/version 三元组，支持多行格式 */
function parsePomXml(content) {
    const deps = [];
    // 匹配 <groupId>...<artifactId>...<version> 代码块，支持多行内容中的非贪婪匹配
    const depPattern = /<groupId>\s*([\w.-]+)\s*<\/groupId>\s*<artifactId>\s*([\w.-]+)\s*<\/artifactId>\s*<version>\s*([\w.-]+)\s*<\/version>/g;
    let match;
    while ((match = depPattern.exec(content)) !== null) {
        deps.push({
            groupId: match[1],
            artifactId: match[2],
            version: match[3],
        });
    }
    return deps;
}
/** 解析 build.gradle — 匹配 group:artifact:version 格式（如 'org.apache:log4j-core:2.14.1'） */
function parseBuildGradle(content) {
    const deps = [];
    // 匹配 group:artifact:version 格式（如 'org.apache.logging.log4j:log4j-core:2.14.1'）
    const depPattern = /['"]([\w.-]+):([\w.-]+):([\d.]+(?:-[a-zA-Z][\w.-]*)?)['"]/g;
    let match;
    while ((match = depPattern.exec(content)) !== null) {
        deps.push({
            group: match[1],
            artifact: match[2],
            version: match[3],
        });
    }
    return deps;
}
// ── 摘要生成 ──
/**
 * 构建扫描结果摘要 — 统计漏洞数量和未锁定版本数量，生成中文摘要文本。
 * @param total - 扫描的依赖总数
 * @param vulnerabilities - 发现的漏洞列表
 * @param unscoped - 未锁定版本列表
 * @returns 中文格式的扫描摘要
 */
function buildSummary(total, vulnerabilities, unscoped) {
    if (total === 0) {
        return "未发现可解析的依赖项";
    }
    const criticalCount = vulnerabilities.filter((v) => v.severity === "critical").length;
    const highCount = vulnerabilities.filter((v) => v.severity === "high").length;
    const vulnParts = [];
    if (vulnerabilities.length > 0) {
        vulnParts.push(`发现 ${vulnerabilities.length} 个漏洞`);
        const severityParts = [];
        if (criticalCount > 0)
            severityParts.push(`${criticalCount} critical`);
        if (highCount > 0)
            severityParts.push(`${highCount} high`);
        if (severityParts.length > 0) {
            vulnParts.push(`(${severityParts.join(", ")})`);
        }
    }
    const unscopedPart = unscoped.length > 0 ? `，${unscoped.length} 个未锁定版本` : "";
    if (vulnParts.length > 0) {
        return `扫描了 ${total} 个依赖，${vulnParts.join("")}${unscopedPart}`;
    }
    return `扫描了 ${total} 个依赖，未发现已知漏洞${unscopedPart}`;
}
// ── 主函数 ──
/**
 * 扫描依赖文件中的已知漏洞 — 根据文件名自动选择解析器。
 * 支持格式: package.json (Node.js)、pom.xml (Java/Maven)、build.gradle (Java/Gradle)。
 * @param content - 依赖文件内容
 * @param filename - 文件名（用于判断解析器类型）
 * @returns 扫描结果，包含漏洞列表、未锁定版本和中文摘要
 */
export function scanDependencies(content, filename) {
    debug("依赖扫描", `开始扫描 ${filename}`);
    const baseName = filename.split("/").pop()?.toLowerCase() ?? "";
    // ── 解析 package.json ──
    if (baseName === "package.json") {
        debug("依赖扫描", "检测到 package.json，使用 Node.js 解析器");
        const parsed = parsePackageJson(content);
        const vulnerabilities = [];
        const unscoped_versions = [];
        let scannedCount = 0;
        for (const dep of parsed) {
            const extracted = extractVersion(dep.version);
            if (extracted === null)
                continue; // URL / git 引用，跳过
            scannedCount++;
            if (isUnscoped(extracted)) {
                unscoped_versions.push(`${dep.name}: ${dep.version}`);
                continue;
            }
            const vuln = checkVulnerability(dep.name, extracted);
            if (vuln) {
                vulnerabilities.push(vuln);
            }
        }
        return {
            file: filename,
            dependencies_scanned: scannedCount,
            vulnerabilities,
            unscoped_versions,
            summary: buildSummary(scannedCount, vulnerabilities, unscoped_versions),
        };
    }
    // ── 解析 pom.xml ──
    if (baseName === "pom.xml") {
        debug("依赖扫描", "检测到 pom.xml，使用 Maven 解析器");
        const parsed = parsePomXml(content);
        const vulnerabilities = [];
        let scannedCount = 0;
        for (const dep of parsed) {
            scannedCount++;
            const vuln = checkVulnerability(dep.artifactId, dep.version);
            if (vuln) {
                vulnerabilities.push(vuln);
            }
        }
        return {
            file: filename,
            dependencies_scanned: scannedCount,
            vulnerabilities,
            unscoped_versions: [],
            summary: buildSummary(scannedCount, vulnerabilities, []),
        };
    }
    // ── 解析 build.gradle ──
    if (baseName === "build.gradle" || baseName === "build.gradle.kts") {
        debug("依赖扫描", "检测到 build.gradle，使用 Gradle 解析器");
        const parsed = parseBuildGradle(content);
        const vulnerabilities = [];
        let scannedCount = 0;
        for (const dep of parsed) {
            scannedCount++;
            const vuln = checkVulnerability(dep.artifact, dep.version);
            if (vuln) {
                vulnerabilities.push(vuln);
            }
        }
        return {
            file: filename,
            dependencies_scanned: scannedCount,
            vulnerabilities,
            unscoped_versions: [],
            summary: buildSummary(scannedCount, vulnerabilities, []),
        };
    }
    // ── 不支持的文件格式，返回空结果 ──
    debug("依赖扫描", `不支持的文件格式 ${filename}`);
    return {
        file: filename,
        dependencies_scanned: 0,
        vulnerabilities: [],
        unscoped_versions: [],
        summary: "不支持的依赖文件格式",
    };
}
//# sourceMappingURL=dependency_scanner.js.map