* Query OSV for full vulnerability details for a single package. * Only called for packages known to have vulnerabilities.
(pkg: PackageQueryInfo)
| 78 | * Only called for packages known to have vulnerabilities. |
| 79 | */ |
| 80 | async function queryOsvDetails(pkg: PackageQueryInfo): Promise<PackageVulnerabilityInfo | null> { |
| 81 | try { |
| 82 | const response = await $fetch<OsvQueryResponse>('https://api.osv.dev/v1/query', { |
| 83 | method: 'POST', |
| 84 | body: { |
| 85 | package: { name: pkg.name, ecosystem: 'npm' }, |
| 86 | version: pkg.version, |
| 87 | }, |
| 88 | }) |
| 89 | |
| 90 | const vulns = response.vulns || [] |
| 91 | if (vulns.length === 0) return null |
| 92 | |
| 93 | const counts = { total: vulns.length, critical: 0, high: 0, moderate: 0, low: 0 } |
| 94 | const vulnerabilities: VulnerabilitySummary[] = [] |
| 95 | |
| 96 | const severityOrder: Record<OsvSeverityLevel, number> = { |
| 97 | critical: 0, |
| 98 | high: 1, |
| 99 | moderate: 2, |
| 100 | low: 3, |
| 101 | unknown: 4, |
| 102 | } |
| 103 | |
| 104 | const sortedVulns = [...vulns].sort( |
| 105 | (a, b) => severityOrder[getSeverityLevel(a)] - severityOrder[getSeverityLevel(b)], |
| 106 | ) |
| 107 | |
| 108 | for (const vuln of sortedVulns) { |
| 109 | const severity = getSeverityLevel(vuln) |
| 110 | if (severity === 'critical') counts.critical++ |
| 111 | else if (severity === 'high') counts.high++ |
| 112 | else if (severity === 'moderate') counts.moderate++ |
| 113 | else if (severity === 'low') counts.low++ |
| 114 | |
| 115 | vulnerabilities.push({ |
| 116 | id: vuln.id, |
| 117 | summary: vuln.summary || 'No description available', |
| 118 | severity, |
| 119 | aliases: vuln.aliases || [], |
| 120 | url: getVulnerabilityUrl(vuln), |
| 121 | fixedIn: getFixedVersion(vuln.affected, pkg.name, pkg.version), |
| 122 | }) |
| 123 | } |
| 124 | |
| 125 | return { |
| 126 | name: pkg.name, |
| 127 | version: pkg.version, |
| 128 | depth: pkg.depth, |
| 129 | path: pkg.path, |
| 130 | vulnerabilities, |
| 131 | counts, |
| 132 | } |
| 133 | } catch (error) { |
| 134 | // oxlint-disable-next-line no-console -- log OSV API failures for debugging |
| 135 | console.warn(`[dep-analysis] OSV detail query failed for ${pkg.name}@${pkg.version}:`, error) |
| 136 | return null |
| 137 | } |
no test coverage detected