Skip to content

Commit

Permalink
feat: add cve summary in vulnerability tab
Browse files Browse the repository at this point in the history
Signed-off-by: Laurentiu Niculae <[email protected]>
  • Loading branch information
laurentiuNiculae committed Dec 19, 2023
1 parent c375c06 commit 464487e
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const endpoints = {
if (!isEmpty(searchTerm)) {
query += `, searchedCVE: "${searchTerm}"`;
}
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}} Summary {Count UnknownCount LowCount MediumCount HighCount CriticalCount}}}`;
},
imageListWithCVEFixed: (cveId, repoName, { pageNumber = 1, pageSize = 3 }, filter = {}) => {
let filterParam = '';
Expand Down
89 changes: 89 additions & 0 deletions src/components/Shared/VulnerabilityCountCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';

import makeStyles from '@mui/styles/makeStyles';
import { Stack, Tooltip } from '@mui/material';

const criticalColor = '#ff5c74';
const criticalBorderColor = '#f9546d';

const highColor = '#ff6840';
const highBorderColor = '#ee6b49';

const mediumColor = '#ffa052';
const mediumBorderColor = '#f19d5b';

const lowColor = '#f9f486';
const lowBorderColor = '#f0ed94';

const unknownColor = '#f2ffdd';
const unknownBorderColor = '#e9f4d7';

const useStyles = makeStyles((theme) => ({
cveCountCard: {
display: 'flex',
alignItems: 'center',
paddingLeft: '0.5rem',
paddingRight: '0.5rem',
color: theme.palette.primary.main,
fontSize: '1rem',
fontWeight: '600',
borderRadius: '3px',
marginBottom: '0'
},
criticalSeverity: {
backgroundColor: criticalColor,
border: '1px solid ' + criticalBorderColor
},
highSeverity: {
backgroundColor: highColor,
border: '1px solid ' + highBorderColor
},
mediumSeverity: {
backgroundColor: mediumColor,
border: '1px solid ' + mediumBorderColor
},
lowSeverity: {
backgroundColor: lowColor,
border: '1px solid ' + lowBorderColor
},
unknownSeverity: {
backgroundColor: unknownColor,
border: '1px solid ' + unknownBorderColor
},
severityList: {
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
gap: '0.5em'
}
}));

function VulnerabilitiyCountCard(props) {
const classes = useStyles();
const { total, critical, high, medium, low, unknown } = props;

return (
<Stack direction="row" spacing="0.5em">
<div className={[classes.cveCountCard].join(' ')}>Total {total}</div>
<div className={classes.severityList}>
<Tooltip title="Critical">
<div className={[classes.cveCountCard, classes.criticalSeverity].join(' ')}>C {critical}</div>
</Tooltip>
<Tooltip title="High">
<div className={[classes.cveCountCard, classes.highSeverity].join(' ')}>H {high}</div>
</Tooltip>
<Tooltip title="Medium">
<div className={[classes.cveCountCard, classes.mediumSeverity].join(' ')}>M {medium}</div>
</Tooltip>
<Tooltip title="Low">
<div className={[classes.cveCountCard, classes.lowSeverity].join(' ')}>L {low}</div>
</Tooltip>
<Tooltip title="Unknown">
<div className={[classes.cveCountCard, classes.unknownSeverity].join(' ')}>U {unknown}</div>
</Tooltip>
</div>
</Stack>
);
}

export default VulnerabilitiyCountCard;
50 changes: 47 additions & 3 deletions src/components/Tag/Tabs/VulnerabilitiesDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { EXPLORE_PAGE_SIZE } from 'utilities/paginationConstants';
import SearchIcon from '@mui/icons-material/Search';

import VulnerabilitiyCard from '../../Shared/VulnerabilityCard';
import VulnerabilityCountCard from '../../Shared/VulnerabilityCountCard';

const useStyles = makeStyles((theme) => ({
title: {
Expand All @@ -22,6 +23,12 @@ const useStyles = makeStyles((theme) => ({
fontWeight: '600',
marginBottom: '0'
},
cveCountSummary: {
color: theme.palette.primary.main,
fontSize: '1.5rem',
fontWeight: '600',
marginBottom: '0'
},
cveId: {
color: theme.palette.primary.main,
fontSize: '1rem',
Expand Down Expand Up @@ -71,6 +78,7 @@ const useStyles = makeStyles((theme) => ({
function VulnerabilitiesDetails(props) {
const classes = useStyles();
const [cveData, setCveData] = useState([]);
const [cveSummary, setCVESummary] = useState({});
const [isLoading, setIsLoading] = useState(true);
const abortController = useMemo(() => new AbortController(), []);
const { name, tag, digest, platform } = props;
Expand Down Expand Up @@ -98,9 +106,23 @@ function VulnerabilitiesDetails(props) {
.then((response) => {
if (response.data && response.data.data) {
let cveInfo = response.data.data.CVEListForImage?.CVEList;
let summary = response.data.data.CVEListForImage?.Summary;
let cveListData = mapCVEInfo(cveInfo);
setCveData((previousState) => (pageNumber === 1 ? cveListData : [...previousState, ...cveListData]));
setIsEndOfList(response.data.data.CVEListForImage.Page?.ItemCount < EXPLORE_PAGE_SIZE);
setCVESummary((previousState) => {
if (isEmpty(summary)) {
return previousState;
}
return {
Count: summary.Count,
UnknownCount: summary.UnknownCount,
LowCount: summary.LowCount,
MediumCount: summary.MediumCount,
HighCount: summary.HighCount,
CriticalCount: summary.CriticalCount
};
});
} else if (response.data.errors) {
setIsEndOfList(true);
}
Expand All @@ -110,6 +132,7 @@ function VulnerabilitiesDetails(props) {
console.error(e);
setIsLoading(false);
setCveData([]);
setCVESummary(() => {});
setIsEndOfList(true);
});
};
Expand Down Expand Up @@ -182,6 +205,24 @@ function VulnerabilitiesDetails(props) {
);
};

const renderCVESummary = () => {
if (cveSummary === undefined) {
return;
}
return !isEmpty(cveSummary) ? (
<VulnerabilityCountCard
total={cveSummary.Count}
critical={cveSummary.CriticalCount}
high={cveSummary.HighCount}
medium={cveSummary.MediumCount}
low={cveSummary.LowCount}
unknown={cveSummary.UnknownCount}
/>
) : (
<div>{!isLoading && <Typography className={classes.none}> No Vulnerabilities </Typography>}</div>
);
};

const renderListBottom = () => {
if (isLoading) {
return <Loading />;
Expand All @@ -194,9 +235,12 @@ function VulnerabilitiesDetails(props) {

return (
<Stack direction="column" spacing="1rem" data-testid="vulnerability-container">
<Typography variant="h4" gutterBottom component="div" align="left" className={classes.title}>
Vulnerabilities
</Typography>
<Stack direction="row" justifyContent="space-between">
<Typography variant="h4" gutterBottom component="div" align="left" className={classes.title}>
Vulnerabilities
</Typography>
{renderCVESummary()}
</Stack>
<Stack className={classes.search}>
<InputBase
placeholder={'Search'}
Expand Down

0 comments on commit 464487e

Please sign in to comment.