3 Commits

Author SHA1 Message Date
Raul-Cristian Kele
8f4c23bf40 fix: Update tooltip for vulnerability chips
Signed-off-by: Raul-Cristian Kele <raulkeleblk@gmail.com>
2023-08-08 21:33:22 +03:00
Raul-Cristian Kele
54c764c996 patch: update cve api usage
- updated CVEListForImage api calls
- updated ImageListWithCVEFixed api calls
- now cves are shown for specific tag
- fixed tags now only shows tags that match platform with current digest
- moved platform selector on tagdetails page

Signed-off-by: Raul-Cristian Kele <raulkeleblk@gmail.com>
2023-07-27 08:51:27 +03:00
Raul-Cristian Kele
44289c751f fix: fixed login page refresh bug
Signed-off-by: Raul-Cristian Kele <raulkeleblk@gmail.com>
2023-07-19 17:44:44 +03:00
6 changed files with 66 additions and 80 deletions

View File

@@ -1,11 +1,11 @@
import axios from 'axios'; import axios from 'axios';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { sortByCriteria } from 'utilities/sortCriteria'; import { sortByCriteria } from 'utilities/sortCriteria';
import { logoutUser } from 'utilities/authUtilities'; import { isAuthenticationEnabled, logoutUser } from 'utilities/authUtilities';
import { host } from 'host'; import { host } from 'host';
axios.interceptors.request.use((config) => { axios.interceptors.request.use((config) => {
if (config.url.includes(endpoints.authConfig)) { if (config.url.includes(endpoints.authConfig) || !isAuthenticationEnabled()) {
config.withCredentials = false; config.withCredentials = false;
} else { } else {
config.headers['X-ZOT-API-CLIENT'] = 'zot-ui'; config.headers['X-ZOT-API-CLIENT'] = 'zot-ui';
@@ -19,6 +19,7 @@ axios.interceptors.response.use(
}, },
(error) => { (error) => {
if (error?.response?.status === 401) { if (error?.response?.status === 401) {
if (window.location.pathname.includes('/login')) return Promise.reject(error);
logoutUser(); logoutUser();
window.location.replace('/login'); window.location.replace('/login');
return Promise.reject(error); return Promise.reject(error);
@@ -97,10 +98,18 @@ const endpoints = {
} }
return `${query}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`; return `${query}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`;
}, },
imageListWithCVEFixed: (cveId, repoName, { pageNumber = 1, pageSize = 3 }) => imageListWithCVEFixed: (cveId, repoName, { pageNumber = 1, pageSize = 3 }, filter = {}) => {
`/v2/_zot/ext/search?query={ImageListWithCVEFixed(id:"${cveId}", image:"${repoName}", requestedPage: {limit:${pageSize} offset:${ let filterParam = '';
if (filter.Os || filter.Arch) {
filterParam = `,filter:{`;
if (filter.Os) filterParam += ` Os:${!isEmpty(filter.Os) ? `${JSON.stringify(filter.Os)}` : '""'}`;
if (filter.Arch) filterParam += ` Arch:${!isEmpty(filter.Arch) ? `${JSON.stringify(filter.Arch)}` : '""'}`;
filterParam += '}';
}
return `/v2/_zot/ext/search?query={ImageListWithCVEFixed(id:"${cveId}", image:"${repoName}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize
}}) {Page {TotalCount ItemCount} Results {Tag}}}`, }}${filterParam}) {Page {TotalCount ItemCount} Results {Tag}}}`;
},
dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) => dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) =>
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${ `/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize

View File

@@ -1,37 +0,0 @@
import React from 'react';
import { makeStyles } from '@mui/styles';
import { Stack, Typography } from '@mui/material';
const useStyles = makeStyles(() => ({
subtext: {
color: '#52637A',
fontSize: '0.8125rem',
fontWeight: '400',
lineHeight: '154%',
letterSpacing: '0.025rem',
marginBottom: '0'
},
text: {
color: '#0F2139',
fontSize: '0.8125rem',
lineHeight: '154%',
fontWeight: '600',
letterSpacing: '0.025rem'
}
}));
export default function TermsOfService(props) {
const classes = useStyles();
return (
<Stack spacing={0}>
<Typography variant="caption" className={classes.subtext} align="justify" {...props} pb={6}>
By using zot UI, you agree to the Terms of Service. For more information about our privacy practices, see
zot&apos;s Privacy Policy.
</Typography>
<Typography variant="caption" className={classes.text} align="center" {...props}>
Privacy Policy | Terms of Service
</Typography>
</Stack>
);
}

View File

@@ -72,7 +72,7 @@ const useStyles = makeStyles((theme) => ({
})); }));
function VulnerabilitiyCard(props) { function VulnerabilitiyCard(props) {
const classes = useStyles(); const classes = useStyles();
const { cve, name } = props; const { cve, name, platform } = props;
const [openDesc, setOpenDesc] = useState(false); const [openDesc, setOpenDesc] = useState(false);
const [openFixed, setOpenFixed] = useState(false); const [openFixed, setOpenFixed] = useState(false);
const [loadingFixed, setLoadingFixed] = useState(true); const [loadingFixed, setLoadingFixed] = useState(true);
@@ -90,7 +90,12 @@ function VulnerabilitiyCard(props) {
setLoadingFixed(true); setLoadingFixed(true);
api api
.get( .get(
`${host()}${endpoints.imageListWithCVEFixed(cve.id, name, { pageNumber, pageSize: CVE_FIXEDIN_PAGE_SIZE })}`, `${host()}${endpoints.imageListWithCVEFixed(
cve.id,
name,
{ pageNumber, pageSize: CVE_FIXEDIN_PAGE_SIZE },
platform ? { Os: platform.Os, Arch: platform.Arch } : {}
)}`,
abortController.signal abortController.signal
) )
.then((response) => { .then((response) => {

View File

@@ -73,7 +73,7 @@ function VulnerabilitiesDetails(props) {
const [cveData, setCveData] = useState([]); const [cveData, setCveData] = useState([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const abortController = useMemo(() => new AbortController(), []); const abortController = useMemo(() => new AbortController(), []);
const { name, tag } = props; const { name, tag, digest, platform } = props;
// pagination props // pagination props
const [cveFilter, setCveFilter] = useState(''); const [cveFilter, setCveFilter] = useState('');
@@ -81,11 +81,15 @@ function VulnerabilitiesDetails(props) {
const [isEndOfList, setIsEndOfList] = useState(false); const [isEndOfList, setIsEndOfList] = useState(false);
const listBottom = useRef(null); const listBottom = useRef(null);
const getCVERequestName = () => {
return digest !== '' ? `${name}@${digest}` : `${name}:${tag}`;
};
const getPaginatedCVEs = () => { const getPaginatedCVEs = () => {
api api
.get( .get(
`${host()}${endpoints.vulnerabilitiesForRepo( `${host()}${endpoints.vulnerabilitiesForRepo(
`${name}:${tag}`, getCVERequestName(),
{ pageNumber, pageSize: EXPLORE_PAGE_SIZE }, { pageNumber, pageSize: EXPLORE_PAGE_SIZE },
cveFilter cveFilter
)}`, )}`,
@@ -171,7 +175,7 @@ function VulnerabilitiesDetails(props) {
const renderCVEs = () => { const renderCVEs = () => {
return !isEmpty(cveData) ? ( return !isEmpty(cveData) ? (
cveData.map((cve, index) => { cveData.map((cve, index) => {
return <VulnerabilitiyCard key={index} cve={cve} name={name} />; return <VulnerabilitiyCard key={index} cve={cve} name={name} platform={platform} />;
}) })
) : ( ) : (
<div>{!isLoading && <Typography className={classes.none}> No Vulnerabilities </Typography>}</div> <div>{!isLoading && <Typography className={classes.none}> No Vulnerabilities </Typography>}</div>

View File

@@ -59,7 +59,6 @@ const useStyles = makeStyles((theme) => ({
fontSize: '1rem', fontSize: '1rem',
lineHeight: '1.5rem', lineHeight: '1.5rem',
color: '#52637A', color: '#52637A',
padding: '1rem 0 0 0',
maxWidth: '100%', maxWidth: '100%',
[theme.breakpoints.down('md')]: { [theme.breakpoints.down('md')]: {
padding: '0.5rem 0 0 0', padding: '0.5rem 0 0 0',
@@ -209,7 +208,14 @@ function TagDetails() {
case 'IsDependentOn': case 'IsDependentOn':
return <IsDependentOn name={imageDetailData.name} digest={selectedManifest.digest} />; return <IsDependentOn name={imageDetailData.name} digest={selectedManifest.digest} />;
case 'Vulnerabilities': case 'Vulnerabilities':
return <VulnerabilitiesDetails name={reponame} tag={tag} />; return (
<VulnerabilitiesDetails
name={reponame}
tag={tag}
digest={selectedManifest?.digest}
platform={selectedManifest.platform}
/>
);
case 'ReferredBy': case 'ReferredBy':
return <ReferredBy referrers={imageDetailData.referrers} />; return <ReferredBy referrers={imageDetailData.referrers} />;
default: default:
@@ -227,10 +233,10 @@ function TagDetails() {
<Card className={classes.cardRoot}> <Card className={classes.cardRoot}>
<CardContent className={classes.cardContent}> <CardContent className={classes.cardContent}>
<Grid container> <Grid container>
<Grid item xs={12} md={8} className={classes.header}> <Grid item xs={12} md={9} className={classes.header}>
<Stack <Stack
alignItems="center" alignItems="center"
sx={{ width: { xs: '100%', md: 'auto' } }} sx={{ width: { xs: '100%', md: 'auto' }, marginBottom: '1rem' }}
direction={{ xs: 'column', md: 'row' }} direction={{ xs: 'column', md: 'row' }}
spacing={1} spacing={1}
> >
@@ -256,30 +262,29 @@ function TagDetails() {
/> />
<SignatureIconCheck isSigned={imageDetailData.isSigned} /> <SignatureIconCheck isSigned={imageDetailData.isSigned} />
</Stack> </Stack>
<Stack sx={{ width: { xs: '100%', md: 'auto' } }}>
<FormControl sx={{ m: '1', minWidth: '4.6875rem' }} className={classes.sortForm} size="small">
<InputLabel>OS/Arch</InputLabel>
{!isEmpty(selectedManifest) && (
<Select
label="OS/Arch"
value={selectedManifest}
onChange={handleOSArchChange}
MenuProps={{ disableScrollLock: true }}
>
{imageDetailData.manifests.map((el) => (
<MenuItem key={el.digest} value={el}>
{`${el.platform?.Os}/${el.platform?.Arch}`}
</MenuItem>
))}
</Select>
)}
</FormControl>
</Stack>
</Stack> </Stack>
<Typography gutterBottom className={classes.digest}> <Stack direction="row" alignItems="center" spacing="1rem">
Digest: {selectedManifest?.digest} <FormControl sx={{ m: '1', minWidth: '4.6875rem' }} className={classes.sortForm} size="small">
</Typography> <InputLabel>OS/Arch</InputLabel>
{!isEmpty(selectedManifest) && (
<Select
label="OS/Arch"
value={selectedManifest}
onChange={handleOSArchChange}
MenuProps={{ disableScrollLock: true }}
>
{imageDetailData.manifests.map((el) => (
<MenuItem key={el.digest} value={el}>
{`${el.platform?.Os}/${el.platform?.Arch}`}
</MenuItem>
))}
</Select>
)}
</FormControl>
<Typography gutterBottom className={classes.digest}>
Digest: {selectedManifest?.digest}
</Typography>
</Stack>
</Grid> </Grid>
</Grid> </Grid>
</CardContent> </CardContent>

View File

@@ -23,7 +23,7 @@ const VerifiedShieldIcon = createSvgIcon(
const NoneVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const NoneVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<OutlinedBugIcon <OutlinedBugIcon
sx={{ sx={{
color: '#43A047!important', color: '#43A047!important',
@@ -40,7 +40,7 @@ const NoneVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
}; };
const UnknownVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const UnknownVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<OutlinedBugIcon <OutlinedBugIcon
sx={{ sx={{
color: '#52637A', color: '#52637A',
@@ -75,7 +75,7 @@ const FailedScanIcon = () => {
}; };
const LowVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const LowVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<OutlinedBugIcon <OutlinedBugIcon
sx={{ sx={{
color: '#FB8C00', color: '#FB8C00',
@@ -92,7 +92,7 @@ const LowVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
}; };
const MediumVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const MediumVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<FilledBugIcon <FilledBugIcon
sx={{ sx={{
color: '#FB8C00', color: '#FB8C00',
@@ -109,7 +109,7 @@ const MediumVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
}; };
const HighVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const HighVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<OutlinedBugIcon <OutlinedBugIcon
sx={{ sx={{
color: '#E53935', color: '#E53935',
@@ -126,7 +126,7 @@ const HighVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
}; };
const CriticalVulnerabilityIcon = ({ vulnerabilityStringTitle }) => { const CriticalVulnerabilityIcon = ({ vulnerabilityStringTitle }) => {
return ( return (
<Tooltip title={`${vulnerabilityStringTitle} Vulnerability`} placement="top"> <Tooltip title={`${vulnerabilityStringTitle}`} placement="top">
<FilledBugIcon <FilledBugIcon
sx={{ sx={{
color: '#E53935', color: '#E53935',