Skip to content

Commit ab93fd7

Browse files
committed
modal, add unknown
1 parent a5e33fb commit ab93fd7

File tree

3 files changed

+120
-69
lines changed

3 files changed

+120
-69
lines changed

apps/site/components/Downloads/EOLModal/index.tsx

Lines changed: 118 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Modal, Title, Content } from '@node-core/ui-components/Common/Modal';
2+
import classNames from 'classnames';
23
import { useTranslations } from 'next-intl';
34
import type { FC } from 'react';
45

56
import LinkWithArrow from '#site/components/LinkWithArrow';
6-
import { VulnerabilityChip } from '#site/components/MDX/EOL/VulnerabilityChips';
7+
import {
8+
VulnerabilityChip,
9+
SEVERITY_ORDER,
10+
} from '#site/components/MDX/EOL/VulnerabilityChips';
711
import type { Vulnerability } from '#site/next-data/providers/vulnerabilities';
812
import type { ModalProps } from '#site/providers/modalProvider';
913
import type { NodeRelease } from '#site/types';
@@ -13,88 +17,134 @@ type EOLModalData = {
1317
vulnerabilities: Array<Vulnerability>;
1418
};
1519

20+
const VulnerabilitiesTable: FC<{
21+
vulnerabilities: Array<Vulnerability>;
22+
maxWidth?: string;
23+
}> = ({ vulnerabilities, maxWidth = 'max-w-2xs' }) => {
24+
const t = useTranslations('components.eolModal');
25+
26+
return (
27+
<table className="w-full">
28+
<thead>
29+
<tr>
30+
<th>{t('table.cves')}</th>
31+
<th>{t('table.severity')}</th>
32+
<th>{t('table.overview')}</th>
33+
<th>{t('table.details')}</th>
34+
</tr>
35+
</thead>
36+
<tbody>
37+
{vulnerabilities.map((vuln, i) => (
38+
<tr key={i}>
39+
<td>
40+
{vuln.cve.length
41+
? vuln.cve.map(cveId => (
42+
<div key={cveId}>
43+
<LinkWithArrow
44+
href={`https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cveId}`}
45+
target="_blank"
46+
rel="noopener noreferrer"
47+
>
48+
{cveId}
49+
</LinkWithArrow>
50+
</div>
51+
))
52+
: '-'}
53+
</td>
54+
<td>
55+
<VulnerabilityChip severity={vuln.severity} />
56+
</td>
57+
<td className={classNames(maxWidth, 'truncate')}>
58+
{vuln.description || vuln.overview || '-'}
59+
</td>
60+
<td>
61+
{vuln.ref ? (
62+
<LinkWithArrow
63+
href={vuln.ref}
64+
target="_blank"
65+
rel="noopener noreferrer"
66+
>
67+
{t('blogLinkText')}
68+
</LinkWithArrow>
69+
) : (
70+
'—'
71+
)}
72+
</td>
73+
</tr>
74+
))}
75+
</tbody>
76+
</table>
77+
);
78+
};
79+
80+
const UnknownSeveritySection: FC<{
81+
vulnerabilities: Array<Vulnerability>;
82+
hasKnownVulns: boolean;
83+
}> = ({ vulnerabilities, hasKnownVulns }) => {
84+
const t = useTranslations('components.eolModal');
85+
86+
if (!vulnerabilities.length) {
87+
return null;
88+
}
89+
90+
return (
91+
<details open={!hasKnownVulns}>
92+
<summary className="cursor-pointer font-semibold">
93+
{t('showUnknownSeverities')} ({vulnerabilities.length})
94+
</summary>
95+
<div className="mt-4">
96+
<VulnerabilitiesTable
97+
vulnerabilities={vulnerabilities}
98+
maxWidth={'max-w-3xs'}
99+
/>
100+
</div>
101+
</details>
102+
);
103+
};
104+
16105
const EOLModal: FC<ModalProps> = ({ open, closeModal, data }) => {
17106
const { release, vulnerabilities } = data as EOLModalData;
18107
const t = useTranslations('components.eolModal');
19108

20-
const modalHeadingKey = release.codename ? 'title' : 'titleWithoutCodename';
21-
22-
const modalHeading = t(modalHeadingKey, {
109+
const modalHeading = t(release.codename ? 'title' : 'titleWithoutCodename', {
23110
version: release.major,
24111
codename: release.codename ?? '',
25112
});
26113

27-
const actualVulnerabilities = vulnerabilities.filter(
28-
vuln => vuln.severity !== 'unknown'
114+
const [knownVulns, unknownVulns] = vulnerabilities.reduce(
115+
(acc, vuln) => {
116+
acc[vuln.severity === 'unknown' ? 1 : 0].push(vuln);
117+
return acc;
118+
},
119+
[[], []] as [Array<Vulnerability>, Array<Vulnerability>]
120+
);
121+
122+
knownVulns.sort(
123+
(a, b) =>
124+
SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity)
29125
);
30126

127+
const hasKnownVulns = knownVulns.length > 0;
128+
const hasAnyVulns = hasKnownVulns || unknownVulns.length > 0;
129+
31130
return (
32131
<Modal open={open} onOpenChange={closeModal}>
33132
<Title>{modalHeading}</Title>
34-
35133
<Content>
36-
{actualVulnerabilities.length > 0 ? (
37-
<>
38-
<p className="m-1">
39-
{t('vulnerabilitiesMessage', {
40-
count: actualVulnerabilities.length,
41-
})}
42-
</p>
43-
44-
<table>
45-
<thead>
46-
<tr>
47-
<th>{t('table.cves')}</th>
48-
<th>{t('table.severity')}</th>
49-
<th>{t('table.overview')}</th>
50-
<th>{t('table.details')}</th>
51-
</tr>
52-
</thead>
53-
<tbody>
54-
{actualVulnerabilities.map((vuln, index) => (
55-
<tr key={index}>
56-
<td>
57-
{vuln.cve.length > 0
58-
? vuln.cve.map(cveId => (
59-
<div key={cveId}>
60-
<LinkWithArrow
61-
href={`https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cveId}`}
62-
target="_blank"
63-
rel="noopener noreferrer"
64-
>
65-
{cveId}
66-
</LinkWithArrow>
67-
</div>
68-
))
69-
: '-'}
70-
</td>
71-
<td>
72-
<VulnerabilityChip severity={vuln.severity} />
73-
</td>
74-
<td className="max-w-xs truncate">
75-
{vuln.description || vuln.overview || '-'}
76-
</td>
77-
<td>
78-
{vuln.ref ? (
79-
<LinkWithArrow
80-
href={vuln.ref}
81-
target="_blank"
82-
rel="noopener noreferrer"
83-
>
84-
{t('blogLinkText')}
85-
</LinkWithArrow>
86-
) : (
87-
'—'
88-
)}
89-
</td>
90-
</tr>
91-
))}
92-
</tbody>
93-
</table>
94-
</>
95-
) : (
96-
<p className="m-1">{t('noVulnerabilitiesMessage')}</p>
134+
{vulnerabilities.length > 0 && (
135+
<p className="m-1">
136+
{t('vulnerabilitiesMessage', { count: vulnerabilities.length })}
137+
</p>
97138
)}
139+
140+
{hasKnownVulns && <VulnerabilitiesTable vulnerabilities={knownVulns} />}
141+
142+
<UnknownSeveritySection
143+
vulnerabilities={unknownVulns}
144+
hasKnownVulns={hasKnownVulns}
145+
/>
146+
147+
{!hasAnyVulns && <p className="m-1">{t('noVulnerabilitiesMessage')}</p>}
98148
</Content>
99149
</Modal>
100150
);

apps/site/components/MDX/EOL/VulnerabilityChips.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const SEVERITY_CONFIG = {
1313
critical: { label: 'Critical', kind: 'error' },
1414
} as const;
1515

16-
const SEVERITY_ORDER = (
16+
export const SEVERITY_ORDER = (
1717
Object.keys(SEVERITY_CONFIG) as Array<keyof typeof SEVERITY_CONFIG>
1818
)
1919
// Remove 'unknown'

packages/i18n/src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
"vulnerabilitiesMessage": "There are {count}+ known vulnerabilities associated with this Node.js release. Please review their severity and details to understand the potential impact.",
178178
"noVulnerabilitiesMessage": "There are no known vulnerabilities for this release, but that does not guarantee it is secure. Older or unsupported versions may still contain undiscovered vulnerabilities. We recommend upgrading to a supported release for continued security and stability.",
179179
"blogLinkText": "Blog",
180+
"showUnknownSeverities": "Show vulnerabilities of unknown severity",
180181
"table": {
181182
"cves": "CVE(s)",
182183
"severity": "Severity",

0 commit comments

Comments
 (0)