11import { Modal , Title , Content } from '@node-core/ui-components/Common/Modal' ;
2+ import classNames from 'classnames' ;
23import { useTranslations } from 'next-intl' ;
34import type { FC } from 'react' ;
45
56import 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' ;
711import type { Vulnerability } from '#site/next-data/providers/vulnerabilities' ;
812import type { ModalProps } from '#site/providers/modalProvider' ;
913import 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+
16105const 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 ) ;
0 commit comments