Commit 20d67a5b authored by Filipa Lacerda's avatar Filipa Lacerda

Adds support to render Dast and Sast Container reports in Pipeline view

parent fa41cf93
...@@ -115,6 +115,10 @@ export default () => { ...@@ -115,6 +115,10 @@ export default () => {
const dependencyScanningHelpPath = datasetOptions.dependencyScanningHelpPath; const dependencyScanningHelpPath = datasetOptions.dependencyScanningHelpPath;
const vulnerabilityFeedbackPath = datasetOptions.vulnerabilityFeedbackPath; const vulnerabilityFeedbackPath = datasetOptions.vulnerabilityFeedbackPath;
const vulnerabilityFeedbackHelpPath = datasetOptions.vulnerabilityFeedbackHelpPath; const vulnerabilityFeedbackHelpPath = datasetOptions.vulnerabilityFeedbackHelpPath;
const dastEndpoint = datasetOptions.dastEndpoint;
const sastContainerEndpoint = datasetOptions.sastContainerEndpoint;
const dastHelpPath = datasetOptions.dastHelpPath;
const sastContainerHelpPath = datasetOptions.sastContainerHelpPath;
const pipelineId = parseInt(datasetOptions.pipelineId, 10); const pipelineId = parseInt(datasetOptions.pipelineId, 10);
const store = createStore(); const store = createStore();
...@@ -164,6 +168,10 @@ export default () => { ...@@ -164,6 +168,10 @@ export default () => {
vulnerabilityFeedbackPath, vulnerabilityFeedbackPath,
vulnerabilityFeedbackHelpPath, vulnerabilityFeedbackHelpPath,
pipelineId, pipelineId,
dastHeadPath: dastEndpoint,
sastContainerHeadPath: sastContainerEndpoint,
dastHelpPath,
sastContainerHelpPath,
}, },
on: { on: {
updateBadgeCount: this.updateBadge, updateBadgeCount: this.updateBadge,
......
#js-pipeline-header-vue.pipeline-header-container #js-pipeline-header-vue.pipeline-header-container
- sast_artifact = @pipeline.sast_artifact - sast_artifact = @pipeline.sast_artifact
- dependency_artifact = @pipeline.dependency_scanning_artifact - dependency_artifact = @pipeline.dependency_scanning_artifact
- dast_artifact = @pipeline.dast_artifact
- sast_container_artifact = @pipeline.sast_container_artifact
- if @commit.present? - if @commit.present?
.commit-box .commit-box
...@@ -36,5 +38,5 @@ ...@@ -36,5 +38,5 @@
= link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
- if sast_artifact || dependency_artifact - if sast_artifact || dependency_artifact || dast_artifact || sast_container_artifact
.js-sast-summary .js-sast-summary
- expose_sast_data = @pipeline.expose_sast_data? - expose_sast_data = @pipeline.expose_sast_data?
- expose_dependency_data = @pipeline.expose_dependency_scanning_data? - expose_dependency_data = @pipeline.expose_dependency_scanning_data?
- expose_dast_data = @pipeline.expose_dast_data?
- expose_sast_container_data = @pipeline.expose_sast_container_data?
- blob_path = project_blob_path(@project, @pipeline.sha) - blob_path = project_blob_path(@project, @pipeline.sha)
.tabs-holder .tabs-holder
...@@ -84,13 +86,17 @@ ...@@ -84,13 +86,17 @@
%pre.build-trace.build-trace-rounded %pre.build-trace.build-trace-rounded
%code.bash.js-build-output %code.bash.js-build-output
= build_summary(build) = build_summary(build)
- if expose_sast_data || expose_dependency_data - if expose_sast_data || expose_dependency_data || expose_dast_data || expose_sast_container_data
#js-tab-security.build-security.tab-pane #js-tab-security.build-security.tab-pane
#js-security-report-app{ data: { endpoint: expose_sast_data ? sast_artifact_url(@pipeline) : nil, #js-security-report-app{ data: { endpoint: expose_sast_data ? sast_artifact_url(@pipeline) : nil,
blob_path: blob_path, blob_path: blob_path,
dependency_scanning_endpoint: expose_dependency_data ? dependency_scanning_artifact_url(@pipeline) : nil, dependency_scanning_endpoint: expose_dependency_data ? dependency_scanning_artifact_url(@pipeline) : nil,
dast_endpoint: expose_dast_data ? dast_artifact_url(@pipeline) : nil,
sast_container_endpoint: expose_sast_container_data ? sast_container_artifact_url(@pipeline) : nil,
pipeline_id: @pipeline.id, pipeline_id: @pipeline.id,
vulnerability_feedback_path: project_vulnerability_feedback_index_path(@project), vulnerability_feedback_path: project_vulnerability_feedback_index_path(@project),
vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"), vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"),
sast_help_path: help_page_path('user/project/merge_requests/sast'), sast_help_path: help_page_path('user/project/merge_requests/sast'),
dependency_scanning_help_path: help_page_path('user/project/merge_requests/dependency_scanning')} } dependency_scanning_help_path: help_page_path('user/project/merge_requests/dependency_scanning'),
dast_help_path: help_page_path('user/project/merge_requests/dast'),
sast_container_help_path: help_page_path('user/project/merge_requests/sast_container')} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { s__, sprintf, n__ } from '~/locale'; import { s__, sprintf, n__ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { SAST } from './store/constants'; import { SAST, DAST, SAST_CONTAINER } from './store/constants';
import ReportSection from './components/report_section.vue'; import ReportSection from './components/report_section.vue';
import IssueModal from './components/modal.vue'; import IssueModal from './components/modal.vue';
import mixin from './mixins/security_report_mixin'; import mixin from './mixins/security_report_mixin';
...@@ -24,6 +24,16 @@ export default { ...@@ -24,6 +24,16 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
dastHeadPath: {
type: String,
required: false,
default: null,
},
sastContainerHeadPath: {
type: String,
required: false,
default: null,
},
dependencyScanningHeadPath: { dependencyScanningHeadPath: {
type: String, type: String,
required: false, required: false,
...@@ -34,6 +44,16 @@ export default { ...@@ -34,6 +44,16 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
sastContainerHelpPath: {
type: String,
required: false,
default: '',
},
dastHelpPath: {
type: String,
required: false,
default: '',
},
dependencyScanningHelpPath: { dependencyScanningHelpPath: {
type: String, type: String,
required: false, required: false,
...@@ -56,8 +76,10 @@ export default { ...@@ -56,8 +76,10 @@ export default {
}, },
}, },
sast: SAST, sast: SAST,
dast: DAST,
sastContainer: SAST_CONTAINER,
computed: { computed: {
...mapState(['sast', 'dependencyScanning']), ...mapState(['sast', 'dependencyScanning', 'sastContainer', 'dast']),
sastText() { sastText() {
return this.summaryTextBuilder('SAST', this.sast.newIssues.length); return this.summaryTextBuilder('SAST', this.sast.newIssues.length);
...@@ -69,6 +91,14 @@ export default { ...@@ -69,6 +91,14 @@ export default {
this.dependencyScanning.newIssues.length, this.dependencyScanning.newIssues.length,
); );
}, },
sastContainerText() {
return this.summaryTextBuilder('Container scanning', this.sastContainer.newIssues.length);
},
dastText() {
return this.summaryTextBuilder('DAST', this.dast.newIssues.length);
},
}, },
created() { created() {
// update the store with the received props // update the store with the received props
...@@ -98,6 +128,30 @@ export default { ...@@ -98,6 +128,30 @@ export default {
createFlash(s__('ciReport|There was an error loading dependency scanning report')), createFlash(s__('ciReport|There was an error loading dependency scanning report')),
); );
} }
if (this.sastContainerHeadPath) {
this.setSastContainerHeadPath(this.sastContainerHeadPath);
this.fetchSastContainerReports()
.then(() => {
this.$emit('updateBadgeCount', this.sastContainer.newIssues.length);
})
.catch(() =>
createFlash(s__('ciReport|There was an error loading container scanning report')),
);
}
if (this.dastHeadPath) {
this.setDastHeadPath(this.dastHeadPath);
this.fetchDastReports()
.then(() => {
this.$emit('updateBadgeCount', this.dast.newIssues.length);
})
.catch(() =>
createFlash(s__('ciReport|There was an error loading DAST report')),
);
}
}, },
methods: { methods: {
...@@ -105,8 +159,12 @@ export default { ...@@ -105,8 +159,12 @@ export default {
'setHeadBlobPath', 'setHeadBlobPath',
'setSastHeadPath', 'setSastHeadPath',
'setDependencyScanningHeadPath', 'setDependencyScanningHeadPath',
'setSastContainerHeadPath',
'setDastHeadPath',
'fetchSastReports', 'fetchSastReports',
'fetchDependencyScanningReports', 'fetchDependencyScanningReports',
'fetchSastContainerReports',
'fetchDastReports',
'setVulnerabilityFeedbackPath', 'setVulnerabilityFeedbackPath',
'setVulnerabilityFeedbackHelpPath', 'setVulnerabilityFeedbackHelpPath',
'setPipelineId', 'setPipelineId',
...@@ -164,6 +222,32 @@ export default { ...@@ -164,6 +222,32 @@ export default {
:popover-options="dependencyScanningPopover" :popover-options="dependencyScanningPopover"
/> />
<report-section
v-if="sastContainerHeadPath"
class="js-dependency-scanning-widget split-report-section"
:type="$options.sastContainer"
:status="checkReportStatus(sastContainer.isLoading, sastContainer.hasError)"
:loading-text="translateText('Container scanning').loading"
:error-text="translateText('Container scanning').error"
:success-text="sastContainerText"
:unresolved-issues="sastContainer.newIssues"
:has-issues="sastContainer.newIssues.length > 0"
:popover-options="sastContainerPopover"
/>
<report-section
v-if="dastHeadPath"
class="js-dast-widget split-report-section"
:type="$options.dast"
:status="checkReportStatus(dast.isLoading, dast.hasError)"
:loading-text="translateText('DAST').loading"
:error-text="translateText('DAST').error"
:success-text="dastText"
:unresolved-issues="dast.newIssues"
:has-issues="dast.newIssues.length > 0"
:popover-options="dastPopover"
/>
<issue-modal /> <issue-modal />
</div> </div>
</template> </template>
---
title: Render container scanning and dast reports in pipeline view
merge_request:
author:
type: added
...@@ -5,7 +5,7 @@ import component from 'ee/vue_shared/security_reports/split_security_reports_app ...@@ -5,7 +5,7 @@ import component from 'ee/vue_shared/security_reports/split_security_reports_app
import createStore from 'ee/vue_shared/security_reports/store'; import createStore from 'ee/vue_shared/security_reports/store';
import state from 'ee/vue_shared/security_reports/store/state'; import state from 'ee/vue_shared/security_reports/store/state';
import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper'; import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { sastIssues } from './mock_data'; import { sastIssues, dast, dockerReport } from './mock_data';
describe('Slipt security reports app', () => { describe('Slipt security reports app', () => {
const Component = Vue.extend(component); const Component = Vue.extend(component);
...@@ -34,6 +34,8 @@ describe('Slipt security reports app', () => { ...@@ -34,6 +34,8 @@ describe('Slipt security reports app', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('sast_head.json').reply(200, sastIssues); mock.onGet('sast_head.json').reply(200, sastIssues);
mock.onGet('dss_head.json').reply(200, sastIssues); mock.onGet('dss_head.json').reply(200, sastIssues);
mock.onGet('dast_head.json').reply(200, dast);
mock.onGet('sast_container_head.json').reply(200, dockerReport);
mock.onGet('vulnerability_feedback_path.json').reply(200, []); mock.onGet('vulnerability_feedback_path.json').reply(200, []);
vm = mountComponentWithStore(Component, { vm = mountComponentWithStore(Component, {
...@@ -43,10 +45,14 @@ describe('Slipt security reports app', () => { ...@@ -43,10 +45,14 @@ describe('Slipt security reports app', () => {
baseBlobPath: 'path', baseBlobPath: 'path',
sastHeadPath: 'sast_head.json', sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json', dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path', sastHelpPath: 'path',
dependencyScanningHelpPath: 'path', dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json', vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path', vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123, pipelineId: 123,
}, },
}); });
...@@ -57,6 +63,8 @@ describe('Slipt security reports app', () => { ...@@ -57,6 +63,8 @@ describe('Slipt security reports app', () => {
expect(vm.$el.textContent).toContain('SAST is loading'); expect(vm.$el.textContent).toContain('SAST is loading');
expect(vm.$el.textContent).toContain('Dependency scanning is loading'); expect(vm.$el.textContent).toContain('Dependency scanning is loading');
expect(vm.$el.textContent).toContain('Container scanning is loading');
expect(vm.$el.textContent).toContain('DAST is loading');
setTimeout(() => { setTimeout(() => {
done(); done();
...@@ -68,6 +76,8 @@ describe('Slipt security reports app', () => { ...@@ -68,6 +76,8 @@ describe('Slipt security reports app', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('sast_head.json').reply(200, sastIssues); mock.onGet('sast_head.json').reply(200, sastIssues);
mock.onGet('dss_head.json').reply(200, sastIssues); mock.onGet('dss_head.json').reply(200, sastIssues);
mock.onGet('dast_head.json').reply(200, dast);
mock.onGet('sast_container_head.json').reply(200, dockerReport);
mock.onGet('vulnerability_feedback_path.json').reply(200, []); mock.onGet('vulnerability_feedback_path.json').reply(200, []);
vm = mountComponentWithStore(Component, { vm = mountComponentWithStore(Component, {
...@@ -77,10 +87,14 @@ describe('Slipt security reports app', () => { ...@@ -77,10 +87,14 @@ describe('Slipt security reports app', () => {
baseBlobPath: 'path', baseBlobPath: 'path',
sastHeadPath: 'sast_head.json', sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json', dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path', sastHelpPath: 'path',
dependencyScanningHelpPath: 'path', dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json', vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path', vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123, pipelineId: 123,
}, },
}); });
...@@ -95,6 +109,12 @@ describe('Slipt security reports app', () => { ...@@ -95,6 +109,12 @@ describe('Slipt security reports app', () => {
expect(removeBreakLine(vm.$el.textContent)).toContain( expect(removeBreakLine(vm.$el.textContent)).toContain(
'Dependency scanning detected 3 vulnerabilities', 'Dependency scanning detected 3 vulnerabilities',
); );
// Renders container scanning result
expect(vm.$el.textContent).toContain('Container scanning detected 2 vulnerabilities');
// Renders DAST result
expect(vm.$el.textContent).toContain('DAST detected 2 vulnerabilities');
done(); done();
}, 0); }, 0);
}); });
...@@ -104,6 +124,8 @@ describe('Slipt security reports app', () => { ...@@ -104,6 +124,8 @@ describe('Slipt security reports app', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('sast_head.json').reply(500); mock.onGet('sast_head.json').reply(500);
mock.onGet('dss_head.json').reply(500); mock.onGet('dss_head.json').reply(500);
mock.onGet('dast_head.json').reply(500);
mock.onGet('sast_container_head.json').reply(500);
mock.onGet('vulnerability_feedback_path.json').reply(500, []); mock.onGet('vulnerability_feedback_path.json').reply(500, []);
vm = mountComponentWithStore(Component, { vm = mountComponentWithStore(Component, {
...@@ -113,10 +135,14 @@ describe('Slipt security reports app', () => { ...@@ -113,10 +135,14 @@ describe('Slipt security reports app', () => {
baseBlobPath: 'path', baseBlobPath: 'path',
sastHeadPath: 'sast_head.json', sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json', dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path', sastHelpPath: 'path',
dependencyScanningHelpPath: 'path', dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json', vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path', vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123, pipelineId: 123,
}, },
}); });
...@@ -130,6 +156,10 @@ describe('Slipt security reports app', () => { ...@@ -130,6 +156,10 @@ describe('Slipt security reports app', () => {
expect(removeBreakLine(vm.$el.textContent)).toContain( expect(removeBreakLine(vm.$el.textContent)).toContain(
'Dependency scanning resulted in error while loading results', 'Dependency scanning resulted in error while loading results',
); );
expect(vm.$el.textContent).toContain(
'Container scanning resulted in error while loading results',
);
expect(vm.$el.textContent).toContain('DAST resulted in error while loading results');
done(); done();
}, 0); }, 0);
}); });
......
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