Skip to content

Commit

Permalink
Display unique vulnerabilities in security report (#3773)
Browse files Browse the repository at this point in the history
Signed-off-by: Cintia Sanchez Garcia <[email protected]>
  • Loading branch information
cynthia-sg authored Apr 18, 2024
1 parent 06c668d commit 775d9f5
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 13 deletions.
16 changes: 15 additions & 1 deletion web/src/layout/package/securityReport/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import API from '../../../api';
import { FixableVulnerabilitiesInReport, RepositoryKind, SecurityReport, SecurityReportSummary } from '../../../types';
import alertDispatcher from '../../../utils/alertDispatcher';
import isFuture from '../../../utils/isFuture';
import { filterFixableVulnerabilities, prepareFixableSummary } from '../../../utils/vulnerabilities';
import sumObjectValues from '../../../utils/sumObjectValues';
import {
filterFixableVulnerabilities,
prepareFixableSummary,
prepareUniqueVulnerabilitiesSummary,
} from '../../../utils/vulnerabilities';
import Modal from '../../common/Modal';
import styles from './Modal.module.css';
import OldVulnerabilitiesWarning from './OldVulnerabilitiesWarning';
Expand Down Expand Up @@ -49,6 +54,8 @@ const SecurityModal = (props: Props) => {
const [hasOnlyOneTarget, setHasOnlyOneTarget] = useState<boolean>(false);
const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);
const [fixableReportSummary, setFixableReportSummary] = useState<FixableVulnerabilitiesInReport | undefined>();
const [uniqueSummary, setUniqueSummary] = useState<SecurityReportSummary | null>(null);
const [totalUniqueVulnerabilities, setTotalUniqueVulnerabilities] = useState<number>(0);
const [showOnlyFixableVulnerabilities, setShowOnlyFixableVulnerabilities] = useState<boolean>(false);
const allVulnerabilitiesAreFixable =
!isUndefined(fixableReportSummary) && fixableReportSummary.total === props.totalVulnerabilities;
Expand Down Expand Up @@ -79,6 +86,11 @@ const SecurityModal = (props: Props) => {
const fixableVulnerabilities = filterFixableVulnerabilities(currentReport);
setFixableReport(fixableVulnerabilities);
setFixableReportSummary(prepareFixableSummary(fixableVulnerabilities));
const uniqueSummary = prepareUniqueVulnerabilitiesSummary(currentReport);
setUniqueSummary(uniqueSummary);
if (!isNull(uniqueSummary)) {
setTotalUniqueVulnerabilities(sumObjectValues(uniqueSummary));
}
activateTargetWhenIsOnlyOne(currentReport);
setIsLoading(false);
setOpenStatus(true);
Expand Down Expand Up @@ -228,6 +240,8 @@ const SecurityModal = (props: Props) => {
fixableSummary={fixableReportSummary.summary}
totalFixableVulnerabilities={fixableReportSummary.total}
allVulnerabilitiesAreFixable={allVulnerabilitiesAreFixable}
uniqueSummary={uniqueSummary}
totalUniqueVulnerabilities={totalUniqueVulnerabilities}
/>
)}

Expand Down
18 changes: 16 additions & 2 deletions web/src/layout/package/securityReport/Summary.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const defaultProps = {
medium: 41,
unknown: 0,
},
uniqueSummary: {
critical: 2,
high: 7,
low: 40,
medium: 60,
unknown: 0,
},
totalUniqueVulnerabilities: 109,
allVulnerabilitiesAreFixable: false,
};

Expand All @@ -39,15 +47,17 @@ describe('SecuritySummary', () => {
render(<SecuritySummary {...defaultProps} />);
expect(screen.getByText('170')).toBeInTheDocument();
expect(screen.getByText(/have been detected in this package's/)).toBeInTheDocument();
expect(screen.getByText('2')).toBeInTheDocument();
expect(screen.getAllByText('2')).toHaveLength(2);
expect(screen.getByText('10')).toBeInTheDocument();
expect(screen.getByText('53')).toBeInTheDocument();
expect(screen.getByText('105')).toBeInTheDocument();
expect(screen.queryByText('0')).toBeNull();
expect(screen.getByText('80')).toBeInTheDocument();
expect(screen.getByText('7')).toBeInTheDocument();
expect(screen.getAllByText('7')).toHaveLength(2);
expect(screen.getByText('32')).toBeInTheDocument();
expect(screen.getByText('41')).toBeInTheDocument();
expect(screen.getByText('40')).toBeInTheDocument();
expect(screen.getByText('60')).toBeInTheDocument();
});

it('renders component with 0 vulnerabilities', () => {
Expand All @@ -67,6 +77,8 @@ describe('SecuritySummary', () => {
low: 0,
}}
allVulnerabilitiesAreFixable={false}
uniqueSummary={null}
totalUniqueVulnerabilities={0}
/>
);
expect(screen.getByText(/No vulnerabilities have been detected in this package's/)).toBeInTheDocument();
Expand All @@ -90,6 +102,8 @@ describe('SecuritySummary', () => {
low: 0,
}}
allVulnerabilitiesAreFixable={false}
uniqueSummary={null}
totalUniqueVulnerabilities={0}
/>
);
expect(screen.getByText(/No vulnerabilities have been detected in this package's/)).toBeInTheDocument();
Expand Down
43 changes: 36 additions & 7 deletions web/src/layout/package/securityReport/Summary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isUndefined } from 'lodash';
import { isNull, isUndefined } from 'lodash';

import { RepositoryKind, SecurityReportSummary, VulnerabilitySeverity } from '../../../types';
import { SEVERITY_ORDER, SEVERITY_RATING } from '../../../utils/data';
Expand All @@ -9,6 +9,8 @@ interface Props {
totalVulnerabilities: number;
summary: SecurityReportSummary;
fixableSummary: SecurityReportSummary;
uniqueSummary: SecurityReportSummary | null;
totalUniqueVulnerabilities: number;
totalFixableVulnerabilities: number;
allVulnerabilitiesAreFixable: boolean;
}
Expand All @@ -22,11 +24,25 @@ const SecuritySummary = (props: Props) => {
}
};

const getFixableVulnerabilitiesNumber = (): JSX.Element => {
if (props.totalFixableVulnerabilities > 0) {
const getExtraData = (): JSX.Element => {
if (props.totalFixableVulnerabilities > 0 || props.totalUniqueVulnerabilities > 0) {
const visibleFixable = props.totalFixableVulnerabilities > 0 && !props.allVulnerabilitiesAreFixable;

return (
<>
(<span className="fw-bold">{props.totalFixableVulnerabilities}</span> fixable){' '}
(
{props.totalUniqueVulnerabilities > 0 && (
<>
<span className="fw-bold">{props.totalUniqueVulnerabilities}</span> unique
{visibleFixable && <>, </>}
</>
)}
{visibleFixable && (
<>
<span className="fw-bold">{props.totalFixableVulnerabilities}</span> fixable
</>
)}
){' '}
</>
);
} else {
Expand All @@ -36,6 +52,7 @@ const SecuritySummary = (props: Props) => {

const renderProgressBar = (summary: SecurityReportSummary, total: number, legend: string): JSX.Element | null => {
if (total === 0) return null;

return (
<>
<div className="fw-bold text-uppercase text-muted">
Expand Down Expand Up @@ -70,10 +87,20 @@ const SecuritySummary = (props: Props) => {
return (
<div className="mb-5">
<div className="h5 my-3 pt-2">
{getVulnerabilitiesNumber()} vulnerabilities {getFixableVulnerabilitiesNumber()} have been detected in this
package's <span className="fw-bold">{props.repoKind === RepositoryKind.Container ? 'image' : 'images'}</span>.
{getVulnerabilitiesNumber()} vulnerabilities {getExtraData()} have been detected in this package's{' '}
<span className="fw-bold">{props.repoKind === RepositoryKind.Container ? 'image' : 'images'}</span>.
</div>

{!isNull(props.uniqueSummary) && (
<>
{renderProgressBar(
props.uniqueSummary,
props.totalUniqueVulnerabilities,
`Unique vulnerabilities (${props.totalUniqueVulnerabilities})`
)}
</>
)}

{!props.allVulnerabilitiesAreFixable && (
<>
{renderProgressBar(
Expand All @@ -87,7 +114,9 @@ const SecuritySummary = (props: Props) => {
{renderProgressBar(
props.summary,
props.totalVulnerabilities,
props.allVulnerabilitiesAreFixable ? '' : `All vulnerabilities (${props.totalVulnerabilities})`
props.allVulnerabilitiesAreFixable && isNull(props.uniqueSummary)
? ''
: `All vulnerabilities (${props.totalVulnerabilities})`
)}
</div>
);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 775d9f5

Please sign in to comment.