Commit a4e7abc8 authored by Mark Florian's avatar Mark Florian

Merge branch '199145-add-severity-badge-to-MR-report' into 'master'

Add severity badge to security reports

See merge request gitlab-org/gitlab!26715
parents e47889f9 651851a0
...@@ -23,7 +23,7 @@ GitLab checks the Container Scanning report, compares the found vulnerabilities ...@@ -23,7 +23,7 @@ GitLab checks the Container Scanning report, compares the found vulnerabilities
between the source and target branches, and shows the information right on the between the source and target branches, and shows the information right on the
merge request. merge request.
![Container Scanning Widget](img/container_scanning.png) ![Container Scanning Widget](img/container_scanning_v12_9.png)
## Use cases ## Use cases
......
...@@ -35,12 +35,12 @@ NOTE: **Note:** ...@@ -35,12 +35,12 @@ NOTE: **Note:**
This comparison logic uses only the latest pipeline executed for the target branch's base commit. This comparison logic uses only the latest pipeline executed for the target branch's base commit.
Running the pipeline on any other commit has no effect on the merge request. Running the pipeline on any other commit has no effect on the merge request.
![DAST Widget](img/dast_all.png) ![DAST Widget](img/dast_all_v12_9.png)
By clicking on one of the detected linked vulnerabilities, you will be able to By clicking on one of the detected linked vulnerabilities, you will be able to
see the details and the URL(s) affected. see the details and the URL(s) affected.
![DAST Widget Clicked](img/dast_single.png) ![DAST Widget Clicked](img/dast_single_v12_9.png)
[Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_Application_Security_Testing) [Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_Application_Security_Testing)
is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy) is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
......
...@@ -25,7 +25,7 @@ that is provided by [Auto DevOps](../../../topics/autodevops/index.md). ...@@ -25,7 +25,7 @@ that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
GitLab checks the SAST report, compares the found vulnerabilities between the GitLab checks the SAST report, compares the found vulnerabilities between the
source and target branches, and shows the information right on the merge request. source and target branches, and shows the information right on the merge request.
![SAST Widget](img/sast.png) ![SAST Widget](img/sast_v12_9.png)
The results are sorted by the priority of the vulnerability: The results are sorted by the priority of the vulnerability:
......
<script> <script>
/** /**
* Renders CONTAINER SCANNING body text * Renders CONTAINER SCANNING body text
* [priority]: [name] in [link]:[line] * [severity-badge] [name] in [link]:[line]
*/ */
import ModalOpenName from '~/reports/components/modal_open_name.vue'; import ModalOpenName from '~/reports/components/modal_open_name.vue';
import { humanize } from '~/lib/utils/text_utility'; import SeverityBadge from './severity_badge.vue';
export default { export default {
name: 'ContainerScanningIssueBody', name: 'ContainerScanningIssueBody',
components: { components: {
ModalOpenName, ModalOpenName,
SeverityBadge,
}, },
props: { props: {
issue: { issue: {
...@@ -22,20 +23,12 @@ export default { ...@@ -22,20 +23,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
severity() {
return this.issue.severity ? humanize(this.issue.severity) : null;
},
},
}; };
</script> </script>
<template> <template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> <div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text"> <div class="report-block-list-issue-description-text">
<template v-if="severity"> <severity-badge v-if="issue.severity" class="d-inline-block" :severity="issue.severity" />
{{ severity }}:
</template>
<modal-open-name :issue="issue" :status="status" /> <modal-open-name :issue="issue" :status="status" />
</div> </div>
</div> </div>
......
<script> <script>
/** /**
* Renders DAST body text * Renders DAST body text
* [severity]: [name] * [severity-badge] [name] in [link]:[line]
*/ */
import ModalOpenName from '~/reports/components/modal_open_name.vue'; import ModalOpenName from '~/reports/components/modal_open_name.vue';
import SeverityBadge from './severity_badge.vue';
export default { export default {
name: 'DastIssueBody', name: 'DastIssueBody',
components: { components: {
ModalOpenName, ModalOpenName,
SeverityBadge,
}, },
props: { props: {
issue: { issue: {
...@@ -27,8 +28,7 @@ export default { ...@@ -27,8 +28,7 @@ export default {
<template> <template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> <div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text"> <div class="report-block-list-issue-description-text">
{{ issue.severity }}: <severity-badge v-if="issue.severity" class="d-inline-block" :severity="issue.severity" />
<modal-open-name :issue="issue" :status="status" class="js-modal-dast" /> <modal-open-name :issue="issue" :status="status" class="js-modal-dast" />
</div> </div>
</div> </div>
......
<script> <script>
/** /**
* Renders SAST body text * Renders SAST body text
* [severity]: [name] in [link] : [line] * [severity-badge] [name] in [link]:[line]
*/ */
import ReportLink from '~/reports/components/report_link.vue'; import ReportLink from '~/reports/components/report_link.vue';
import ModalOpenName from '~/reports/components/modal_open_name.vue'; import ModalOpenName from '~/reports/components/modal_open_name.vue';
import { humanize } from '~/lib/utils/text_utility'; import SeverityBadge from './severity_badge.vue';
export default { export default {
name: 'SastIssueBody', name: 'SastIssueBody',
components: { components: {
ReportLink, ReportLink,
ModalOpenName, ModalOpenName,
SeverityBadge,
}, },
props: { props: {
issue: { issue: {
type: Object, type: Object,
...@@ -26,25 +25,14 @@ export default { ...@@ -26,25 +25,14 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
title() {
const { severity, priority } = this.issue;
if (severity) {
return humanize(severity);
}
return priority;
},
},
}; };
</script> </script>
<template> <template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> <div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text"> <div class="report-block-list-issue-description-text">
{{ title }}: <severity-badge v-if="issue.severity" class="d-inline-block" :severity="issue.severity" />
<modal-open-name :issue="issue" :status="status" /> <modal-open-name :issue="issue" :status="status" />
</div> </div>
<report-link v-if="issue.path" :issue="issue" /> <report-link v-if="issue.path" :issue="issue" />
</div> </div>
</template> </template>
---
title: Add severity badge to security reports
merge_request: 26715
author:
type: changed
...@@ -7,8 +7,10 @@ exports[`Container Scanning Issue Body matches snapshot 1`] = ` ...@@ -7,8 +7,10 @@ exports[`Container Scanning Issue Body matches snapshot 1`] = `
<div <div
class="report-block-list-issue-description-text" class="report-block-list-issue-description-text"
> >
<severity-badge-stub
Low: class="d-inline-block"
severity="Low"
/>
<modal-open-name-stub <modal-open-name-stub
issue="[object Object]" issue="[object Object]"
......
...@@ -7,10 +7,11 @@ exports[`Dast Issue Body matches the snaphot 1`] = ` ...@@ -7,10 +7,11 @@ exports[`Dast Issue Body matches the snaphot 1`] = `
<div <div
class="report-block-list-issue-description-text" class="report-block-list-issue-description-text"
> >
<severity-badge-stub
Low: class="d-inline-block"
severity="Low"
/>
<modal-open-name-stub <modal-open-name-stub
class="js-modal-dast" class="js-modal-dast"
issue="[object Object]" issue="[object Object]"
......
...@@ -7,9 +7,11 @@ exports[`Sast Issue Body matches snapshot 1`] = ` ...@@ -7,9 +7,11 @@ exports[`Sast Issue Body matches snapshot 1`] = `
<div <div
class="report-block-list-issue-description-text" class="report-block-list-issue-description-text"
> >
<severity-badge-stub
Medium: class="d-inline-block"
severity="Medium"
/>
<modal-open-name-stub <modal-open-name-stub
issue="[object Object]" issue="[object Object]"
status="failed" status="failed"
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import ContainerScanningIssueBody from 'ee/vue_shared/security_reports/components/container_scanning_issue_body.vue'; import ContainerScanningIssueBody from 'ee/vue_shared/security_reports/components/container_scanning_issue_body.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
describe('Container Scanning Issue Body', () => { describe('Container Scanning Issue Body', () => {
let wrapper; let wrapper;
const createComponent = severity => { const createComponent = (severity = undefined) => {
wrapper = shallowMount(ContainerScanningIssueBody, { wrapper = shallowMount(ContainerScanningIssueBody, {
propsData: { propsData: {
issue: { issue: {
...@@ -30,13 +31,13 @@ describe('Container Scanning Issue Body', () => { ...@@ -30,13 +31,13 @@ describe('Container Scanning Issue Body', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
it('renders severity if present on issue', () => { it('does show SeverityBadge if severity is present', () => {
createComponent('Low'); createComponent('Low');
expect(wrapper.find('.report-block-list-issue-description-text').text()).toBe('Low:'); expect(wrapper.find(SeverityBadge).props('severity')).toBe('Low');
}); });
it('does not render severity if not present on issue', () => { it('does not show SeverityBadge if severity is not present', () => {
createComponent(); createComponent();
expect(wrapper.find('.report-block-list-issue-description-text').text()).toBe(''); expect(wrapper.contains(SeverityBadge)).toBe(false);
}); });
}); });
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { STATUS_FAILED } from '~/reports/constants'; import { STATUS_FAILED } from '~/reports/constants';
import SastIssueBody from 'ee/vue_shared/security_reports/components/sast_issue_body.vue'; import SastIssueBody from 'ee/vue_shared/security_reports/components/sast_issue_body.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import ReportLink from '~/reports/components/report_link.vue'; import ReportLink from '~/reports/components/report_link.vue';
describe('Sast Issue Body', () => { describe('Sast Issue Body', () => {
let wrapper; let wrapper;
const findDescriptionText = () => wrapper.find('.report-block-list-issue-description-text');
const findReportLink = () => wrapper.find(ReportLink); const findReportLink = () => wrapper.find(ReportLink);
const createComponent = issue => { const createComponent = issue => {
...@@ -25,27 +25,23 @@ describe('Sast Issue Body', () => { ...@@ -25,27 +25,23 @@ describe('Sast Issue Body', () => {
it('matches snapshot', () => { it('matches snapshot', () => {
createComponent({ createComponent({
severity: 'medium', severity: 'Medium',
priority: 'high',
}); });
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
it('renders priority if no security are passed', () => { it('does show SeverityBadge if severity is present', () => {
createComponent({ createComponent({
priority: 'high', severity: 'Medium',
}); });
expect(findDescriptionText().text()).toBe('high:'); expect(wrapper.find(SeverityBadge).props('severity')).toBe('Medium');
}); });
it('renders severity', () => { it('does not show SeverityBadge if severity is not present', () => {
createComponent({ createComponent({});
severity: 'medium', expect(wrapper.contains(SeverityBadge)).toBe(false);
});
expect(findDescriptionText().text()).toBe('Medium:');
}); });
it('does not render report link if no path is passed', () => { it('does not render report link if no path is passed', () => {
......
...@@ -5,7 +5,7 @@ export const sastParsedIssues = [ ...@@ -5,7 +5,7 @@ export const sastParsedIssues = [
title: 'Arbitrary file existence disclosure in Action Pack', title: 'Arbitrary file existence disclosure in Action Pack',
path: 'Gemfile.lock', path: 'Gemfile.lock',
line: 12, line: 12,
priority: 'High', severity: 'High',
urlPath: 'foo/Gemfile.lock', urlPath: 'foo/Gemfile.lock',
}, },
]; ];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment