|
1 | 1 | import Badge from '@node-core/ui-components/Common/Badge'; |
2 | 2 | import type { FC } from 'react'; |
3 | 3 |
|
4 | | -// mapping of vulnerability severities to UI labels and colors |
| 4 | +import type { Vulnerability } from '#site/next-data/providers/vulnerabilities.js'; |
| 5 | + |
| 6 | +// Mapping of vulnerability severities to UI labels and colors |
5 | 7 | // TODO @bmuenzenmeyer we need i18n keys for these labels |
6 | | -const severityLabels: Record<string, { label: string; kind: string }> = { |
7 | | - low: { label: 'Low', kind: 'default' }, |
8 | | - medium: { label: 'Medium', kind: 'info' }, |
9 | | - high: { label: 'High', kind: 'warning' }, |
| 8 | +const SEVERITY_CONFIG = { |
10 | 9 | critical: { label: 'Critical', kind: 'error' }, |
| 10 | + high: { label: 'High', kind: 'warning' }, |
| 11 | + medium: { label: 'Medium', kind: 'info' }, |
| 12 | + low: { label: 'Low', kind: 'default' }, |
| 13 | +} as const; |
| 14 | + |
| 15 | +const SEVERITY_ORDER = Object.keys(SEVERITY_CONFIG) as Array< |
| 16 | + keyof typeof SEVERITY_CONFIG |
| 17 | +>; |
| 18 | + |
| 19 | +type VulnerabilityChipsProps = { |
| 20 | + vulnerabilities: Array<Vulnerability>; |
11 | 21 | }; |
12 | 22 |
|
13 | | -// eslint-disable-next-line @typescript-eslint/no-explicit-any |
14 | | -// TODO @bmuenzenmeyer type |
15 | | -const VulnerabilityChips: FC<{ vulnerabilities: Array<any> }> = ({ |
| 23 | +const VulnerabilityChips: FC<VulnerabilityChipsProps> = ({ |
16 | 24 | vulnerabilities, |
17 | 25 | }) => { |
18 | | - if (!vulnerabilities || vulnerabilities.length === 0) { |
19 | | - return <span>No vulnerabilities reported</span>; |
20 | | - } |
21 | | - |
22 | | - // group all vulnerabilities by severity |
23 | | - const groupedVulnerabilities = vulnerabilities.reduce( |
| 26 | + // Group vulnerabilities by severity |
| 27 | + const groupedBySeverity = vulnerabilities.reduce<Record<string, number>>( |
24 | 28 | (acc, vuln) => { |
25 | 29 | const severity = vuln.severity.toLowerCase(); |
26 | | - if (!acc[severity]) { |
27 | | - acc[severity] = []; |
28 | | - } |
29 | | - acc[severity].push(vuln); |
| 30 | + acc[severity] = (acc[severity] || 0) + 1; |
30 | 31 | return acc; |
31 | 32 | }, |
32 | | - {} as Record<string, Array<any>> |
| 33 | + {} |
33 | 34 | ); |
34 | 35 |
|
35 | 36 | return ( |
36 | 37 | <div className="vulnerability-chips"> |
37 | | - {Object.entries(groupedVulnerabilities) |
38 | | - .sort((a, b) => { |
39 | | - const severityOrder = ['critical', 'high', 'medium', 'low']; |
40 | | - return severityOrder.indexOf(a[0]) - severityOrder.indexOf(b[0]); |
41 | | - }) |
42 | | - .filter(([severity]) => severityLabels[severity]) // filter out unknown severities. there are 78+, with increasing frequency the older you get into our release or security governance |
43 | | - .map(([severity, vulnerabilityCount]) => { |
44 | | - const { label, kind } = severityLabels[severity]; |
| 38 | + {SEVERITY_ORDER.filter(severity => groupedBySeverity[severity] > 0).map( |
| 39 | + severity => { |
| 40 | + const { label, kind } = SEVERITY_CONFIG[severity]; |
| 41 | + const count = groupedBySeverity[severity]; |
| 42 | + |
45 | 43 | return ( |
46 | 44 | <Badge size="small" key={severity} kind={kind} className="mr-0.5"> |
47 | | - {label} ({vulnerabilityCount.length}) |
| 45 | + {label} ({count}) |
48 | 46 | </Badge> |
49 | 47 | ); |
50 | | - })} |
| 48 | + } |
| 49 | + )} |
51 | 50 | </div> |
52 | 51 | ); |
53 | 52 | }; |
|
0 commit comments