const TIMEOUT_MS = 5000;
// ---------------------------------------------------------------------------
// npm advisory API
// ---------------------------------------------------------------------------
/**
 * Fetch CVE findings for an npm package from the npm advisory bulk API.
 * POST https://registry.npmjs.org/-/npm/v1/security/advisories/bulk
 * Body: { [pkg]: [version] }
 * Returns [] on any error (network, timeout, parse).
 */
export async function fetchNpmCve(pkg, version) {
    const body = JSON.stringify({ [pkg]: [version] });
    return makeHttpsPost('https://registry.npmjs.org/-/npm/v1/security/advisories/bulk', body, (raw) => parseNpmAdvisory(raw));
}
function parseNpmAdvisory(raw) {
    const parsed = JSON.parse(raw);
    const findings = [];
    for (const advisories of Object.values(parsed)) {
        for (const adv of advisories) {
            const cveIds = adv.cves ?? [];
            const severity = normalizeSeverity(adv.severity);
            for (const cveId of cveIds) {
                findings.push({
                    cveId,
                    severity,
                    title: adv.title ?? 'Unknown vulnerability',
                    affectedVersions: adv.vulnerable_versions ?? 'unknown',
                    fixedIn: adv.patched_versions ?? null,
                });
            }
            // If no CVE IDs but has advisory data, include with advisory ID placeholder
            if (cveIds.length === 0 && adv.title) {
                findings.push({
                    cveId: 'ADVISORY',
                    severity,
                    title: adv.title,
                    affectedVersions: adv.vulnerable_versions ?? 'unknown',
                    fixedIn: adv.patched_versions ?? null,
                });
            }
        }
    }
    return findings;
}
// ---------------------------------------------------------------------------
// PyPI advisory API
// ---------------------------------------------------------------------------
/**
 * Fetch CVE findings for a PyPI package from the PyPI JSON API.
 * GET https://pypi.org/pypi/${pkg}/${version}/json
 * Extracts vulnerabilities array if present.
 * Returns [] on any error.
 */
export async function fetchPypiCve(pkg, version) {
    return makeHttpsGet(`https://pypi.org/pypi/${encodeURIComponent(pkg)}/${encodeURIComponent(version)}/json`, (raw) => parsePypiAdvisory(raw));
}
function parsePypiAdvisory(raw) {
    const parsed = JSON.parse(raw);
    const vulns = parsed.vulnerabilities ?? [];
    return vulns.map((v) => {
        const cveAlias = v.aliases?.find((a) => a.startsWith('CVE-')) ?? v.id ?? 'PYPI-ADVISORY';
        return {
            cveId: cveAlias,
            severity: normalizeSeverity(v.severity?.[0]?.type),
            title: v.details ?? 'PyPI vulnerability',
            affectedVersions: 'see advisory',
            fixedIn: v.fixed_in?.[0] ?? null,
        };
    });
}
// ---------------------------------------------------------------------------
// crates.io / RUSTSEC stub
// ---------------------------------------------------------------------------
/**
 * Fetch CVE findings for a Rust crate.
 * NOTE: crates.io does not expose a public advisory/vulnerability API endpoint.
 * The RUSTSEC database (https://rustsec.org) is the authoritative source but
 * requires parsing a git repository or using the `cargo-audit` CLI.
 * This function returns [] — integration with cargo-audit CLI is a future enhancement.
 */
export function fetchCargoCve(_pkg, _version) {
    // crates.io advisory API is not publicly available via HTTP JSON.
    // Full integration requires cargo-audit CLI or parsing the RUSTSEC advisory DB git repo.
    // Returning empty array as a safe stub — no false positives.
    return Promise.resolve([]);
}
// ---------------------------------------------------------------------------
// Shared HTTP helpers
// ---------------------------------------------------------------------------
async function makeHttpsGet(url, parser) {
    return new Promise((resolve) => {
        import('node:https')
            .then(({ default: https }) => {
            /* v8 ignore start */
            const req = https.get(url, { timeout: TIMEOUT_MS }, (res) => {
                let data = '';
                res.on('data', (chunk) => {
                    data += chunk.toString();
                });
                res.on('end', () => {
                    try {
                        resolve(parser(data));
                    }
                    catch {
                        resolve([]);
                    }
                });
            });
            req.on('error', () => {
                resolve([]);
            });
            req.on('timeout', () => {
                req.destroy();
                resolve([]);
            });
            /* v8 ignore stop */
        })
            /* v8 ignore start */
            .catch(() => {
            resolve([]);
        });
        /* v8 ignore stop */
    });
}
async function makeHttpsPost(url, body, parser) {
    return new Promise((resolve) => {
        import('node:https')
            .then(({ default: https }) => {
            /* v8 ignore start */
            const parsedUrl = new URL(url);
            const options = {
                hostname: parsedUrl.hostname,
                path: parsedUrl.pathname + parsedUrl.search,
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Content-Length': Buffer.byteLength(body),
                    'User-Agent': 'planu/1.0',
                },
                timeout: TIMEOUT_MS,
            };
            const req = https.request(options, (res) => {
                let data = '';
                res.on('data', (chunk) => {
                    data += chunk.toString();
                });
                res.on('end', () => {
                    try {
                        resolve(parser(data));
                    }
                    catch {
                        resolve([]);
                    }
                });
            });
            req.on('error', () => {
                resolve([]);
            });
            req.on('timeout', () => {
                req.destroy();
                resolve([]);
            });
            req.write(body);
            req.end();
            /* v8 ignore stop */
        })
            /* v8 ignore start */
            .catch(() => {
            resolve([]);
        });
        /* v8 ignore stop */
    });
}
function normalizeSeverity(raw) {
    const s = (raw ?? '').toLowerCase();
    if (s === 'critical') {
        return 'critical';
    }
    if (s === 'high') {
        return 'high';
    }
    if (s === 'moderate' || s === 'medium') {
        return 'medium';
    }
    return 'low';
}
//# sourceMappingURL=cve-fetcher.js.map