From 36eb23cd50300ef8868f1afe6d25c8331c514c78 Mon Sep 17 00:00:00 2001 From: Thomas Johnson <123108455+hawkishpolicy@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:58:06 -0500 Subject: [PATCH] Added Severity Label validation to All Vulns table (CRASM-822) (#720) * Added Severity Label validation to All Vulns table - Ensured that null or empty string values are displayed as 'N/A' in the severity column of the All Vulns table. - Ensured that values of Other or any value that does not include 'Critical', 'High', 'Medium', or 'Low' are displayed as 'Other' in the severity column of the All Vulns table.. - Formatted the severity column of the All Vulns table to display the severity label in title case. - Updated the vulnerability search endpoint to return vulnerabilities with a severity of 'Other' if the severity is not 'Critical', 'High', 'Medium', or 'Low'. - Updated the vulnerability search endpoint to return vulnerabilities with a severity of 'N/A' if the severity is null or an empty string. - Updated the vulnerability search endpoint to return vulnerabilities regardless of the case of the severity value. * Resolved issue with frontend snapshot - Could be from the HTML encodings for the org search introduced in CRASM-746/PR #691 * Reverted edit to RegionAndOrganizationFilters.tsx - Added an if statement and a console.log to help troubleshoot a github actions issue. - This change was reverted because it was erroneously included in the draft pull request. * Resolve fixes --------- Co-authored-by: Janson Bunce --- backend/src/api/vulnerabilities.ts | 25 ++++++- .../RegionAndOrganizationFilters.tsx | 1 + .../src/pages/Risk/VulnerabilityBarChart.tsx | 4 +- .../pages/Vulnerabilities/Vulnerabilities.tsx | 67 ++++++++++++------- 4 files changed, 69 insertions(+), 28 deletions(-) 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[] = [ {