Skip to content

Commit cec8e78

Browse files
committed
feat: deep vulnerability checks
1 parent c1741a3 commit cec8e78

15 files changed

Lines changed: 787 additions & 913 deletions

app/components/PackageDependencies.vue

Lines changed: 20 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<script setup lang="ts">
2-
import { SEVERITY_LEVELS } from '~~/shared/types'
3-
import { SEVERITY_COLORS } from '~~/shared/utils/severity'
2+
import { useVulnerabilityTree } from '~/composables/useVulnerabilityTree'
3+
import { SEVERITY_TEXT_COLORS, getHighestSeverity } from '#shared/utils/severity'
44
55
const props = defineProps<{
66
packageName: string
7+
version: string
78
dependencies?: Record<string, string>
89
peerDependencies?: Record<string, string>
910
peerDependenciesMeta?: Record<string, { optional?: boolean }>
@@ -13,8 +14,17 @@ const props = defineProps<{
1314
// Fetch outdated info for dependencies
1415
const outdatedDeps = useOutdatedDependencies(() => props.dependencies)
1516
16-
// Fetch vulnerability info for dependencies
17-
const vulnerableDeps = useVulnerableDependencies(() => props.dependencies)
17+
// Get vulnerability info from shared cache (already fetched by PackageVulnerabilityTree)
18+
const { data: vulnTree } = useVulnerabilityTree(
19+
() => props.packageName,
20+
() => props.version,
21+
)
22+
23+
// Check if a dependency has vulnerabilities (only direct deps)
24+
function getVulnerableDepInfo(depName: string) {
25+
if (!vulnTree.value) return null
26+
return vulnTree.value.vulnerablePackages.find(p => p.name === depName && p.depth === 'direct')
27+
}
1828
1929
// Expanded state for each section
2030
const depsExpanded = shallowRef(false)
@@ -49,68 +59,10 @@ const sortedOptionalDependencies = computed(() => {
4959
if (!props.optionalDependencies) return []
5060
return Object.entries(props.optionalDependencies).sort(([a], [b]) => a.localeCompare(b))
5161
})
52-
53-
// Vulnerability summary for banner
54-
const vulnerabilitySummary = computed(() => {
55-
const deps = Object.values(vulnerableDeps.value)
56-
if (deps.length === 0) return null
57-
58-
const counts = { critical: 0, high: 0, moderate: 0, low: 0 }
59-
let total = 0
60-
61-
for (const info of deps) {
62-
if (!info?.counts) continue
63-
total += info.counts.total || 0
64-
for (const s of SEVERITY_LEVELS) counts[s] += info.counts[s] || 0
65-
}
66-
67-
const severity = SEVERITY_LEVELS.find(s => counts[s] > 0) || 'low'
68-
69-
return { affectedDeps: deps.length, totalVulns: total, severity, counts }
70-
})
71-
72-
const vulnBreakdownText = computed(() => {
73-
if (!vulnerabilitySummary.value) return ''
74-
const { counts } = vulnerabilitySummary.value
75-
return SEVERITY_LEVELS.filter(s => counts[s])
76-
.map(s => `${counts[s]} ${$t(`package.vulnerabilities.severity.${s}`)}`)
77-
.join(', ')
78-
})
7962
</script>
8063

8164
<template>
8265
<div class="space-y-8">
83-
<!-- Vulnerability warning banner -->
84-
<div
85-
v-if="vulnerabilitySummary"
86-
role="alert"
87-
class="rounded-lg border px-4 py-3 cursor-help"
88-
:class="SEVERITY_COLORS[vulnerabilitySummary.severity]"
89-
:title="
90-
$t(
91-
'package.vulnerabilities.deps_affected',
92-
{ count: vulnerabilitySummary.affectedDeps },
93-
vulnerabilitySummary.affectedDeps,
94-
)
95-
"
96-
>
97-
<div class="flex items-center gap-2">
98-
<span class="i-carbon-security w-4 h-4 shrink-0 self-start mt-0.5" aria-hidden="true" />
99-
<div>
100-
<div class="font-mono text-sm">
101-
{{
102-
$t(
103-
'package.vulnerabilities.deps_found',
104-
{ count: vulnerabilitySummary.totalVulns },
105-
vulnerabilitySummary.totalVulns,
106-
)
107-
}}
108-
</div>
109-
<div class="font-mono text-xs opacity-70">{{ vulnBreakdownText }}</div>
110-
</div>
111-
</div>
112-
</div>
113-
11466
<!-- Dependencies -->
11567
<section v-if="sortedDependencies.length > 0" aria-labelledby="dependencies-heading">
11668
<h2 id="dependencies-heading" class="text-xs text-fg-subtle uppercase tracking-wider mb-3">
@@ -139,14 +91,14 @@ const vulnBreakdownText = computed(() => {
13991
<span class="i-carbon-warning-alt w-3 h-3 block" />
14092
</span>
14193
<NuxtLink
142-
v-if="vulnerableDeps[dep]?.version"
94+
v-if="getVulnerableDepInfo(dep)"
14395
:to="{
14496
name: 'package',
145-
params: { package: [...dep.split('/'), 'v', vulnerableDeps[dep].version] },
97+
params: { package: [...dep.split('/'), 'v', getVulnerableDepInfo(dep)!.version] },
14698
}"
14799
class="shrink-0"
148-
:class="getVulnerabilitySeverityClass(vulnerableDeps[dep])"
149-
:title="getVulnerabilityTooltip(vulnerableDeps[dep])"
100+
:class="SEVERITY_TEXT_COLORS[getHighestSeverity(getVulnerableDepInfo(dep)!.counts)]"
101+
:title="`${getVulnerableDepInfo(dep)!.counts.total} vulnerabilities`"
150102
>
151103
<span class="i-carbon-security w-3 h-3 block" aria-hidden="true" />
152104
<span class="sr-only">View vulnerabilities</span>
@@ -162,8 +114,8 @@ const vulnBreakdownText = computed(() => {
162114
<span v-if="outdatedDeps[dep]" class="sr-only">
163115
({{ getOutdatedTooltip(outdatedDeps[dep]) }})
164116
</span>
165-
<span v-if="vulnerableDeps[dep]" class="sr-only">
166-
({{ getVulnerabilityTooltip(vulnerableDeps[dep]) }})
117+
<span v-if="getVulnerableDepInfo(dep)" class="sr-only">
118+
({{ getVulnerableDepInfo(dep)!.counts.total }} vulnerabilities)
167119
</span>
168120
</span>
169121
</li>

app/components/PackageVulnerabilities.vue

Lines changed: 0 additions & 150 deletions
This file was deleted.

0 commit comments

Comments
 (0)