Commit a4877e03 authored by Mark Florian's avatar Mark Florian

Merge branch '119040-pipeline-mr-autoremediation-fix' into 'master'

Fixes: Auto-remediation on the pipeline security dashboard

See merge request gitlab-org/gitlab!23677
parents b324c957 3a7b83f7
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 { s__ } from '~/locale';
import Translate from '~/vue_shared/translate';
Vue.use(Translate);
import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline_security_dashboard.vue';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
const initSecurityDashboardApp = el => {
const {
......@@ -13,38 +9,26 @@ const initSecurityDashboardApp = el => {
emptyStateSvgPath,
pipelineId,
projectId,
sourceBranch,
vulnerabilitiesEndpoint,
vulnerabilityFeedbackHelpPath,
} = el.dataset;
return new Vue({
el,
store: createDashboardStore(),
store: createDashboardStore({
dashboardType: DASHBOARD_TYPES.PIPELINE,
}),
render(createElement) {
return createElement(SecurityDashboardApp, {
return createElement(PipelineSecurityDashboard, {
props: {
lockToProject: {
id: parseInt(projectId, 10),
},
projectId: parseInt(projectId, 10),
pipelineId: parseInt(pipelineId, 10),
vulnerabilitiesEndpoint,
vulnerabilityFeedbackHelpPath,
},
scopedSlots: {
emptyState: () =>
createElement(GlEmptyState, {
props: {
title: s__(`No vulnerabilities found for this pipeline`),
svgPath: emptyStateSvgPath,
description: s__(
`While it's rare to have no vulnerabilities for your pipeline, it can happen. In any event, we ask that you double check your settings to make sure all security scanning jobs have passed successfully.`,
),
primaryButtonLink: dashboardDocumentation,
primaryButtonText: s__(
'Security Reports|Learn more about setting up your dashboard',
),
},
}),
sourceBranch,
dashboardDocumentation,
emptyStateSvgPath,
},
});
},
......
<script>
import { mapActions } from 'vuex';
import { GlEmptyState } from '@gitlab/ui';
import SecurityDashboard from './app.vue';
export default {
name: 'PipelineSecurityDashboard',
components: {
GlEmptyState,
SecurityDashboard,
},
props: {
dashboardDocumentation: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
pipelineId: {
type: Number,
required: true,
},
projectId: {
type: Number,
required: true,
},
sourceBranch: {
type: String,
required: true,
},
vulnerabilitiesEndpoint: {
type: String,
required: true,
},
vulnerabilityFeedbackHelpPath: {
type: String,
required: true,
},
},
created() {
this.setSourceBranch(this.sourceBranch);
},
methods: {
...mapActions('vulnerabilities', ['setSourceBranch']),
},
};
</script>
<template>
<security-dashboard
:vulnerabilities-endpoint="vulnerabilitiesEndpoint"
:vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath"
:lock-to-project="{ id: projectId }"
:pipeline-id="pipelineId"
>
<template #emptyState>
<gl-empty-state
:title="s__('No vulnerabilities found for this pipeline')"
:svg-path="emptyStateSvgPath"
:description="
s__(
`While it's rare to have no vulnerabilities for your pipeline, it can happen. In any event, we ask that you double check your settings to make sure all security scanning jobs have passed successfully.`,
)
"
:primary-button-link="dashboardDocumentation"
:primary-button-text="s__('Security Reports|Learn more about setting up your dashboard')"
/>
</template>
</security-dashboard>
</template>
......@@ -20,6 +20,8 @@ const hideModal = () => $('#modal-mrwidget-security-issue').modal('hide');
export const setPipelineId = ({ commit }, id) => commit(types.SET_PIPELINE_ID, id);
export const setSourceBranch = ({ commit }, ref) => commit(types.SET_SOURCE_BRANCH, ref);
export const setVulnerabilitiesEndpoint = ({ commit }, endpoint) => {
commit(types.SET_VULNERABILITIES_ENDPOINT, endpoint);
};
......@@ -375,13 +377,17 @@ export const downloadPatch = ({ state }) => {
$('#modal-mrwidget-security-issue').modal('hide');
};
export const createMergeRequest = ({ dispatch }, { vulnerability, flashError }) => {
export const createMergeRequest = ({ state, dispatch }, { vulnerability, flashError }) => {
const {
report_type,
project_fingerprint,
create_vulnerability_feedback_merge_request_path,
} = vulnerability;
// The target branch for the MR is the source branch of the pipeline.
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23677#note_278221556
const targetBranch = state.sourceBranch;
dispatch('requestCreateMergeRequest');
axios
......@@ -392,6 +398,7 @@ export const createMergeRequest = ({ dispatch }, { vulnerability, flashError })
project_fingerprint,
vulnerability_data: {
...vulnerability,
target_branch: targetBranch,
category: report_type,
},
},
......
export const SET_PIPELINE_ID = 'SET_PIPELINE_ID';
export const SET_SOURCE_BRANCH = 'SET_SOURCE_BRANCH';
export const SET_VULNERABILITIES_ENDPOINT = 'SET_VULNERABILITIES_ENDPOINT';
export const SET_VULNERABILITIES_PAGE = 'SET_VULNERABILITIES_PAGE';
export const REQUEST_VULNERABILITIES = 'REQUEST_VULNERABILITIES';
......
......@@ -10,6 +10,9 @@ export default {
[types.SET_PIPELINE_ID](state, payload) {
state.pipelineId = payload;
},
[types.SET_SOURCE_BRANCH](state, payload) {
state.sourceBranch = payload;
},
[types.SET_VULNERABILITIES_ENDPOINT](state, payload) {
state.vulnerabilitiesEndpoint = payload;
},
......
......@@ -18,6 +18,7 @@ export default () => ({
vulnerabilitiesHistoryEndpoint: null,
vulnerabilitiesEndpoint: null,
activeVulnerability: null,
sourceBranch: null,
modal: {
data: {
description: { text: s__('Vulnerability|Description') },
......
......@@ -11,6 +11,7 @@
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
pipeline_id: pipeline.id,
project_id: project.id,
source_branch: pipeline.source_ref,
vulnerabilities_endpoint: vulnerabilities_endpoint_path,
vulnerability_feedback_help_path: help_page_path('user/application_security/index') } }
......
---
title: Fixes a bug that prevented auto-remediation on the pipeline security dashboard
merge_request: 23677
author:
type: fixed
import Vuex from 'vuex';
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline_security_dashboard.vue';
import SecurityDashboard from 'ee/security_dashboard/components/app.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
const dashboardDocumentation = '/help/docs';
const emptyStateSvgPath = '/svgs/empty/svg';
const pipelineId = 1234;
const projectId = 5678;
const sourceBranch = 'feature-branch-1';
const vulnerabilitiesEndpoint = '/vulnerabilities';
const vulnerabilityFeedbackHelpPath = '/vulnerabilities_feedback_help';
describe('Pipeline Security Dashboard component', () => {
let store;
let wrapper;
const factory = options => {
store = new Vuex.Store({
modules: {
vulnerabilities: {
namespaced: true,
actions: {
setSourceBranch() {},
},
},
},
});
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = shallowMount(PipelineSecurityDashboard, {
localVue,
store,
propsData: {
dashboardDocumentation,
emptyStateSvgPath,
pipelineId,
projectId,
sourceBranch,
vulnerabilitiesEndpoint,
vulnerabilityFeedbackHelpPath,
},
...options,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('on creation', () => {
beforeEach(() => {
factory();
});
it('dispatches the expected actions', () => {
expect(store.dispatch.mock.calls).toEqual([
['vulnerabilities/setSourceBranch', sourceBranch],
]);
});
it('renders the security dashboard', () => {
const dashboard = wrapper.find(SecurityDashboard);
expect(dashboard.exists()).toBe(true);
expect(dashboard.props()).toMatchObject({
lockToProject: { id: projectId },
pipelineId,
vulnerabilitiesEndpoint,
vulnerabilityFeedbackHelpPath,
});
});
});
describe('with a stubbed dashboard for slot testing', () => {
beforeEach(() => {
factory({
stubs: {
'security-dashboard': { template: '<div><slot name="emptyState"></slot></div>' },
},
});
});
it('renders empty state component with correct props', () => {
const emptyState = wrapper.find(GlEmptyState);
expect(emptyState.attributes('title')).toBe('No vulnerabilities found for this pipeline');
});
});
});
......@@ -13,6 +13,8 @@ import mockDataVulnerabilities from './data/mock_data_vulnerabilities.json';
import mockDataVulnerabilitiesCount from './data/mock_data_vulnerabilities_count.json';
import mockDataVulnerabilitiesHistory from './data/mock_data_vulnerabilities_history.json';
const sourceBranch = 'feature-branch-1';
describe('vulnerabilities count actions', () => {
const data = mockDataVulnerabilitiesCount;
const params = { filters: { type: ['sast'] } };
......@@ -43,6 +45,24 @@ describe('vulnerabilities count actions', () => {
});
});
describe('setSourceBranch', () => {
it('should commit the correct mutation', done => {
testAction(
actions.setSourceBranch,
sourceBranch,
state,
[
{
type: types.SET_SOURCE_BRANCH,
payload: sourceBranch,
},
],
[],
done,
);
});
});
describe('setVulnerabilitiesCountEndpoint', () => {
it('should commit the correct mutuation', done => {
const endpoint = 'fakepath.json';
......
......@@ -21,6 +21,16 @@ describe('vulnerabilities module mutations', () => {
});
});
describe('SET_SOURCE_BRANCH', () => {
const sourceBranch = 'feature-branch-1';
it(`should set the sourceBranch to ${sourceBranch}`, () => {
mutations[types.SET_SOURCE_BRANCH](state, sourceBranch);
expect(state.sourceBranch).toBe(sourceBranch);
});
});
describe('SET_VULNERABILITIES_ENDPOINT', () => {
it('should set `vulnerabilitiesEndpoint` to `fakepath.json`', () => {
const endpoint = 'fakepath.json';
......
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