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
55const 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
1415const 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
2030const 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 >
0 commit comments