Commit d87f2e8f authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'secure-delete-pipelines' into 'master'

Delete old pipelines security report

See merge request gitlab-org/gitlab!22152
parents c10e3d61 5d662ff4
......@@ -2,8 +2,6 @@ import Vue from 'vue';
import { GlEmptyState } from '@gitlab/ui';
import createDashboardStore from 'ee/security_dashboard/store';
import SecurityDashboardApp from 'ee/security_dashboard/components/app.vue';
import SecurityReportApp from 'ee/vue_shared/security_reports/split_security_reports_app.vue';
import createStore from 'ee/vue_shared/security_reports/store';
import { s__ } from '~/locale';
import Translate from '~/vue_shared/translate';
......@@ -53,71 +51,10 @@ const initSecurityDashboardApp = el => {
});
};
const initSplitSecurityReportsApp = el => {
const datasetOptions = el.dataset;
const {
headBlobPath,
sourceBranch,
sastHeadPath,
sastHelpPath,
dependencyScanningHeadPath,
dependencyScanningHelpPath,
vulnerabilityFeedbackPath,
vulnerabilityFeedbackHelpPath,
createVulnerabilityFeedbackIssuePath,
createVulnerabilityFeedbackMergeRequestPath,
createVulnerabilityFeedbackDismissalPath,
dastHeadPath,
sastContainerHeadPath,
dastHelpPath,
sastContainerHelpPath,
} = datasetOptions;
const pipelineId = parseInt(datasetOptions.pipelineId, 10);
const store = createStore();
return new Vue({
el,
store,
components: {
SecurityReportApp,
},
render(createElement) {
return createElement('security-report-app', {
props: {
headBlobPath,
sourceBranch,
sastHeadPath,
sastHelpPath,
dependencyScanningHeadPath,
dependencyScanningHelpPath,
vulnerabilityFeedbackPath,
vulnerabilityFeedbackHelpPath,
createVulnerabilityFeedbackIssuePath,
createVulnerabilityFeedbackMergeRequestPath,
createVulnerabilityFeedbackDismissalPath,
pipelineId,
dastHeadPath,
sastContainerHeadPath,
dastHelpPath,
sastContainerHelpPath,
canCreateIssue: Boolean(createVulnerabilityFeedbackIssuePath),
canCreateMergeRequest: Boolean(createVulnerabilityFeedbackMergeRequestPath),
canDismissVulnerability: Boolean(createVulnerabilityFeedbackDismissalPath),
},
});
},
});
};
export default () => {
const securityTab = document.getElementById('js-security-report-app');
if (securityTab) {
if (gon.features && gon.features.pipelineReportApi) {
initSecurityDashboardApp(securityTab);
} else {
initSplitSecurityReportsApp(securityTab);
}
}
};
<script>
import { mapActions, mapState } from 'vuex';
import { componentNames } from 'ee/reports/components/issue_body';
import { s__, sprintf, n__ } from '~/locale';
import createFlash from '~/flash';
import ReportSection from '~/reports/components/report_section.vue';
import IssueModal from './components/modal.vue';
import mixin from './mixins/security_report_mixin';
import reportsMixin from './mixins/reports_mixin';
import messages from './store/messages';
export default {
components: {
ReportSection,
IssueModal,
},
messages,
mixins: [mixin, reportsMixin],
props: {
alwaysOpen: {
type: Boolean,
required: false,
default: false,
},
headBlobPath: {
type: String,
required: true,
},
sourceBranch: {
type: String,
required: false,
default: null,
},
sastHeadPath: {
type: String,
required: false,
default: null,
},
dastHeadPath: {
type: String,
required: false,
default: null,
},
sastContainerHeadPath: {
type: String,
required: false,
default: null,
},
dependencyScanningHeadPath: {
type: String,
required: false,
default: null,
},
sastHelpPath: {
type: String,
required: false,
default: null,
},
sastContainerHelpPath: {
type: String,
required: false,
default: '',
},
dastHelpPath: {
type: String,
required: false,
default: '',
},
dependencyScanningHelpPath: {
type: String,
required: false,
default: null,
},
vulnerabilityFeedbackPath: {
type: String,
required: false,
default: '',
},
vulnerabilityFeedbackHelpPath: {
type: String,
required: false,
default: '',
},
createVulnerabilityFeedbackIssuePath: {
type: String,
required: false,
default: '',
},
createVulnerabilityFeedbackMergeRequestPath: {
type: String,
required: false,
default: '',
},
createVulnerabilityFeedbackDismissalPath: {
type: String,
required: false,
default: '',
},
pipelineId: {
type: Number,
required: false,
default: null,
},
canCreateIssue: {
type: Boolean,
required: true,
},
canCreateMergeRequest: {
type: Boolean,
required: true,
},
canDismissVulnerability: {
type: Boolean,
required: true,
},
},
componentNames,
computed: {
...mapState([
'sast',
'dependencyScanning',
'sastContainer',
'dast',
'modal',
'canCreateIssuePermission',
'canCreateFeedbackPermission',
]),
sastText() {
return this.summaryTextBuilder(messages.SAST, this.sast.newIssues.length);
},
dependencyScanningText() {
return this.summaryTextBuilder(
messages.DEPENDENCY_SCANNING,
this.dependencyScanning.newIssues.length,
);
},
sastContainerText() {
return this.summaryTextBuilder(
messages.CONTAINER_SCANNING,
this.sastContainer.newIssues.length,
);
},
dastText() {
return this.summaryTextBuilder(messages.DAST, this.dast.newIssues.length);
},
issuesCount() {
return (
this.dast.newIssues.length +
this.dependencyScanning.newIssues.length +
this.sastContainer.newIssues.length +
this.sast.newIssues.length
);
},
},
created() {
// update the store with the received props
this.setHeadBlobPath(this.headBlobPath);
this.setSourceBranch(this.sourceBranch);
this.setVulnerabilityFeedbackPath(this.vulnerabilityFeedbackPath);
this.setVulnerabilityFeedbackHelpPath(this.vulnerabilityFeedbackHelpPath);
this.setCreateVulnerabilityFeedbackIssuePath(this.createVulnerabilityFeedbackIssuePath);
this.setCreateVulnerabilityFeedbackMergeRequestPath(
this.createVulnerabilityFeedbackMergeRequestPath,
);
this.setCreateVulnerabilityFeedbackDismissalPath(this.createVulnerabilityFeedbackDismissalPath);
this.setPipelineId(this.pipelineId);
this.setCanCreateIssuePermission(this.canCreateIssue);
this.setCanCreateFeedbackPermission(this.canCreateFeedback);
if (this.sastHeadPath) {
this.setSastHeadPath(this.sastHeadPath);
this.fetchSastReports().catch(() =>
createFlash(s__('ciReport|There was an error loading SAST report')),
);
}
if (this.dependencyScanningHeadPath) {
this.setDependencyScanningHeadPath(this.dependencyScanningHeadPath);
this.fetchDependencyScanningReports().catch(() =>
createFlash(s__('ciReport|There was an error loading dependency scanning report')),
);
}
if (this.sastContainerHeadPath) {
this.setSastContainerHeadPath(this.sastContainerHeadPath);
this.fetchSastContainerReports().catch(() =>
createFlash(s__('ciReport|There was an error loading container scanning report')),
);
}
if (this.dastHeadPath) {
this.setDastHeadPath(this.dastHeadPath);
this.fetchDastReports().catch(() =>
createFlash(s__('ciReport|There was an error loading DAST report')),
);
}
},
methods: {
...mapActions([
'setHeadBlobPath',
'setSourceBranch',
'setDependencyScanningHeadPath',
'setSastContainerHeadPath',
'setDastHeadPath',
'fetchDependencyScanningReports',
'fetchSastContainerReports',
'fetchDastReports',
'setVulnerabilityFeedbackPath',
'setVulnerabilityFeedbackHelpPath',
'setCreateVulnerabilityFeedbackIssuePath',
'setCreateVulnerabilityFeedbackMergeRequestPath',
'setCreateVulnerabilityFeedbackDismissalPath',
'setPipelineId',
'setCanCreateIssuePermission',
'setCanCreateFeedbackPermission',
'dismissVulnerability',
'revertDismissVulnerability',
'createNewIssue',
'createMergeRequest',
'openDismissalCommentBox',
'closeDismissalCommentBox',
'downloadPatch',
'addDismissalComment',
'deleteDismissalComment',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
]),
...mapActions('sast', {
setSastHeadPath: 'setHeadPath',
fetchSastReports: 'fetchReports',
}),
summaryTextBuilder(reportType, issuesCount = 0) {
if (issuesCount === 0) {
return sprintf(s__('ciReport|%{reportType} detected no vulnerabilities'), {
reportType,
});
}
return sprintf(
n__(
'ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability',
'ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities',
issuesCount,
),
{ reportType, vulnerabilityCount: issuesCount },
);
},
},
};
</script>
<template>
<div>
<report-section
v-if="sastHeadPath"
:always-open="alwaysOpen"
:component="$options.componentNames.SastIssueBody"
:status="checkReportStatus(sast.isLoading, sast.hasError)"
:loading-text="$options.messages.SAST_IS_LOADING"
:error-text="$options.messages.SAST_HAS_ERROR"
:success-text="sastText"
:unresolved-issues="sast.newIssues"
:has-issues="sast.newIssues.length > 0"
:popover-options="sastPopover"
class="js-sast-widget split-report-section"
/>
<report-section
v-if="dependencyScanningHeadPath"
:always-open="alwaysOpen"
:component="$options.componentNames.SastIssueBody"
:status="checkReportStatus(dependencyScanning.isLoading, dependencyScanning.hasError)"
:loading-text="$options.messages.DEPENDENCY_SCANNING_IS_LOADING"
:error-text="$options.messages.DEPENDENCY_SCANNING_HAS_ERROR"
:success-text="dependencyScanningText"
:unresolved-issues="dependencyScanning.newIssues"
:has-issues="dependencyScanning.newIssues.length > 0"
:popover-options="dependencyScanningPopover"
class="js-dss-widget split-report-section"
data-qa-selector="dependency_scanning_report"
/>
<report-section
v-if="sastContainerHeadPath"
:always-open="alwaysOpen"
:component="$options.componentNames.SastContainerIssueBody"
:status="checkReportStatus(sastContainer.isLoading, sastContainer.hasError)"
:loading-text="$options.messages.CONTAINER_SCANNING_IS_LOADING"
:error-text="$options.messages.CONTAINER_SCANNING_HAS_ERROR"
:success-text="sastContainerText"
:unresolved-issues="sastContainer.newIssues"
:has-issues="sastContainer.newIssues.length > 0"
:popover-options="sastContainerPopover"
class="js-dependency-scanning-widget split-report-section"
data-qa-selector="container_scanning_report"
/>
<report-section
v-if="dastHeadPath"
:always-open="alwaysOpen"
:component="$options.componentNames.DastIssueBody"
:status="checkReportStatus(dast.isLoading, dast.hasError)"
:loading-text="$options.messages.DAST_IS_LOADING"
:error-text="$options.messages.DAST_HAS_ERROR"
:success-text="dastText"
:unresolved-issues="dast.newIssues"
:has-issues="dast.newIssues.length > 0"
:popover-options="dastPopover"
class="js-dast-widget split-report-section"
/>
<issue-modal
:modal="modal"
:vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath"
:can-create-issue="canCreateIssue"
:can-create-merge-request="canCreateMergeRequest"
:can-dismiss-vulnerability="canDismissVulnerability"
@closeDismissalCommentBox="closeDismissalCommentBox()"
@createNewIssue="createNewIssue"
@createMergeRequest="createMergeRequest"
@dismissVulnerability="dismissVulnerability"
@openDismissalCommentBox="openDismissalCommentBox()"
@revertDismissVulnerability="revertDismissVulnerability"
@downloadPatch="downloadPatch"
@addDismissalComment="addDismissalComment({ comment: $event })"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@deleteDismissalComment="deleteDismissalComment"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
/>
</div>
</template>
......@@ -12,7 +12,6 @@ module EE
# because the user can freely navigate between them *without*
# triggering a page load.
before_action only: [:show, :builds, :failures, :security, :licenses] do
push_frontend_feature_flag(:pipeline_report_api, default_enabled: true)
push_frontend_feature_flag(:parsed_license_report, default_enabled: true)
end
end
......
- pipeline = local_assigns.fetch(:pipeline)
- project = local_assigns.fetch(:project)
- sast_endpoint = pipeline.downloadable_path_for_report_type(:sast)
- dependency_scanning_endpoint = pipeline.downloadable_path_for_report_type(:dependency_scanning)
- dast_endpoint = pipeline.downloadable_path_for_report_type(:dast)
- sast_container_endpoint = pipeline.downloadable_path_for_report_type(:container_scanning)
- blob_path = project_blob_path(project, pipeline.sha)
- license_management_settings_path = can?(current_user, :admin_software_license_policy, project) ? license_management_settings_path(project) : nil
- licenses_api_path = licenses_project_pipeline_path(project, pipeline) if project.feature_available?(:license_management)
- vulnerabilities_endpoint_path = expose_path(api_v4_projects_vulnerability_findings_path(id: project.id, params: { pipeline_id: pipeline.id, scope: 'dismissed' }))
- if pipeline.expose_security_dashboard?
#js-tab-security.build-security.tab-pane
- if Feature.enabled?(:pipeline_report_api, default_enabled: true)
#js-security-report-app{ data: { dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'),
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
pipeline_id: pipeline.id,
project_id: project.id,
vulnerabilities_endpoint: vulnerabilities_endpoint_path,
vulnerability_feedback_help_path: help_page_path('user/application_security/index') } }
- else
#js-security-report-app{ data: { head_blob_path: blob_path,
sast_head_path: sast_endpoint,
dependency_scanning_head_path: dependency_scanning_endpoint,
dast_head_path: dast_endpoint,
sast_container_head_path: sast_container_endpoint,
pipeline_id: pipeline.id,
source_branch: pipeline.ref,
vulnerability_feedback_path: project_vulnerability_feedback_index_path(project),
vulnerability_feedback_help_path: help_page_path('user/application_security/index'),
sast_help_path: help_page_path('user/application_security/sast/index'),
dependency_scanning_help_path: help_page_path('user/application_security/dependency_scanning/index'),
dast_help_path: help_page_path('user/application_security/dast/index'),
sast_container_help_path: help_page_path('user/application_security/container_scanning/index'),
create_vulnerability_feedback_issue_path: create_vulnerability_feedback_issue_path(project),
create_vulnerability_feedback_merge_request_path: create_vulnerability_feedback_merge_request_path(project),
create_vulnerability_feedback_dismissal_path: create_vulnerability_feedback_dismissal_path(project) } }
- if pipeline.expose_license_scanning_data?
#js-tab-licenses.tab-pane
......
---
title: Remove old pipeline security report view in favor of the Security Dashboard
merge_request: 22152
author:
type: removed
......@@ -101,10 +101,6 @@ describe 'Pipeline', :js do
context 'with a sast artifact' do
before do
create(:ee_ci_build, :sast, pipeline: pipeline)
end
context 'when feature flag is enabled' do
before do
visit security_project_pipeline_path(project, pipeline)
end
......@@ -118,23 +114,6 @@ describe 'Pipeline', :js do
end
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(pipeline_report_api: false)
visit security_project_pipeline_path(project, pipeline)
end
it 'shows jobs tab pane as active' do
expect(page).to have_content('Security')
expect(page).to have_css('#js-tab-security')
end
it 'shows security report section' do
expect(page).to have_content('SAST is loading')
end
end
end
context 'without sast artifact' do
before do
visit security_project_pipeline_path(project, pipeline)
......
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import component from 'ee/vue_shared/security_reports/split_security_reports_app.vue';
import createStore from 'ee/vue_shared/security_reports/store';
import state from 'ee/vue_shared/security_reports/store/state';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import axios from '~/lib/utils/axios_utils';
import { sastIssues, dast, dockerReport } from './mock_data';
describe('Split security reports app', () => {
const Component = Vue.extend(component);
let vm;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
vm.$store.replaceState(state());
vm.$destroy();
mock.restore();
});
describe('while loading', () => {
beforeEach(() => {
mock.onGet('sast_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, []);
vm = mountComponentWithStore(Component, {
store: createStore(),
props: {
headBlobPath: 'path',
sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
},
});
});
it('renders loading summary text + spinner', done => {
expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(vm.$el.textContent).toContain('SAST 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(() => {
done();
}, 0);
});
});
describe('with all reports', () => {
beforeEach(() => {
mock.onGet('sast_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, []);
vm = mountComponentWithStore(Component, {
store: createStore(),
props: {
headBlobPath: 'path',
sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
},
});
});
it('renders reports', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.textContent).toContain('SAST detected 3 vulnerabilities');
expect(vm.$el.textContent).toContain('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');
expect(vm.$el.textContent).not.toContain('for the source branch only');
done();
}, 0);
});
it('renders all reports collapsed by default', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
const reports = vm.$el.querySelectorAll('.js-report-section-container');
reports.forEach(report => {
expect(report).toHaveCss({ display: 'none' });
});
done();
}, 0);
});
it('renders all reports expanded with the option always-open', done => {
vm.alwaysOpen = true;
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-collapse-btn')).toBeNull();
const reports = vm.$el.querySelectorAll('.js-report-section-container');
reports.forEach(report => {
expect(report).not.toHaveCss({ display: 'none' });
});
done();
}, 0);
});
});
describe('with error', () => {
beforeEach(() => {
mock.onGet('sast_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, []);
vm = mountComponentWithStore(Component, {
store: createStore(),
props: {
headBlobPath: 'path',
sastHeadPath: 'sast_head.json',
dependencyScanningHeadPath: 'dss_head.json',
dastHeadPath: 'dast_head.json',
sastContainerHeadPath: 'sast_container_head.json',
sastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
dastHelpPath: 'path',
sastContainerHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
},
});
});
it('renders error state', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.textContent).toContain('SAST: Loading resulted in an error');
expect(vm.$el.textContent).toContain('Dependency scanning: Loading resulted in an error');
expect(vm.$el.textContent).toContain('Container scanning: Loading resulted in an error');
expect(vm.$el.textContent).toContain('DAST: Loading resulted in an error');
done();
}, 0);
});
});
});
......@@ -21322,14 +21322,6 @@ msgstr ""
msgid "ciReport|%{reportType} %{status} detected no vulnerabilities for the source branch only"
msgstr ""
msgid "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerability"
msgid_plural "ciReport|%{reportType} detected %{vulnerabilityCount} vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|%{reportType} detected no vulnerabilities"
msgstr ""
msgid "ciReport|%{reportType} is loading"
msgstr ""
......@@ -21489,18 +21481,6 @@ msgstr ""
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
msgstr ""
msgid "ciReport|There was an error loading DAST report"
msgstr ""
msgid "ciReport|There was an error loading SAST report"
msgstr ""
msgid "ciReport|There was an error loading container scanning report"
msgstr ""
msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
......
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