feat: Implemented fixed-in, fixed tagdetails page query
Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
parent
988249588f
commit
9baeb5bba2
@ -4,64 +4,56 @@ import TagDetails from 'components/TagDetails';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const mockImage = {
|
const mockImage = {
|
||||||
ExpandedRepoInfo: {
|
Image: {
|
||||||
Images: [
|
RepoName: 'centos',
|
||||||
{
|
Tag: '8',
|
||||||
Digest: '7374731e3dd3112d41ece21cf2db5a16f11a51b33bf065e98c767893f50d3dec',
|
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||||
Tag: 'latest',
|
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||||
Layers: [
|
Size: '75183423',
|
||||||
{
|
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||||
Size: '28572596',
|
Platform: {
|
||||||
Digest: '3b65ec22a9e96affe680712973e88355927506aa3f792ff03330f3a3eb601a98'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '1835',
|
|
||||||
Digest: '016bc871e2b33f0e2a37272769ebd6defdb4b702f0d41ec1e685f0366b64e64a'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '3059542',
|
|
||||||
Digest: '9ddd649edd82d79ffc6f573cd5da7909ae50596b95aca684a571aff6e36aa8cb'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '6506025',
|
|
||||||
Digest: '39bf776c01e412c9cf35ea7a41f97370c486dee27a2aab228cf2e850a8863e8b'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '149',
|
|
||||||
Digest: 'f7f0405a2fe343547a60a9d4182261ca02d70bb9e47d6cd248f3285d6b41e64c'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '1447',
|
|
||||||
Digest: '89785d0d9c65afe73fbd9bcb29c451090ca84df0e128cf1ecf5712c036e8c9d2'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '261',
|
|
||||||
Digest: 'fd40d84c80b0302ca13faab8210d8c7082814f6f2ab576b3a61f467d03e1cb0b'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '193228772',
|
|
||||||
Digest: 'd50d65ac4752500ab9f3c24c86b4aa218bea9a0bb0a837ae54ffe2e6d2454f5a'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Size: '5067',
|
|
||||||
Digest: '255e24cbd370c0055e0d31e063e63c792fa68aff9e25a7ac0a21d39cf6d47573'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
Summary: {
|
|
||||||
Name: 'mongo',
|
|
||||||
LastUpdated: '2022-08-02T01:30:49.193203152Z',
|
|
||||||
Size: '231383863',
|
|
||||||
Platforms: [
|
|
||||||
{
|
|
||||||
Os: 'linux',
|
Os: 'linux',
|
||||||
Arch: 'amd64'
|
Arch: 'amd64'
|
||||||
|
},
|
||||||
|
Vendor: 'CentOS',
|
||||||
|
History: [
|
||||||
|
{
|
||||||
|
Layer: {
|
||||||
|
Size: '75181999',
|
||||||
|
Digest: 'sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621',
|
||||||
|
Score: null
|
||||||
|
},
|
||||||
|
HistoryDescription: {
|
||||||
|
Created: '2020-12-08T00:22:52.526672082Z',
|
||||||
|
CreatedBy:
|
||||||
|
'/bin/sh -c #(nop) ADD file:bd7a2aed6ede423b719ceb2f723e4ecdfa662b28639c8429731c878e86fb138b in / ',
|
||||||
|
Author: '',
|
||||||
|
Comment: '',
|
||||||
|
EmptyLayer: false
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
Vendors: [''],
|
{
|
||||||
NewestImage: null
|
Layer: null,
|
||||||
|
HistoryDescription: {
|
||||||
|
Created: '2020-12-08T00:22:52.895811646Z',
|
||||||
|
CreatedBy:
|
||||||
|
'/bin/sh -c #(nop) LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201204',
|
||||||
|
Author: '',
|
||||||
|
Comment: '',
|
||||||
|
EmptyLayer: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Layer: null,
|
||||||
|
HistoryDescription: {
|
||||||
|
Created: '2020-12-08T00:22:53.076477777Z',
|
||||||
|
CreatedBy: '/bin/sh -c #(nop) CMD ["/bin/bash"]',
|
||||||
|
Author: '',
|
||||||
|
Comment: '',
|
||||||
|
EmptyLayer: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,6 +65,10 @@ jest.mock('react-router-dom', () => ({
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.scrollTo = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// restore the spy created with spyOn
|
// restore the spy created with spyOn
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
|
@ -2,9 +2,14 @@ import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|||||||
import { api } from 'api';
|
import { api } from 'api';
|
||||||
import VulnerabilitiesDetails from 'components/VulnerabilitiesDetails';
|
import VulnerabilitiesDetails from 'components/VulnerabilitiesDetails';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
const StateVulnerabilitiesWrapper = () => {
|
const StateVulnerabilitiesWrapper = () => {
|
||||||
return <VulnerabilitiesDetails name="mongo" />;
|
return (
|
||||||
|
<MemoryRouter>
|
||||||
|
<VulnerabilitiesDetails name="mongo" />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockCVEList = {
|
const mockCVEList = {
|
||||||
@ -426,6 +431,20 @@ const mockCVEList = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockCVEFixed = {
|
||||||
|
ImageListWithCVEFixed: [
|
||||||
|
{
|
||||||
|
Tag: '1.0.16'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tag: '0.4.33'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tag: '1.0.17'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// restore the spy created with spyOn
|
// restore the spy created with spyOn
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
@ -468,4 +487,16 @@ describe('Vulnerabilties page', () => {
|
|||||||
render(<StateVulnerabilitiesWrapper />);
|
render(<StateVulnerabilitiesWrapper />);
|
||||||
await waitFor(() => expect(error).toBeCalledTimes(1));
|
await waitFor(() => expect(error).toBeCalledTimes(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find out which version fixes the CVEs', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(api, 'get')
|
||||||
|
// @ts-ignore
|
||||||
|
.mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } })
|
||||||
|
// @ts-ignore
|
||||||
|
.mockResolvedValue({ status: 200, data: { data: mockCVEFixed } });
|
||||||
|
render(<StateVulnerabilitiesWrapper />);
|
||||||
|
await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1));
|
||||||
|
await waitFor(() => expect(screen.getAllByText('1.0.16')).toHaveLength(20));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -64,10 +64,14 @@ const endpoints = {
|
|||||||
'/v2/_zot/ext/search?query={RepoListWithNewestImage(){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Title Source Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}',
|
'/v2/_zot/ext/search?query={RepoListWithNewestImage(){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Title Source Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}',
|
||||||
detailedRepoInfo: (name) =>
|
detailedRepoInfo: (name) =>
|
||||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag Layers {Size Digest}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag Layers {Size Digest}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||||
|
detailedImageInfo: (name, tag) =>
|
||||||
|
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName Tag Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
||||||
vulnerabilitiesForRepo: (name) =>
|
vulnerabilitiesForRepo: (name) =>
|
||||||
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag, CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag, CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
||||||
layersDetailsForImage: (name) =>
|
layersDetailsForImage: (name) =>
|
||||||
`/v2/_zot/ext/search?query={Image(image: "${name}"){History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
`/v2/_zot/ext/search?query={Image(image: "${name}"){History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
||||||
|
imageListWithCVEFixed: (cveId, repoName) =>
|
||||||
|
`/v2/_zot/ext/search?query={ImageListWithCVEFixed(id:"${cveId}", image:"${repoName}") {Tag}}`,
|
||||||
dependsOnForImage: (name) => `/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName}}`,
|
dependsOnForImage: (name) => `/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName}}`,
|
||||||
isDependentOnForImage: (name) => `/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName}}`,
|
isDependentOnForImage: (name) => `/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName}}`,
|
||||||
globalSearch: ({ searchQuery = '""', pageNumber = 1, pageSize = 15, filter = {} }) => {
|
globalSearch: ({ searchQuery = '""', pageNumber = 1, pageSize = 15, filter = {} }) => {
|
||||||
|
@ -9,6 +9,7 @@ import { Card, CardContent, Divider, Grid, Stack, Typography } from '@mui/materi
|
|||||||
import makeStyles from '@mui/styles/makeStyles';
|
import makeStyles from '@mui/styles/makeStyles';
|
||||||
import { host } from '../host';
|
import { host } from '../host';
|
||||||
import Monitor from '../assets/Monitor.png';
|
import Monitor from '../assets/Monitor.png';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
card: {
|
card: {
|
||||||
@ -116,9 +117,13 @@ function HistoryLayers(props) {
|
|||||||
const [historyData, setHistoryData] = useState([]);
|
const [historyData, setHistoryData] = useState([]);
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
const { name } = props;
|
const { name, history } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (history && !isEmpty(history)) {
|
||||||
|
setHistoryData(history);
|
||||||
|
setIsLoaded(true);
|
||||||
|
} else {
|
||||||
api
|
api
|
||||||
.get(`${host()}${endpoints.layersDetailsForImage(name)}`)
|
.get(`${host()}${endpoints.layersDetailsForImage(name)}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@ -133,6 +138,7 @@ function HistoryLayers(props) {
|
|||||||
setHistoryData([]);
|
setHistoryData([]);
|
||||||
setIsLoaded(false);
|
setIsLoaded(false);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -175,18 +175,6 @@ function RepoDetails() {
|
|||||||
// return list[Math.floor(Math.random() * list.length)];
|
// return list[Math.floor(Math.random() * list.length)];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const vulnerabilityCheck = () => {
|
|
||||||
// const noneVulnerability = <Chip label="None Vulnerability" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
|
|
||||||
// const unknownVulnerability = <Chip label="Unknown Vulnerability" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
|
||||||
// const lowVulnerability = <Chip label="Low Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
|
||||||
// const mediumVulnerability = <Chip label="Medium Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
|
||||||
// const highVulnerability = <Chip label="High Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
|
||||||
// const criticalVulnerability = <Chip label="Critical Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#E53935!important" }} />}/>;
|
|
||||||
|
|
||||||
// const arrVulnerability = [noneVulnerability, unknownVulnerability, lowVulnerability, mediumVulnerability, highVulnerability, criticalVulnerability]
|
|
||||||
// return(getRandom(arrVulnerability));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const signatureCheck = () => {
|
// const signatureCheck = () => {
|
||||||
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||||
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
||||||
|
@ -118,61 +118,52 @@ const randomImage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function TagDetails() {
|
function TagDetails() {
|
||||||
const [repoDetailData, setRepoDetailData] = useState({});
|
const [imageDetailData, setImageDetailData] = useState({});
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
//const [isLoading, setIsLoading] = useState(false);
|
//const [isLoading, setIsLoading] = useState(false);
|
||||||
const [selectedTab, setSelectedTab] = useState('Layers');
|
const [selectedTab, setSelectedTab] = useState('Layers');
|
||||||
const [tagName, setTagName] = useState('');
|
const [fullName, setFullName] = useState('');
|
||||||
|
|
||||||
// get url param from <Route here (i.e. image name)
|
// get url param from <Route here (i.e. image name)
|
||||||
const { name } = useParams();
|
const { name, tag } = useParams();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
// const { description, overviewTitle, dependencies, dependents } = props;
|
// const { description, overviewTitle, dependencies, dependents } = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// if same-page navigation because of tag update, following 2 lines help ux
|
||||||
|
setSelectedTab('Layers');
|
||||||
|
window?.scrollTo(0, 0);
|
||||||
api
|
api
|
||||||
.get(`${host()}${endpoints.detailedRepoInfo(name)}`)
|
.get(`${host()}${endpoints.detailedImageInfo(name, tag)}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data && response.data.data) {
|
if (response.data && response.data.data) {
|
||||||
let repoInfo = response.data.data.ExpandedRepoInfo;
|
let imageInfo = response.data.data.Image;
|
||||||
let imageData = {
|
let imageData = {
|
||||||
name: name,
|
name: imageInfo.RepoName,
|
||||||
tags: repoInfo.Images[0]?.Tag,
|
tag: imageInfo.Tag,
|
||||||
lastUpdated: repoInfo.Summary?.LastUpdated,
|
lastUpdated: imageInfo.LastUpdated,
|
||||||
size: repoInfo.Summary?.Size,
|
size: imageInfo.Size,
|
||||||
latestDigest: repoInfo.Images[0].Digest,
|
digest: imageInfo.ConfigDigest,
|
||||||
layers: repoInfo.Images[0].Layers,
|
platform: imageInfo.Platform,
|
||||||
platforms: repoInfo.Summary?.Platforms,
|
vendor: imageInfo.Vendor,
|
||||||
vendors: repoInfo.Summary?.Vendors,
|
history: imageInfo.History
|
||||||
newestTag: repoInfo.Summary?.NewestImage?.Tag
|
|
||||||
};
|
};
|
||||||
setRepoDetailData(imageData);
|
setImageDetailData(imageData);
|
||||||
setTagName(imageData.name + ':' + imageData.newestTag);
|
setFullName(imageData.name + ':' + imageData.tag);
|
||||||
|
|
||||||
//setIsLoading(false);
|
//setIsLoading(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setRepoDetailData({});
|
setImageDetailData({});
|
||||||
});
|
});
|
||||||
}, [name]);
|
}, [name, tag]);
|
||||||
//function that returns a random element from an array
|
//function that returns a random element from an array
|
||||||
// function getRandom(list) {
|
// function getRandom(list) {
|
||||||
// return list[Math.floor(Math.random() * list.length)];
|
// return list[Math.floor(Math.random() * list.length)];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const vulnerabilityCheck = () => {
|
|
||||||
// const noneVulnerability = <Chip label="No Vulnerability" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
|
|
||||||
// const unknownVulnerability = <Chip label="Unknown Vulnerability" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
|
||||||
// const lowVulnerability = <Chip label="Low Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
|
||||||
// const mediumVulnerability = <Chip label="Medium Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
|
||||||
// const highVulnerability = <Chip label="High Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
|
||||||
// const criticalVulnerability = <Chip label="Critical Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#E53935!important" }} />}/>;
|
|
||||||
|
|
||||||
// const arrVulnerability = [noneVulnerability, unknownVulnerability, lowVulnerability, mediumVulnerability, highVulnerability, criticalVulnerability]
|
|
||||||
// return(getRandom(arrVulnerability));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const signatureCheck = () => {
|
// const signatureCheck = () => {
|
||||||
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||||
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
||||||
@ -184,7 +175,7 @@ function TagDetails() {
|
|||||||
|
|
||||||
const getPlatform = () => {
|
const getPlatform = () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return repoDetailData?.platforms ? repoDetailData.platforms[0] : '--/--';
|
return imageDetailData?.platform ? imageDetailData.platform : '--/--';
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -192,31 +183,6 @@ function TagDetails() {
|
|||||||
setSelectedTab(newValue);
|
setSelectedTab(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
//will need this but not for now
|
|
||||||
// const renderDependencies = () => {
|
|
||||||
// return (<Card className={classes.card}>
|
|
||||||
// <CardContent>
|
|
||||||
// <Typography variant="h4" align="left">Dependecies ({dependencies || '---'})</Typography>
|
|
||||||
// </CardContent>
|
|
||||||
// </Card>);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const renderDependents = () => {
|
|
||||||
// return (<Card className={classes.card}>
|
|
||||||
// <CardContent>
|
|
||||||
// <Typography variant="h4" align="left">Dependents ({dependents || '---'})</Typography>
|
|
||||||
// </CardContent>
|
|
||||||
// </Card>);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const renderVulnerabilities = () => {
|
|
||||||
// return (<Card className={classes.card}>
|
|
||||||
// <CardContent>
|
|
||||||
// <Typography variant="h4" align="left">Vulnerabilities</Typography>
|
|
||||||
// </CardContent>
|
|
||||||
// </Card>);
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.pageWrapper}>
|
<div className={classes.pageWrapper}>
|
||||||
<Card className={classes.cardRoot}>
|
<Card className={classes.cardRoot}>
|
||||||
@ -237,7 +203,7 @@ function TagDetails() {
|
|||||||
{name}:
|
{name}:
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
repoDetailData?.newestTag
|
tag
|
||||||
}
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
{/* {vulnerabilityCheck()}
|
{/* {vulnerabilityCheck()}
|
||||||
@ -253,7 +219,7 @@ function TagDetails() {
|
|||||||
Digest:{' '}
|
Digest:{' '}
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
repoDetailData?.latestDigest
|
imageDetailData?.digest
|
||||||
}
|
}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -280,13 +246,19 @@ function TagDetails() {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TabPanel value="Layers" className={classes.tabPanel}>
|
<TabPanel value="Layers" className={classes.tabPanel}>
|
||||||
<HistoryLayers name={tagName} />
|
<HistoryLayers
|
||||||
|
name={fullName}
|
||||||
|
history={
|
||||||
|
// @ts-ignore
|
||||||
|
imageDetailData.history
|
||||||
|
}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
||||||
<DependsOn name={tagName} />
|
<DependsOn name={fullName} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
||||||
<IsDependentOn name={tagName} />
|
<IsDependentOn name={fullName} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||||
<VulnerabilitiesDetails name={name} />
|
<VulnerabilitiesDetails name={name} />
|
||||||
@ -299,11 +271,11 @@ function TagDetails() {
|
|||||||
<Grid item xs={4} className={classes.metadata}>
|
<Grid item xs={4} className={classes.metadata}>
|
||||||
<TagDetailsMetadata
|
<TagDetailsMetadata
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
platforms={getPlatform()}
|
platform={getPlatform()}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
size={repoDetailData?.size}
|
size={imageDetailData?.size}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
lastUpdated={repoDetailData?.lastUpdated}
|
lastUpdated={imageDetailData?.lastUpdated}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -35,7 +35,7 @@ const useStyles = makeStyles(() => ({
|
|||||||
|
|
||||||
function TagDetailsMetadata(props) {
|
function TagDetailsMetadata(props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { platforms, lastUpdated, size } = props;
|
const { platform, lastUpdated, size } = props;
|
||||||
const lastDate = (lastUpdated ? DateTime.fromISO(lastUpdated) : DateTime.now().minus({ days: 1 })).toRelative({
|
const lastDate = (lastUpdated ? DateTime.fromISO(lastUpdated) : DateTime.now().minus({ days: 1 })).toRelative({
|
||||||
unit: 'days'
|
unit: 'days'
|
||||||
});
|
});
|
||||||
@ -48,7 +48,7 @@ function TagDetailsMetadata(props) {
|
|||||||
OS/Arch
|
OS/Arch
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" className={classes.metadataBody}>
|
<Typography variant="body1" className={classes.metadataBody}>
|
||||||
{platforms.Os || `----`} / {platforms.Arch || `----`}
|
{platform?.Os || `----`} / {platform?.Arch || `----`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -11,6 +11,8 @@ import { host } from '../host';
|
|||||||
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
||||||
import PestControlIcon from '@mui/icons-material/PestControl';
|
import PestControlIcon from '@mui/icons-material/PestControl';
|
||||||
import Monitor from '../assets/Monitor.png';
|
import Monitor from '../assets/Monitor.png';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
card: {
|
card: {
|
||||||
@ -46,7 +48,15 @@ const useStyles = makeStyles(() => ({
|
|||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
paddingBottom: '0.5rem',
|
paddingBottom: '0.5rem',
|
||||||
paddingTop: '0.5rem'
|
paddingTop: '0.5rem',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
color: '#52637A',
|
||||||
|
fontSize: '1rem',
|
||||||
|
letterSpacing: '0.009375rem',
|
||||||
|
paddingRight: '1rem',
|
||||||
|
textDecorationLine: 'underline'
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
width: '27.25rem',
|
width: '27.25rem',
|
||||||
@ -154,8 +164,40 @@ const vulnerabilityCheck = (status) => {
|
|||||||
|
|
||||||
function VulnerabilitiyCard(props) {
|
function VulnerabilitiyCard(props) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { cve } = props;
|
const { cve, name } = props;
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [loadingFixed, setLoadingFixed] = useState(true);
|
||||||
|
const [fixedInfo, setFixedInfo] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoadingFixed(true);
|
||||||
|
api
|
||||||
|
.get(`${host()}${endpoints.imageListWithCVEFixed(cve.Id, name)}`)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data && response.data.data) {
|
||||||
|
const fixedTagsList = response.data.data.ImageListWithCVEFixed?.map((e) => e.Tag);
|
||||||
|
setFixedInfo(fixedTagsList);
|
||||||
|
}
|
||||||
|
setLoadingFixed(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderFixedVer = () => {
|
||||||
|
if (!isEmpty(fixedInfo)) {
|
||||||
|
return fixedInfo.map((tag, index) => {
|
||||||
|
return (
|
||||||
|
<Link key={index} to={`/image/${encodeURIComponent(name)}/tag/${tag}`} className={classes.link}>
|
||||||
|
{tag}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return 'Not fixed';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={classes.card} raised>
|
<Card className={classes.card} raised>
|
||||||
@ -179,6 +221,15 @@ function VulnerabilitiyCard(props) {
|
|||||||
{cve.Title}
|
{cve.Title}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack sx={{ flexDirection: 'row' }}>
|
||||||
|
<Typography variant="body1" align="left" className={classes.title}>
|
||||||
|
Fixed In:{' '}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" align="left" className={classes.values} noWrap>
|
||||||
|
{' '}
|
||||||
|
{loadingFixed ? 'Loading...' : renderFixedVer()}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
color: '#1479FF',
|
color: '#1479FF',
|
||||||
@ -234,7 +285,7 @@ function VulnerabilitiesDetails(props) {
|
|||||||
return (
|
return (
|
||||||
cves &&
|
cves &&
|
||||||
cves.map((cve, index) => {
|
cves.map((cve, index) => {
|
||||||
return <VulnerabilitiyCard key={index} cve={cve} />;
|
return <VulnerabilitiyCard key={index} cve={cve} name={name} />;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user