diff --git a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js
index bf41de02..f787d493 100644
--- a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js
+++ b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js
@@ -1,4 +1,4 @@
-import { render, screen, waitFor, fireEvent } from '@testing-library/react';
+import { render, screen, waitFor, within, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MockThemeProvider from '__mocks__/MockThemeProvider';
import { api } from 'api';
@@ -18,6 +18,52 @@ const StateVulnerabilitiesWrapper = () => {
);
};
+const simpleMockCVEList = {
+ CVEListForImage: {
+ Tag: '',
+ Page: { ItemCount: 2, TotalCount: 2 },
+ Summary: {
+ Count: 2,
+ UnknownCount: 0,
+ LowCount: 0,
+ MediumCount: 1,
+ HighCount: 0,
+ CriticalCount: 1,
+ },
+ CVEList: [
+ {
+ Id: 'CVE-2020-16156',
+ Title: 'perl-CPAN: Bypass of verification of signatures in CHECKSUMS files',
+ Description: 'CPAN 2.28 allows Signature Verification Bypass.',
+ Severity: 'MEDIUM',
+ PackageList: [
+ {
+ Name: 'perl-base',
+ PackagePath: 'Not Specified',
+ InstalledVersion: '5.30.0-9ubuntu0.2',
+ FixedVersion: 'Not Specified'
+ }
+ ]
+ },
+ {
+ Id: 'CVE-2016-1000027',
+ Title: 'spring: HttpInvokerServiceExporter readRemoteInvocation method untrusted java deserialization',
+ Description: "Pivotal Spring Framework through 5.3.16 suffers from a potential remote code execution (RCE) issue if used for Java deserialization of untrusted data. Depending on how the library is implemented within a product, this issue may or not occur, and authentication may be required. NOTE: the vendor's position is that untrusted data is not an intended use case. The product's behavior will not be changed because some users rely on deserialization of trusted data.",
+ Severity: 'CRITICAL',
+ Reference: 'https://avd.aquasec.com/nvd/cve-2016-1000027',
+ PackageList: [
+ {
+ Name: 'org.springframework:spring-web',
+ PackagePath: 'usr/local/tomcat/webapps/spring4shell.war/WEB-INF/lib/spring-web-5.3.15.jar',
+ InstalledVersion: '5.3.15',
+ FixedVersion: '6.0.0'
+ }
+ ]
+ },
+ ]
+ }
+}
+
const mockCVEList = {
CVEListForImage: {
Tag: '',
@@ -39,6 +85,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'perl-base',
+ PackagePath: 'Not Specified',
InstalledVersion: '5.30.0-9ubuntu0.2',
FixedVersion: 'Not Specified'
}
@@ -54,26 +101,31 @@ const mockCVEList = {
PackageList: [
{
Name: 'krb5-locales',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libgssapi-krb5-2',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libk5crypto3',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libkrb5-3',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libkrb5support0',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
}
@@ -88,6 +140,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libgnutls30',
+ PackagePath: 'Not Specified',
InstalledVersion: '3.6.13-2ubuntu1.6',
FixedVersion: '3.6.13-2ubuntu1.7'
}
@@ -102,6 +155,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libpcre2-8-0',
+ PackagePath: 'Not Specified',
InstalledVersion: '10.34-7',
FixedVersion: 'Not Specified'
}
@@ -116,6 +170,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libsqlite3-0',
+ PackagePath: 'Not Specified',
InstalledVersion: '3.31.1-4ubuntu0.3',
FixedVersion: '3.31.1-4ubuntu0.4'
}
@@ -130,6 +185,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libpcre3',
+ PackagePath: 'Not Specified',
InstalledVersion: '2:8.39-12ubuntu0.1',
FixedVersion: 'Not Specified'
}
@@ -144,6 +200,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libsqlite3-0',
+ PackagePath: 'Not Specified',
InstalledVersion: '3.31.1-4ubuntu0.3',
FixedVersion: '3.31.1-4ubuntu0.4'
}
@@ -158,11 +215,13 @@ const mockCVEList = {
PackageList: [
{
Name: 'login',
+ PackagePath: 'Not Specified',
InstalledVersion: '1:4.8.1-1ubuntu5.20.04.2',
FixedVersion: 'Not Specified'
},
{
Name: 'passwd',
+ PackagePath: 'Not Specified',
InstalledVersion: '1:4.8.1-1ubuntu5.20.04.2',
FixedVersion: 'Not Specified'
}
@@ -177,6 +236,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libgmp10',
+ PackagePath: 'Not Specified',
InstalledVersion: '2:6.2.0+dfsg-4',
FixedVersion: 'Not Specified'
}
@@ -191,6 +251,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libgnutls30',
+ PackagePath: 'Not Specified',
InstalledVersion: '3.6.13-2ubuntu1.6',
FixedVersion: '3.6.13-2ubuntu1.7'
}
@@ -205,26 +266,31 @@ const mockCVEList = {
PackageList: [
{
Name: 'libncurses6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'libncursesw6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'libtinfo6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'ncurses-base',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'ncurses-bin',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
}
@@ -239,6 +305,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libpcre2-8-0',
+ PackagePath: 'Not Specified',
InstalledVersion: '10.34-7',
FixedVersion: 'Not Specified'
}
@@ -253,26 +320,31 @@ const mockCVEList = {
PackageList: [
{
Name: 'libncurses6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'libncursesw6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'libtinfo6',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'ncurses-base',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
},
{
Name: 'ncurses-bin',
+ PackagePath: 'Not Specified',
InstalledVersion: '6.2-0ubuntu2',
FixedVersion: 'Not Specified'
}
@@ -287,6 +359,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'coreutils',
+ PackagePath: 'Not Specified',
InstalledVersion: '8.30-3ubuntu2',
FixedVersion: 'Not Specified'
}
@@ -301,46 +374,55 @@ const mockCVEList = {
PackageList: [
{
Name: 'libasn1-8-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libgssapi3-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libhcrypto4-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libheimbase1-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libheimntlm0-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libhx509-5-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libkrb5-26-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libroken18-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
},
{
Name: 'libwind0-heimdal',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.7.0+dfsg-1ubuntu1',
FixedVersion: 'Not Specified'
}
@@ -355,11 +437,13 @@ const mockCVEList = {
PackageList: [
{
Name: 'libc-bin',
+ PackagePath: 'Not Specified',
InstalledVersion: '2.31-0ubuntu9.9',
FixedVersion: 'Not Specified'
},
{
Name: 'libc6',
+ PackagePath: 'Not Specified',
InstalledVersion: '2.31-0ubuntu9.9',
FixedVersion: 'Not Specified'
}
@@ -373,6 +457,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libcurl4',
+ PackagePath: 'Not Specified',
InstalledVersion: '7.68.0-1ubuntu2.12',
FixedVersion: '7.68.0-1ubuntu2.13'
}
@@ -388,26 +473,31 @@ const mockCVEList = {
PackageList: [
{
Name: 'krb5-locales',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libgssapi-krb5-2',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libk5crypto3',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libkrb5-3',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
},
{
Name: 'libkrb5support0',
+ PackagePath: 'Not Specified',
InstalledVersion: '1.17-6ubuntu4.1',
FixedVersion: 'Not Specified'
}
@@ -422,6 +512,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'libsqlite3-0',
+ PackagePath: 'Not Specified',
InstalledVersion: '3.31.1-4ubuntu0.3',
FixedVersion: '3.31.1-4ubuntu0.4'
}
@@ -437,6 +528,7 @@ const mockCVEList = {
PackageList: [
{
Name: 'zlib1g',
+ PackagePath: 'Not Specified',
InstalledVersion: '1:1.2.11.dfsg-2ubuntu1.3',
FixedVersion: 'Not Specified'
}
@@ -667,6 +759,50 @@ describe('Vulnerabilties page', () => {
expect(await screen.findByText('latest')).toBeInTheDocument();
});
+ it('should show the list of vulnerable packages for the CVEs', async () => {
+ jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: simpleMockCVEList } })
+ render();
+ const expandListBtn = await screen.findByTestId('expand-list-view-toggle');
+ fireEvent.click(expandListBtn);
+ const packageLists = await screen.findAllByTestId('cve-package-list');
+ expect(packageLists.length).toEqual(2); // Data set has 2 CVEs, so 2 package lists
+
+ const expectedData = [
+ {
+ Name: 'perl-base',
+ PackagePath: 'Not Specified',
+ InstalledVersion: '5.30.0-9ubuntu0.2',
+ FixedVersion: 'Not Specified'
+ },
+ {
+ Name: 'org.springframework:spring-web',
+ PackagePath: 'usr/local/tomcat/webapps/spring4shell.war/WEB-INF/lib/spring-web-5.3.15.jar',
+ InstalledVersion: '5.3.15',
+ FixedVersion: '6.0.0'
+ }
+ ];
+
+ for (let index = 0; index < 2; index++) {
+ const expectedPackageData = expectedData[index];
+ const container = packageLists[index];
+ const pkgName = await within(container).findAllByTestId('cve-info-pkg-name');
+ expect(pkgName).toHaveLength(1);
+ expect(pkgName[0]).toHaveTextContent(expectedPackageData.Name);
+
+ const pkgPath = await within(container).findAllByTestId('cve-info-pkg-path');
+ expect(pkgPath).toHaveLength(1);
+ expect(pkgPath[0]).toHaveTextContent(expectedPackageData.PackagePath);
+
+ const pkgInstalledVer = await within(container).findAllByTestId('cve-info-pkg-install-ver');
+ expect(pkgInstalledVer).toHaveLength(1);
+ expect(pkgInstalledVer[0]).toHaveTextContent(expectedPackageData.InstalledVersion);
+
+ const pkgFixedVer = await within(container).findAllByTestId('cve-info-pkg-fixed-ver');
+ expect(pkgFixedVer).toHaveLength(1);
+ expect(pkgFixedVer[0]).toHaveTextContent(expectedPackageData.FixedVersion);
+ }
+ });
+
it('should allow export of vulnerabilities list', async () => {
const xlsxMock = jest.createMockFromModule('xlsx');
xlsxMock.writeFile = jest.fn();
diff --git a/src/api.js b/src/api.js
index 6bcb0de1..603f2ace 100644
--- a/src/api.js
+++ b/src/api.js
@@ -113,10 +113,10 @@ const endpoints = {
if (!isEmpty(severity)) {
query += `, severity: "${severity}"`;
}
- return `${query}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity Reference PackageList {Name InstalledVersion FixedVersion}} Summary {Count UnknownCount LowCount MediumCount HighCount CriticalCount}}}`;
+ return `${query}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity Reference PackageList {Name PackagePath InstalledVersion FixedVersion}} Summary {Count UnknownCount LowCount MediumCount HighCount CriticalCount}}}`;
},
allVulnerabilitiesForRepo: (name) =>
- `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity Reference PackageList {Name InstalledVersion FixedVersion}}}}`,
+ `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity Reference PackageList {Name PackagePath InstalledVersion FixedVersion}}}}`,
imageListWithCVEFixed: (cveId, repoName, { pageNumber = 1, pageSize = 3 }, filter = {}) => {
let filterParam = '';
if (filter.Os || filter.Arch) {
diff --git a/src/components/Shared/VulnerabilityCard.jsx b/src/components/Shared/VulnerabilityCard.jsx
index 423eed22..976913bc 100644
--- a/src/components/Shared/VulnerabilityCard.jsx
+++ b/src/components/Shared/VulnerabilityCard.jsx
@@ -13,6 +13,7 @@ import { Link } from 'react-router-dom';
import { KeyboardArrowDown, KeyboardArrowRight } from '@mui/icons-material';
import { VulnerabilityChipCheck } from 'utilities/vulnerabilityAndSignatureCheck';
import { CVE_FIXEDIN_PAGE_SIZE } from 'utilities/paginationConstants';
+import VulnerabilityPackageSection from './VulnerabilityPackageSection';
const useStyles = makeStyles((theme) => ({
card: {
@@ -258,30 +259,14 @@ function VulnerabilitiyCard(props) {
Packages
-
-
-
- Name
-
-
- Installed Version
-
-
- Fixed Version
-
-
- {cve.packageList.map((el) => (
-
-
- {el.packageName}
-
-
- {el.packageInstalledVersion}
-
-
- {el.packageFixedVersion}
-
-
+
+ {cve.packageList.map((pkg) => (
+
))}
diff --git a/src/components/Shared/VulnerabilityPackageSection.jsx b/src/components/Shared/VulnerabilityPackageSection.jsx
new file mode 100644
index 00000000..f6f296c7
--- /dev/null
+++ b/src/components/Shared/VulnerabilityPackageSection.jsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { Divider, Grid, Stack, Typography } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+
+const useStyles = makeStyles(() => ({
+ cvePackageCard: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ background: '#FFFFFF',
+ boxShadow: '0rem 0.3125rem 0.625rem rgba(131, 131, 131, 0.08)',
+ border: '1px solid #E0E5EB',
+ borderRadius: '0.75rem',
+ flex: 'none',
+ alignSelf: 'stretch',
+ width: '100%'
+ },
+ cveInfo: {
+ marginTop: '2%'
+ },
+ vulnerabilityCardDivider: {
+ margin: '1rem 1rem'
+ }
+}));
+
+function VulnerabilityPackageSection(props) {
+ const { cve } = props;
+ const classes = useStyles();
+
+ return (
+
+
+ {cve.packageName}
+
+
+ Package Path
+
+
+ {cve.packagePath}
+
+
+
+
+ Installed Version
+
+
+ {cve.packageInstalledVersion}
+
+
+
+
+ Fixed Version
+
+
+ {cve.packageFixedVersion}
+
+
+
+
+
+ );
+}
+
+export default VulnerabilityPackageSection;
diff --git a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx
index 1111c608..ca4b461a 100644
--- a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx
+++ b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx
@@ -411,6 +411,7 @@ function VulnerabilitiesDetails(props) {
className={classes.view}
selected={selectedViewMore}
onChange={() => setSelectedViewMore(true)}
+ data-testid="expand-list-view-toggle"
>
diff --git a/src/utilities/objectModels.js b/src/utilities/objectModels.js
index 6caa3f65..03403858 100644
--- a/src/utilities/objectModels.js
+++ b/src/utilities/objectModels.js
@@ -100,6 +100,7 @@ const mapCVEInfo = (cveInfo) => {
reference: cve.Reference,
packageList: cve.PackageList?.map((pkg) => ({
packageName: pkg.Name,
+ packagePath: pkg.PackagePath,
packageInstalledVersion: pkg.InstalledVersion,
packageFixedVersion: pkg.FixedVersion
}))
@@ -118,6 +119,7 @@ const mapAllCVEInfo = (cveInfo) => {
description: cve.Description,
reference: cve.Reference,
packageName: packageInfo.Name,
+ packagePath: packageInfo.PackagePath,
packageInstalledVersion: packageInfo.InstalledVersion,
packageFixedVersion: packageInfo.FixedVersion
};