diff --git a/backend/src/api/vulnerabilities.ts b/backend/src/api/vulnerabilities.ts index da2fb697..1e0cbe6a 100644 --- a/backend/src/api/vulnerabilities.ts +++ b/backend/src/api/vulnerabilities.ts @@ -122,9 +122,28 @@ class VulnerabilitySearch { }); } if (this.filters?.severity) { - qs.andWhere('vulnerability.severity ILIKE :severity', { - severity: `%${this.filters.severity}%` - }); + if (this.filters.severity === 'N/A') { + qs.andWhere( + "vulnerability.severity IS NULL OR vulnerability.severity = ''" + ); + } else if (this.filters.severity === 'Other') { + qs.andWhere( + `vulnerability.severity NOT ILIKE 'N/A' AND + vulnerability.severity NOT ILIKE 'Low' AND + vulnerability.severity NOT ILIKE 'Medium' AND + vulnerability.severity NOT ILIKE 'High' AND + vulnerability.severity NOT ILIKE 'Critical'AND + vulnerability.severity NOT ILIKE '' OR + vulnerability.severity ILIKE :other`, + { + other: 'Other' + } + ); + } else { + qs.andWhere('vulnerability.severity ILIKE :severity', { + severity: `%${this.filters.severity}%` + }); + } } if (this.filters?.cpe) { qs.andWhere('vulnerability.cpe ILIKE :cpe', { diff --git a/frontend/src/components/RegionAndOrganizationFilters.tsx b/frontend/src/components/RegionAndOrganizationFilters.tsx index 8643d366..a26dae8e 100644 --- a/frontend/src/components/RegionAndOrganizationFilters.tsx +++ b/frontend/src/components/RegionAndOrganizationFilters.tsx @@ -98,6 +98,7 @@ export const RegionAndOrganizationFilters: React.FC< regions } }); + const orgs = results.body.hits.hits.map((hit) => hit._source); // Filter out organizations that match the exclusions const refinedOrgs = orgs.filter((org) => { diff --git a/frontend/src/pages/Risk/VulnerabilityBarChart.tsx b/frontend/src/pages/Risk/VulnerabilityBarChart.tsx index 53f3a1e2..2a14a806 100644 --- a/frontend/src/pages/Risk/VulnerabilityBarChart.tsx +++ b/frontend/src/pages/Risk/VulnerabilityBarChart.tsx @@ -75,11 +75,11 @@ const VulnerabilityBarChart = (props: { // Place null values in "N/A" and capitalize the first letter of each word in the data. const titleCaseData: BarData[] = data.map((d) => { - if (d.id === 'null') { + if (d.id === 'null' || d.id === null || d.id === '') { return { id: 'N/A', value: d.value }; } else { return { - id: d.id[0].toUpperCase() + d.id.slice(1).toLowerCase(), + id: d.id[0]?.toUpperCase() + d.id.slice(1)?.toLowerCase(), value: d.value }; } diff --git a/frontend/src/pages/Vulnerabilities/Vulnerabilities.tsx b/frontend/src/pages/Vulnerabilities/Vulnerabilities.tsx index 14e6d09c..09c2bdd9 100644 --- a/frontend/src/pages/Vulnerabilities/Vulnerabilities.tsx +++ b/frontend/src/pages/Vulnerabilities/Vulnerabilities.tsx @@ -306,29 +306,50 @@ export const Vulnerabilities: React.FC<{ groupBy?: string }> = ({ }); }, [fetchVulnerabilities, initialFilters]); - const vulRows: VulnerabilityRow[] = vulnerabilities.map((vuln) => ({ - id: vuln.id, - title: vuln.title, - severity: vuln.severity ?? 'N/A', - kev: vuln.isKev ? 'Yes' : 'No', - domain: vuln?.domain?.name, - domainId: vuln?.domain?.id, - product: vuln.cpe - ? vuln.cpe - : vuln.service && - vuln.service.products && - vuln.service.products.length > 0 && - vuln.service.products[0].cpe - ? vuln.service.products[0].cpe || 'N/A' - : 'N/A', - createdAt: vuln?.createdAt - ? `${differenceInCalendarDays( - Date.now(), - parseISO(vuln?.createdAt) - )} days` - : '', - state: vuln.state + (vuln.substate ? ` (${vuln.substate})` : '') - })); + const vulRows: VulnerabilityRow[] = vulnerabilities.map((vuln) => { + //The following logic is to format irregular severity levels to match those used in VulnerabilityBarChart.tsx + + const titleCase = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); + + const severityLevels: string[] = ['Low', 'Medium', 'High', 'Critical']; + + const formatSeverity = (severity: string) => { + if (severity === null || severity === '' || severity === 'N/A') { + return 'N/A'; + } else if (severityLevels.includes(titleCase(severity))) { + return titleCase(severity); + } else { + return 'Other'; + } + }; + + const severity = formatSeverity(vuln.severity ?? ''); + + return { + id: vuln.id, + title: vuln.title, + severity: severity, + kev: vuln.isKev ? 'Yes' : 'No', + domain: vuln?.domain?.name, + domainId: vuln?.domain?.id, + product: vuln.cpe + ? vuln.cpe + : vuln.service && + vuln.service.products && + vuln.service.products.length > 0 && + vuln.service.products[0].cpe + ? vuln.service.products[0].cpe || 'N/A' + : 'N/A', + createdAt: vuln?.createdAt + ? `${differenceInCalendarDays( + Date.now(), + parseISO(vuln?.createdAt) + )} days` + : '', + state: vuln.state + (vuln.substate ? ` (${vuln.substate})` : '') + }; + }); const vulCols: GridColDef[] = [ {