"use strict";
/**
 * Layer 3: OSV/Security Advisory Integration
 * Checks packages against OSV.dev for known vulnerabilities
 *
 * Features:
 * - Queries OSV.dev API for known malicious/vulnerable packages
 * - Caches advisories for efficiency
 * - Supports npm and PyPI ecosystems
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.advisoryCache = void 0;
exports.checkPackageAdvisories = checkPackageAdvisories;
exports.queryOSV = queryOSV;
exports.queryOSVBatch = queryOSVBatch;
exports.mapOSVSeverity = mapOSVSeverity;
exports.isMaliciousPackage = isMaliciousPackage;
exports.getCacheKey = getCacheKey;
const registry_clients_1 = require("../utils/registry-clients");
// ============================================================================
// Configuration
// ============================================================================
// OSV API endpoint
const OSV_API_URL = 'https://api.osv.dev/v1/query';
const OSV_BATCH_URL = 'https://api.osv.dev/v1/querybatch';
// Cache TTL (24 hours)
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
// Maximum packages to check per scan (cost/time control)
const MAX_PACKAGES_TO_CHECK = 100;
// ============================================================================
// Advisory Cache
// ============================================================================
const advisoryCache = new Map();
exports.advisoryCache = advisoryCache;
/**
 * Get cache key for a package
 */
function getCacheKey(name, ecosystem) {
    return `${ecosystem}:${name.toLowerCase()}`;
}
/**
 * Check if cached advisory is still valid
 */
function isCacheValid(cached) {
    return Date.now() - cached.timestamp < CACHE_TTL_MS;
}
/**
 * Get cached advisories for a package
 */
function getCachedAdvisories(name, ecosystem) {
    const key = getCacheKey(name, ecosystem);
    const cached = advisoryCache.get(key);
    if (cached && isCacheValid(cached)) {
        return cached.advisories;
    }
    return null;
}
/**
 * Cache advisories for a package
 */
function cacheAdvisories(name, ecosystem, advisories) {
    const key = getCacheKey(name, ecosystem);
    advisoryCache.set(key, {
        timestamp: Date.now(),
        advisories,
    });
}
// ============================================================================
// OSV API Client
// ============================================================================
/**
 * Query OSV.dev for vulnerabilities affecting a single package
 */
async function queryOSV(packageName, ecosystem, version) {
    // Check cache first
    const cached = getCachedAdvisories(packageName, ecosystem);
    if (cached !== null) {
        return cached;
    }
    try {
        const query = {
            package: {
                name: packageName,
                ecosystem,
            },
        };
        if (version) {
            query.version = version;
        }
        const response = await fetch(OSV_API_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(query),
        });
        if (!response.ok) {
            // Don't throw - gracefully degrade
            cacheAdvisories(packageName, ecosystem, []);
            return [];
        }
        const data = (await response.json());
        const advisories = data.vulns || [];
        // Cache the result
        cacheAdvisories(packageName, ecosystem, advisories);
        return advisories;
    }
    catch {
        // Network error - gracefully degrade
        return [];
    }
}
/**
 * Query OSV.dev for multiple packages in batch
 */
async function queryOSVBatch(packages) {
    const results = new Map();
    // Split into cached and uncached
    const uncached = [];
    for (let i = 0; i < packages.length; i++) {
        const pkg = packages[i];
        const cached = getCachedAdvisories(pkg.name, pkg.ecosystem);
        if (cached !== null) {
            results.set(getCacheKey(pkg.name, pkg.ecosystem), cached);
        }
        else {
            uncached.push({ ...pkg, index: i });
        }
    }
    if (uncached.length === 0) {
        return results;
    }
    try {
        const queries = uncached.map(pkg => ({
            package: {
                name: pkg.name,
                ecosystem: pkg.ecosystem,
            },
            version: pkg.version,
        }));
        const response = await fetch(OSV_BATCH_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ queries }),
        });
        if (!response.ok) {
            // Gracefully degrade - return empty for all uncached
            for (const pkg of uncached) {
                cacheAdvisories(pkg.name, pkg.ecosystem, []);
                results.set(getCacheKey(pkg.name, pkg.ecosystem), []);
            }
            return results;
        }
        const data = (await response.json());
        // Process batch results
        for (let i = 0; i < uncached.length; i++) {
            const pkg = uncached[i];
            const advisories = data.results[i]?.vulns || [];
            cacheAdvisories(pkg.name, pkg.ecosystem, advisories);
            results.set(getCacheKey(pkg.name, pkg.ecosystem), advisories);
        }
        return results;
    }
    catch {
        // Network error - gracefully degrade
        for (const pkg of uncached) {
            results.set(getCacheKey(pkg.name, pkg.ecosystem), []);
        }
        return results;
    }
}
// ============================================================================
// Severity Mapping
// ============================================================================
/**
 * Determine severity from OSV vulnerability
 */
function mapOSVSeverity(vuln) {
    // Check for malicious package (critical)
    if (vuln.database_specific?.malicious) {
        return 'critical';
    }
    // Check CVSS score
    const cvss = vuln.severity?.find(s => s.type === 'CVSS_V3');
    if (cvss) {
        const score = parseFloat(cvss.score);
        if (score >= 9.0)
            return 'critical';
        if (score >= 7.0)
            return 'high';
        if (score >= 4.0)
            return 'medium';
        return 'low';
    }
    // Check database_specific severity
    const dbSeverity = vuln.database_specific?.severity?.toLowerCase();
    if (dbSeverity) {
        if (dbSeverity === 'critical')
            return 'critical';
        if (dbSeverity === 'high')
            return 'high';
        if (dbSeverity === 'moderate' || dbSeverity === 'medium')
            return 'medium';
        if (dbSeverity === 'low')
            return 'low';
    }
    // Default to high for unknown severity (conservative)
    return 'high';
}
/**
 * Check if vulnerability is for a malicious package
 */
function isMaliciousPackage(vuln) {
    return vuln.database_specific?.malicious === true ||
        vuln.id.startsWith('MAL-') ||
        (vuln.summary?.toLowerCase().includes('malicious') ?? false);
}
// ============================================================================
// Main Check Function
// ============================================================================
/**
 * Check packages in a file for known vulnerabilities via OSV.dev
 */
async function checkPackageAdvisories(content, filePath) {
    const vulnerabilities = [];
    // Determine file type
    const fileType = (0, registry_clients_1.getPackageFileType)(filePath);
    if (!fileType) {
        return vulnerabilities;
    }
    // Extract dependencies based on file type
    let dependencies = [];
    if (fileType === 'npm' && filePath.endsWith('package.json')) {
        dependencies = (0, registry_clients_1.extractNpmDependencies)(content);
    }
    else if (fileType === 'python') {
        dependencies = (0, registry_clients_1.extractPythonRequirements)(content);
    }
    if (dependencies.length === 0) {
        return vulnerabilities;
    }
    const lines = content.split('\n');
    const ecosystem = fileType === 'npm' ? 'npm' : 'PyPI';
    // Limit packages to check
    const limitedDeps = dependencies.slice(0, MAX_PACKAGES_TO_CHECK);
    // Query OSV in batch for efficiency
    const packagesToQuery = limitedDeps.map(dep => ({
        name: dep.name,
        ecosystem: ecosystem,
        version: dep.version,
    }));
    const advisoriesMap = await queryOSVBatch(packagesToQuery);
    // Process results
    for (const dep of limitedDeps) {
        const key = getCacheKey(dep.name, ecosystem);
        const advisories = advisoriesMap.get(key) || [];
        if (advisories.length === 0)
            continue;
        // Find the most severe vulnerability
        let mostSevere = null;
        let highestSeverity = 'info';
        const severityOrder = ['info', 'low', 'medium', 'high', 'critical'];
        for (const adv of advisories) {
            const sev = mapOSVSeverity(adv);
            if (severityOrder.indexOf(sev) > severityOrder.indexOf(highestSeverity)) {
                highestSeverity = sev;
                mostSevere = adv;
            }
        }
        if (!mostSevere)
            continue;
        // Determine category based on malicious vs vulnerable
        const isMalicious = advisories.some(a => isMaliciousPackage(a));
        const category = isMalicious ? 'ai_package_malicious' : 'suspicious_package';
        // Build description
        const advIds = advisories.map(a => a.id).slice(0, 3).join(', ');
        const moreCount = advisories.length > 3 ? ` +${advisories.length - 3} more` : '';
        const description = isMalicious
            ? `Package "${dep.name}" is flagged as MALICIOUS in OSV.dev (${advIds}${moreCount}). This package may contain malware or data exfiltration code.`
            : `Package "${dep.name}" has ${advisories.length} known security advisories (${advIds}${moreCount}). ${mostSevere.summary || 'Review before use.'}`;
        // Build suggested fix
        const suggestedFix = isMalicious
            ? `Remove "${dep.name}" immediately. Do not use this package.`
            : `Update "${dep.name}" to a patched version or find an alternative. Check: https://osv.dev/list?ecosystem=${ecosystem}&q=${encodeURIComponent(dep.name)}`;
        vulnerabilities.push({
            id: `osv-${filePath}-${dep.name}`,
            filePath,
            lineNumber: dep.line,
            lineContent: lines[dep.line - 1]?.trim() || dep.name,
            severity: highestSeverity,
            category,
            title: isMalicious
                ? `Malicious package: ${dep.name}`
                : `Vulnerable package: ${dep.name} (${advisories.length} advisories)`,
            description,
            suggestedFix,
            confidence: 'high',
            layer: 3,
            requiresAIValidation: false, // OSV data is authoritative
        });
        // Rate limit between individual package queries if not using batch
        await (0, registry_clients_1.rateLimitDelay)();
    }
    return vulnerabilities;
}
//# sourceMappingURL=osv-check.js.map