From d3fdcdfb51d782e8f22a9eecdb5091def8ff4176 Mon Sep 17 00:00:00 2001 From: Rohit Rai Date: Thu, 21 Dec 2023 18:14:24 +0530 Subject: [PATCH] fix(quay): fix sorting and ordering of vulneribilities based on severity (#1033) * fix(quay): fix ordering of vulneribilities based on severity * fix(quay): fix list page sorting of vulnerabilities based on severity --- .../QuayRepository/tableHeading.tsx | 20 +- .../components/QuayTagDetails/component.tsx | 15 +- plugins/quay/src/lib/utils.data.ts | 176 ++++++++++++++++++ plugins/quay/src/lib/utils.test.ts | 33 +++- plugins/quay/src/lib/utils.ts | 27 ++- plugins/quay/src/types.ts | 3 +- 6 files changed, 249 insertions(+), 25 deletions(-) create mode 100644 plugins/quay/src/lib/utils.data.ts diff --git a/plugins/quay/src/components/QuayRepository/tableHeading.tsx b/plugins/quay/src/components/QuayRepository/tableHeading.tsx index c455771d96..f4b6540d07 100644 --- a/plugins/quay/src/components/QuayRepository/tableHeading.tsx +++ b/plugins/quay/src/components/QuayRepository/tableHeading.tsx @@ -5,27 +5,9 @@ import { Link, Progress, TableColumn } from '@backstage/core-components'; import { Tooltip } from '@material-ui/core'; import makeStyles from '@material-ui/core/styles/makeStyles'; +import { vulnerabilitySummary } from '../../lib/utils'; import type { Layer } from '../../types'; -const vulnerabilitySummary = (layer: Layer): string => { - const summary: Record = {}; - - layer?.Features.forEach(feature => { - feature.Vulnerabilities?.forEach(vulnerability => { - const { Severity } = vulnerability; - if (!summary[Severity]) { - summary[Severity] = 0; - } - summary[Severity]++; - }); - }); - - const scanResults = Object.entries(summary) - .map(([severity, count]) => `${severity}: ${count}`) - .join(', '); - return scanResults.trim() !== '' ? scanResults : 'Passed'; -}; - export const columns: TableColumn[] = [ { title: 'Tag', diff --git a/plugins/quay/src/components/QuayTagDetails/component.tsx b/plugins/quay/src/components/QuayTagDetails/component.tsx index 1f125fe200..a178bed8d3 100644 --- a/plugins/quay/src/components/QuayTagDetails/component.tsx +++ b/plugins/quay/src/components/QuayTagDetails/component.tsx @@ -9,7 +9,12 @@ import LinkIcon from '@material-ui/icons/Link'; import WarningIcon from '@material-ui/icons/Warning'; import { SEVERITY_COLORS } from '../../lib/utils'; -import { Layer, Vulnerability, VulnerabilityListItem } from '../../types'; +import { + Layer, + Vulnerability, + VulnerabilityListItem, + VulnerabilityOrder, +} from '../../types'; type QuayTagDetailsProps = { layer: Layer; @@ -125,7 +130,13 @@ export const QuayTagDetails = ({ }, ); }) - .flat(); + .flat() + .sort((a, b) => { + const severityA = VulnerabilityOrder[a.Severity]; + const severityB = VulnerabilityOrder[b.Severity]; + + return severityA - severityB; + }); return ( diff --git a/plugins/quay/src/lib/utils.data.ts b/plugins/quay/src/lib/utils.data.ts new file mode 100644 index 0000000000..c6d52128d3 --- /dev/null +++ b/plugins/quay/src/lib/utils.data.ts @@ -0,0 +1,176 @@ +export const mockLayer = { + Name: 'TestLayer', + ParentName: 'ParentLayer', + NamespaceName: 'Namespace', + IndexedByVersion: 1, + Features: [ + { + Name: 'openssl-libs', + VersionFormat: '', + NamespaceName: '', + AddedBy: + 'sha256:2fe2b9e85b7c384f07f3665132f448c43aa7a5a600f89b5adde70258bc22e5f2', + Version: '1:1.1.1k-6.el8_5', + Vulnerabilities: [ + { + Severity: 'Medium', + NamespaceName: 'RHEL8-rhel-8', + Link: 'https://access.redhat.com/errata/RHSA-2022:5818 https://access.redhat.com/security/cve/CVE-2022-1292 https://access.redhat.com/security/cve/CVE-2022-2068 https://access.redhat.com/security/cve/CVE-2022-2097', + FixedBy: '1:1.1.1k-7.el8_6', + Description: + 'OpenSSL is a toolkit that implements the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols, as well as a full-strength general-purpose cryptography library.\n\nSecurity Fix(es):\n\n* openssl: c_rehash script allows command injection (CVE-2022-1292)\n\n* openssl: the c_rehash script allows command injection (CVE-2022-2068)\n\n* openssl: AES OCB fails to encrypt some bytes (CVE-2022-2097)\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.', + Name: 'RHSA-2022:5818: openssl security update (Moderate)', + Metadata: { + UpdatedBy: 'RHEL8-rhel-8', + RepoName: 'cpe:/o:redhat:enterprise_linux:8::baseos', + RepoLink: null, + DistroName: 'Red Hat Enterprise Linux Server', + DistroVersion: '8', + NVD: { + CVSSv3: { + Vectors: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H', + Score: 9.8, + }, + }, + }, + }, + { + Severity: 'High', + NamespaceName: 'RHEL8-rhel-8', + Link: 'https://access.redhat.com/errata/RHSA-2023:1405 https://access.redhat.com/security/cve/CVE-2022-4304 https://access.redhat.com/security/cve/CVE-2022-4450 https://access.redhat.com/security/cve/CVE-2023-0215 https://access.redhat.com/security/cve/CVE-2023-0286', + FixedBy: '1:1.1.1k-9.el8_7', + Description: + 'OpenSSL is a toolkit that implements the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols, as well as a full-strength general-purpose cryptography library.\n\nSecurity Fix(es):\n\n* openssl: X.400 address type confusion in X.509 GeneralName (CVE-2023-0286)\n\n* openssl: timing attack in RSA Decryption implementation (CVE-2022-4304)\n\n* openssl: double free after calling PEM_read_bio_ex (CVE-2022-4450)\n\n* openssl: use-after-free following BIO_new_NDEF (CVE-2023-0215)\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.', + Name: 'RHSA-2023:1405: openssl security update (Important)', + Metadata: { + UpdatedBy: 'RHEL8-rhel-8', + RepoName: 'cpe:/o:redhat:enterprise_linux:8::baseos', + RepoLink: null, + DistroName: 'Red Hat Enterprise Linux Server', + DistroVersion: '8', + NVD: { + CVSSv3: { + Vectors: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H', + Score: 7.5, + }, + }, + }, + }, + { + Severity: 'Low', + NamespaceName: 'RHEL8-rhel-8-including-unpatched', + Link: 'https://access.redhat.com/errata/RHSA-2023:7877 https://access.redhat.com/security/cve/CVE-2023-3446 https://access.redhat.com/security/cve/CVE-2023-3817 https://access.redhat.com/security/cve/CVE-2023-5678', + FixedBy: '1:1.1.1k-12.el8_9', + Description: + 'OpenSSL is a toolkit that implements the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols, as well as a full-strength general-purpose cryptography library.\n\nSecurity Fix(es):\n\n* openssl: Excessive time spent checking DH keys and parameters (CVE-2023-3446)\n\n* OpenSSL: Excessive time spent checking DH q parameter value (CVE-2023-3817)\n\n* openssl: Generating excessively long X9.42 DH keys or checking excessively long X9.42 DH keys or parameters may be very slow (CVE-2023-5678)\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.', + Name: 'RHSA-2023:7877: openssl security update (Low)', + Metadata: { + UpdatedBy: 'RHEL8-rhel-8-including-unpatched', + RepoName: 'cpe:/o:redhat:enterprise_linux:8::baseos', + RepoLink: null, + DistroName: 'Red Hat Enterprise Linux Server', + DistroVersion: '8', + NVD: { + CVSSv3: { + Vectors: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', + Score: 5.3, + }, + }, + }, + }, + ], + }, + { + Name: 'ncurses-base', + VersionFormat: '', + NamespaceName: '', + AddedBy: + 'sha256:2fe2b9e85b7c384f07f3665132f448c43aa7a5a600f89b5adde70258bc22e5f2', + Version: '6.1-9.20180224.el8', + Vulnerabilities: [ + { + Severity: 'Medium', + NamespaceName: 'RHEL8-rhel-8', + Link: 'https://access.redhat.com/errata/RHSA-2023:5249 https://access.redhat.com/security/cve/CVE-2023-29491', + FixedBy: '0:6.1-9.20180224.el8_8.1', + Description: + 'The ncurses (new curses) library routines are a terminal-independent method of updating character screens with reasonable optimization. The ncurses packages contain support utilities including a terminfo compiler tic, a decompiler infocmp, clear, tput, tset, and a termcap conversion tool captoinfo.\n\nSecurity Fix(es):\n\n* ncurses: Local users can trigger security-relevant memory corruption via malformed data (CVE-2023-29491)\n\nFor more details about the security issue(s), including the impact, a CVSS score, acknowledgments, and other related information, refer to the CVE page(s) listed in the References section.', + Name: 'RHSA-2023:5249: ncurses security update (Moderate)', + Metadata: { + UpdatedBy: 'RHEL8-rhel-8', + RepoName: 'cpe:/o:redhat:enterprise_linux:8::baseos', + RepoLink: null, + DistroName: 'Red Hat Enterprise Linux Server', + DistroVersion: '8', + NVD: { + CVSSv3: { + Vectors: 'CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H', + Score: 7.8, + }, + }, + }, + }, + ], + }, + { + Name: 'Feature1', + VersionFormat: '1.0.0', + NamespaceName: 'Namespace', + AddedBy: 'Tester', + Version: '1.0.0', + Vulnerabilities: [ + { + Severity: 'High', + NamespaceName: 'Namespace', + Link: 'https://example.com', + FixedBy: 'Fixer', + Description: 'Sample vulnerability', + Name: 'Vuln1', + Metadata: { + UpdatedBy: 'Updater', + RepoName: 'Repo', + RepoLink: 'https://repo.example.com', + DistroName: 'Ubuntu', + DistroVersion: '18.04', + NVD: { + CVSSv3: { + Vectors: 'CVSSv3 Vectors', + Score: 7.5, + }, + }, + }, + }, + ], + }, + { + Name: 'Feature2', + VersionFormat: '2.0.0', + NamespaceName: 'Namespace', + AddedBy: 'Tester', + Version: '2.0.0', + Vulnerabilities: [ + { + Severity: 'High', + NamespaceName: 'Namespace', + Link: 'https://example.com', + FixedBy: 'Fixer', + Description: 'Another vulnerability', + Name: 'Vuln2', + Metadata: { + UpdatedBy: 'Updater', + RepoName: 'Repo', + RepoLink: 'https://repo.example.com', + DistroName: 'Ubuntu', + DistroVersion: '20.04', + NVD: { + CVSSv3: { + Vectors: 'CVSSv3 Vectors', + Score: 5.0, + }, + }, + }, + }, + ], + }, + ], +}; diff --git a/plugins/quay/src/lib/utils.test.ts b/plugins/quay/src/lib/utils.test.ts index e34dfb9b14..cb1103ed7b 100644 --- a/plugins/quay/src/lib/utils.test.ts +++ b/plugins/quay/src/lib/utils.test.ts @@ -1,5 +1,6 @@ -import { VulnerabilitySeverity } from '../types'; -import { SEVERITY_COLORS } from './utils'; +import { Layer, VulnerabilitySeverity } from '../types'; +import { SEVERITY_COLORS, vulnerabilitySummary } from './utils'; +import { mockLayer } from './utils.data'; describe('SEVERITY_COLORS', () => { it('should return the correct hex color code', () => { @@ -16,3 +17,31 @@ describe('SEVERITY_COLORS', () => { expect(result).toBe('#8A8D90'); }); }); + +describe('vulnerabilitySummary', () => { + test('returns "Passed" when no vulnerabilities are present', () => { + const layer: Layer = { + Name: 'TestLayer', + ParentName: 'ParentLayer', + NamespaceName: 'Namespace', + IndexedByVersion: 1, + Features: [ + { + Name: 'Feature1', + VersionFormat: '1.0.0', + NamespaceName: 'Namespace', + AddedBy: 'Tester', + Version: '1.0.0', + Vulnerabilities: [], + }, + ], + }; + + expect(vulnerabilitySummary(layer)).toBe('Passed'); + }); + + test('returns a string with vulnerability counts in the correct order', () => { + const result = vulnerabilitySummary(mockLayer as Layer); + expect(result).toMatch('High: 3, Medium: 2, Low: 1'); + }); +}); diff --git a/plugins/quay/src/lib/utils.ts b/plugins/quay/src/lib/utils.ts index afc4bcc814..d4a0d4e465 100644 --- a/plugins/quay/src/lib/utils.ts +++ b/plugins/quay/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { VulnerabilitySeverity } from '../types'; +import { Layer, VulnerabilityOrder, VulnerabilitySeverity } from '../types'; export const SEVERITY_COLORS = new Proxy( new Map([ @@ -13,3 +13,28 @@ export const SEVERITY_COLORS = new Proxy( o.has(k) ? o.get(k) : '#8A8D90', }, ); + +export const vulnerabilitySummary = (layer: Layer): string => { + const summary: Record = {}; + + layer?.Features.forEach(feature => { + feature.Vulnerabilities?.forEach(vulnerability => { + const { Severity } = vulnerability; + if (!summary[Severity]) { + summary[Severity] = 0; + } + summary[Severity]++; + }); + }); + + const scanResults = Object.entries(summary) + .sort((a, b) => { + const severityA = VulnerabilityOrder[a[0] as VulnerabilitySeverity]; + const severityB = VulnerabilityOrder[b[0] as VulnerabilitySeverity]; + + return severityA - severityB; + }) + .map(([severity, count]) => `${severity}: ${count}`) + .join(', '); + return scanResults.trim() !== '' ? scanResults : 'Passed'; +}; diff --git a/plugins/quay/src/types.ts b/plugins/quay/src/types.ts index 1d8bb0b002..e95768372b 100644 --- a/plugins/quay/src/types.ts +++ b/plugins/quay/src/types.ts @@ -115,7 +115,8 @@ export const VulnerabilityOrder = { [VulnerabilitySeverity.Medium]: 2, [VulnerabilitySeverity.Low]: 3, [VulnerabilitySeverity.Negligible]: 4, - [VulnerabilitySeverity.Unknown]: 5, + [VulnerabilitySeverity.None]: 5, + [VulnerabilitySeverity.Unknown]: 6, }; export interface ManifestByDigestResponse {