Commit 451beb2f authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch...

Merge branch '13648-move-dast-reports-logic-for-the-merge-request-widget-to-the-backend-frontend-part' into 'master'

Move DAST reports logic for the Merge Request widget to the backend - frontend part

Closes #13648

See merge request gitlab-org/gitlab-ee!15403
parents 86b8a1a5 536c81d0
......@@ -176,6 +176,11 @@ export default {
shouldRenderDependencyScanning() {
const { head, diffEndpoint } = this.dependencyScanning.paths;
return head || diffEndpoint;
},
shouldRenderDast() {
const { head, diffEndpoint } = this.dast.paths;
return head || diffEndpoint;
},
},
......@@ -225,7 +230,12 @@ export default {
this.fetchSastContainerReports();
}
if (this.dastHeadPath) {
const dastDiffEndpoint = gl && gl.mrWidgetData && gl.mrWidgetData.dast_comparison_path;
if (gon.features && gon.features.dastMergeRequestReportApi && dastDiffEndpoint) {
this.setDastDiffEndpoint(dastDiffEndpoint);
this.fetchDastDiff();
} else if (this.dastHeadPath) {
this.setDastHeadPath(this.dastHeadPath);
if (this.dastBasePath) {
......@@ -291,6 +301,8 @@ export default {
'setSastContainerDiffEndpoint',
'fetchDependencyScanningDiff',
'setDependencyScanningDiffEndpoint',
'fetchDastDiff',
'setDastDiffEndpoint',
]),
...mapActions('sast', {
setSastHeadPath: 'setHeadPath',
......@@ -373,7 +385,7 @@ export default {
/>
</template>
<template v-if="dastHeadPath">
<template v-if="shouldRenderDast">
<summary-row
:summary="groupedDastText"
:status-icon="dastStatusIcon"
......
......@@ -93,6 +93,9 @@ export const receiveSastContainerError = ({ commit }, error) =>
export const receiveSastContainerDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_SAST_CONTAINER_DIFF_SUCCESS, response);
export const receiveSastContainerDiffError = ({ commit }) =>
commit(types.RECEIVE_SAST_CONTAINER_DIFF_ERROR);
export const fetchSastContainerDiff = ({ state, dispatch }) => {
dispatch('requestSastContainerReports');
......@@ -111,7 +114,7 @@ export const fetchSastContainerDiff = ({ state, dispatch }) => {
});
})
.catch(() => {
dispatch('receiveSastContainerError');
dispatch('receiveSastContainerDiffError');
});
};
......@@ -151,6 +154,8 @@ export const setDastHeadPath = ({ commit }, path) => commit(types.SET_DAST_HEAD_
export const setDastBasePath = ({ commit }, path) => commit(types.SET_DAST_BASE_PATH, path);
export const setDastDiffEndpoint = ({ commit }, path) => commit(types.SET_DAST_DIFF_ENDPOINT, path);
export const requestDastReports = ({ commit }) => commit(types.REQUEST_DAST_REPORTS);
export const receiveDastReports = ({ commit }, response) =>
......@@ -186,6 +191,33 @@ export const fetchDastReports = ({ state, dispatch }) => {
export const updateDastIssue = ({ commit }, issue) => commit(types.UPDATE_DAST_ISSUE, issue);
export const receiveDastDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_DAST_DIFF_SUCCESS, response);
export const receiveDastDiffError = ({ commit }) => commit(types.RECEIVE_DAST_DIFF_ERROR);
export const fetchDastDiff = ({ state, dispatch }) => {
dispatch('requestDastReports');
return Promise.all([
pollUntilComplete(state.dast.paths.diffEndpoint),
axios.get(state.vulnerabilityFeedbackPath, {
params: {
category: 'dast',
},
}),
])
.then(values => {
dispatch('receiveDastDiffSuccess', {
diff: values[0].data,
enrichData: values[1].data,
});
})
.catch(() => {
dispatch('receiveDastDiffError');
});
};
/**
* DEPENDENCY SCANNING
*/
......@@ -210,6 +242,9 @@ export const receiveDependencyScanningError = ({ commit }, error) =>
export const receiveDependencyScanningDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS, response);
export const receiveDependencyScanningDiffError = ({ commit }) =>
commit(types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR);
export const fetchDependencyScanningDiff = ({ state, dispatch }) => {
dispatch('requestDependencyScanningReports');
......@@ -228,7 +263,7 @@ export const fetchDependencyScanningDiff = ({ state, dispatch }) => {
});
})
.catch(() => {
dispatch('receiveDependencyScanningError');
dispatch('receiveDependencyScanningDiffError');
});
};
......
......@@ -21,13 +21,17 @@ export const REQUEST_SAST_CONTAINER_REPORTS = 'REQUEST_SAST_CONTAINER_REPORTS';
export const RECEIVE_SAST_CONTAINER_REPORTS = 'RECEIVE_SAST_CONTAINER_REPORTS';
export const RECEIVE_SAST_CONTAINER_ERROR = 'RECEIVE_SAST_CONTAINER_ERROR';
export const RECEIVE_SAST_CONTAINER_DIFF_SUCCESS = 'RECEIVE_SAST_CONTAINER_DIFF_SUCCESS';
export const RECEIVE_SAST_CONTAINER_DIFF_ERROR = 'RECEIVE_SAST_CONTAINER_DIFF_ERROR';
// DAST
export const SET_DAST_HEAD_PATH = 'SET_DAST_HEAD_PATH';
export const SET_DAST_DIFF_ENDPOINT = 'SET_DAST_DIFF_ENDPOINT';
export const SET_DAST_BASE_PATH = 'SET_DAST_BASE_PATH';
export const REQUEST_DAST_REPORTS = 'REQUEST_DAST_REPORTS';
export const RECEIVE_DAST_REPORTS = 'RECEIVE_DAST_REPORTS';
export const RECEIVE_DAST_ERROR = 'RECEIVE_DAST_ERROR';
export const RECEIVE_DAST_DIFF_SUCCESS = 'RECEIVE_DAST_DIFF_SUCCESS';
export const RECEIVE_DAST_DIFF_ERROR = 'RECEIVE_DAST_DIFF_ERROR';
// DEPENDENCY_SCANNING
export const SET_DEPENDENCY_SCANNING_HEAD_PATH = 'SET_DEPENDENCY_SCANNING_HEAD_PATH';
......@@ -37,6 +41,7 @@ export const REQUEST_DEPENDENCY_SCANNING_REPORTS = 'REQUEST_DEPENDENCY_SCANNING_
export const RECEIVE_DEPENDENCY_SCANNING_REPORTS = 'RECEIVE_DEPENDENCY_SCANNING_REPORTS';
export const RECEIVE_DEPENDENCY_SCANNING_ERROR = 'RECEIVE_DEPENDENCY_SCANNING_ERROR';
export const RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS = 'RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS';
export const RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR = 'RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR';
// Dismiss security issue
export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
......
......@@ -114,6 +114,11 @@ export default {
Vue.set(state.sastContainer, 'allIssues', existing);
},
[types.RECEIVE_SAST_CONTAINER_DIFF_ERROR](state) {
Vue.set(state.sastContainer, 'isLoading', false);
Vue.set(state.sastContainer, 'hasError', true);
},
[types.RECEIVE_SAST_CONTAINER_ERROR](state) {
Vue.set(state.sastContainer, 'isLoading', false);
Vue.set(state.sastContainer, 'hasError', true);
......@@ -129,6 +134,10 @@ export default {
Vue.set(state.dast.paths, 'base', path);
},
[types.SET_DAST_DIFF_ENDPOINT](state, path) {
Vue.set(state.dast.paths, 'diffEndpoint', path);
},
[types.REQUEST_DAST_REPORTS](state) {
Vue.set(state.dast, 'isLoading', true);
},
......@@ -152,6 +161,20 @@ export default {
}
},
[types.RECEIVE_DAST_DIFF_SUCCESS](state, { diff, enrichData }) {
const { added, fixed, existing } = parseDiff(diff, enrichData);
Vue.set(state.dast, 'isLoading', false);
Vue.set(state.dast, 'newIssues', added);
Vue.set(state.dast, 'resolvedIssues', fixed);
Vue.set(state.dast, 'allIssues', existing);
},
[types.RECEIVE_DAST_DIFF_ERROR](state) {
Vue.set(state.dast, 'isLoading', false);
Vue.set(state.dast, 'hasError', true);
},
[types.RECEIVE_DAST_ERROR](state) {
Vue.set(state.dast, 'isLoading', false);
Vue.set(state.dast, 'hasError', true);
......@@ -234,6 +257,11 @@ export default {
Vue.set(state.dependencyScanning, 'allIssues', existing);
},
[types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR](state) {
Vue.set(state.dependencyScanning, 'isLoading', false);
Vue.set(state.dependencyScanning, 'hasError', true);
},
[types.RECEIVE_DEPENDENCY_SCANNING_ERROR](state) {
Vue.set(state.dependencyScanning, 'isLoading', false);
Vue.set(state.dependencyScanning, 'hasError', true);
......
......@@ -33,6 +33,7 @@ export default () => ({
paths: {
head: null,
base: null,
diffEndpoint: null,
},
isLoading: false,
......
......@@ -841,6 +841,15 @@ describe('security reports mutations', () => {
});
});
describe('RECEIVE_SAST_CONTAINER_DIFF_ERROR', () => {
it('should set sast container loading flag to false and error flag to true', () => {
mutations[types.RECEIVE_SAST_CONTAINER_DIFF_ERROR](stateCopy);
expect(stateCopy.sastContainer.isLoading).toEqual(false);
expect(stateCopy.sastContainer.hasError).toEqual(true);
});
});
describe('SET_DEPENDENCY_SCANNING_DIFF_ENDPOINT', () => {
const endpoint = 'dependency_scannning_diff_endpoint.json';
......@@ -898,4 +907,80 @@ describe('security reports mutations', () => {
});
});
});
describe('RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR', () => {
it('should set dependency scanning loading flag to false and error flag to true', () => {
mutations[types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR](stateCopy);
expect(stateCopy.dependencyScanning.isLoading).toEqual(false);
expect(stateCopy.dependencyScanning.hasError).toEqual(true);
});
});
describe('SET_DAST_DIFF_ENDPOINT', () => {
const endpoint = 'dast_diff_endpoint.json';
beforeEach(() => {
mutations[types.SET_DAST_DIFF_ENDPOINT](stateCopy, endpoint);
});
it('should set the correct endpoint', () => {
expect(stateCopy.dast.paths.diffEndpoint).toEqual(endpoint);
});
});
describe('RECEIVE_DAST_DIFF_SUCCESS', () => {
let reports = {};
beforeEach(() => {
reports = {
diff: {
added: [
{ name: 'added vuln 1', report_type: 'dast' },
{ name: 'added vuln 2', report_type: 'dast' },
],
fixed: [{ name: 'fixed vuln 1', report_type: 'dast' }],
existing: [{ name: 'existing vuln 1', report_type: 'dast' }],
},
};
mutations[types.RECEIVE_DAST_DIFF_SUCCESS](stateCopy, reports);
});
it('should set isLoading to false', () => {
expect(stateCopy.dast.isLoading).toBe(false);
});
it('should parse and set the added vulnerabilities', () => {
reports.diff.added.forEach((vuln, i) => {
expect(stateCopy.dast.newIssues[i]).toEqual(
expect.objectContaining({
name: vuln.name,
title: vuln.name,
category: vuln.report_type,
}),
);
});
});
it('should parse and set the fixed vulnerabilities', () => {
reports.diff.fixed.forEach((vuln, i) => {
expect(stateCopy.dast.resolvedIssues[i]).toEqual(
expect.objectContaining({
name: vuln.name,
title: vuln.name,
category: vuln.report_type,
}),
);
});
});
});
describe('RECEIVE_DAST_DIFF_ERROR', () => {
it('should set dast loading flag to false and error flag to true', () => {
mutations[types.RECEIVE_DAST_DIFF_ERROR](stateCopy);
expect(stateCopy.dast.isLoading).toEqual(false);
expect(stateCopy.dast.hasError).toEqual(true);
});
});
});
......@@ -368,5 +368,51 @@ describe('Grouped security reports app', () => {
);
});
});
describe('dast reports', () => {
const dastEndpoint = 'dast.json';
beforeEach(done => {
gon.features = gon.features || {};
gon.features.dastMergeRequestReportApi = true;
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.dast_comparison_path = dastEndpoint;
mock.onGet(dastEndpoint).reply(200, {
added: [dockerReport.vulnerabilities[0]],
fixed: [dockerReport.vulnerabilities[1], dockerReport.vulnerabilities[2]],
});
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
vm = mountComponent(Component, {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
});
waitForMutation(vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
it('should set setDastDiffEndpoint', () => {
expect(vm.dast.paths.diffEndpoint).toEqual(dastEndpoint);
});
it('should call `fetchDastDiff`', () => {
expect(vm.$el.textContent).toContain('DAST detected 1 new, and 2 fixed vulnerabilities');
});
});
});
});
......@@ -57,10 +57,16 @@ import actions, {
hideDismissalDeleteButtons,
setSastContainerDiffEndpoint,
receiveSastContainerDiffSuccess,
receiveSastContainerDiffError,
fetchSastContainerDiff,
setDependencyScanningDiffEndpoint,
receiveDependencyScanningDiffSuccess,
receiveDependencyScanningDiffError,
fetchDependencyScanningDiff,
setDastDiffEndpoint,
receiveDastDiffSuccess,
receiveDastDiffError,
fetchDastDiff,
} from 'ee/vue_shared/security_reports/store/actions';
import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import state from 'ee/vue_shared/security_reports/store/state';
......@@ -1633,6 +1639,23 @@ describe('security reports actions', () => {
});
});
describe('receiveSastContainerDiffError', () => {
it('should commit container diff error mutation', done => {
testAction(
receiveSastContainerDiffError,
undefined,
mockedState,
[
{
type: types.RECEIVE_SAST_CONTAINER_DIFF_ERROR,
},
],
[],
done,
);
});
});
describe('fetchSastContainerDiff', () => {
const diff = { vulnerabilities: [] };
......@@ -1695,7 +1718,7 @@ describe('security reports actions', () => {
type: 'requestSastContainerReports',
},
{
type: 'receiveSastContainerError',
type: 'receiveSastContainerDiffError',
},
],
done,
......@@ -1724,7 +1747,7 @@ describe('security reports actions', () => {
type: 'requestSastContainerReports',
},
{
type: 'receiveSastContainerError',
type: 'receiveSastContainerDiffError',
},
],
done,
......@@ -1773,6 +1796,23 @@ describe('security reports actions', () => {
});
});
describe('receiveDependencyScanningDiffError', () => {
it('should commit dependency scanning diff error mutation', done => {
testAction(
receiveDependencyScanningDiffError,
undefined,
mockedState,
[
{
type: types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR,
},
],
[],
done,
);
});
});
describe('fetchDependencyScanningDiff', () => {
const diff = { foo: {} };
......@@ -1835,7 +1875,7 @@ describe('security reports actions', () => {
type: 'requestDependencyScanningReports',
},
{
type: 'receiveDependencyScanningError',
type: 'receiveDependencyScanningDiffError',
},
],
done,
......@@ -1864,7 +1904,164 @@ describe('security reports actions', () => {
type: 'requestDependencyScanningReports',
},
{
type: 'receiveDependencyScanningError',
type: 'receiveDependencyScanningDiffError',
},
],
done,
);
});
});
});
describe('setDastDiffEndpoint', () => {
it('should pass down the endpoint to the mutation', done => {
const payload = '/dast_endpoint.json';
testAction(
setDastDiffEndpoint,
payload,
mockedState,
[
{
type: types.SET_DAST_DIFF_ENDPOINT,
payload,
},
],
[],
done,
);
});
});
describe('receiveDastDiffSuccess', () => {
it('should pass down the response to the mutation', done => {
const payload = { data: 'Effort yields its own rewards.' };
testAction(
receiveDastDiffSuccess,
payload,
mockedState,
[
{
type: types.RECEIVE_DAST_DIFF_SUCCESS,
payload,
},
],
[],
done,
);
});
});
describe('receiveDastDiffError', () => {
it('should commit dast diff error mutation', done => {
testAction(
receiveDastDiffError,
undefined,
mockedState,
[
{
type: types.RECEIVE_DAST_DIFF_ERROR,
},
],
[],
done,
);
});
});
describe('fetchDastDiff', () => {
const diff = { foo: {} };
beforeEach(() => {
mockedState.vulnerabilityFeedbackPath = 'vulnerabilities_feedback';
mockedState.dast.paths.diffEndpoint = 'dast_diff.json';
});
describe('on success', () => {
it('should dispatch `receiveDastDiffSuccess`', done => {
mock.onGet('dast_diff.json').reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'dast',
},
})
.reply(200, dastFeedbacks);
testAction(
fetchDastDiff,
null,
mockedState,
[],
[
{
type: 'requestDastReports',
},
{
type: 'receiveDastDiffSuccess',
payload: {
diff,
enrichData: dastFeedbacks,
},
},
],
done,
);
});
});
describe('when vulnerabilities path errors', () => {
it('should dispatch `receiveDastError`', done => {
mock.onGet('dast_diff.json').reply(500);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'dast',
},
})
.reply(200, dastFeedbacks);
testAction(
fetchDastDiff,
null,
mockedState,
[],
[
{
type: 'requestDastReports',
},
{
type: 'receiveDastDiffError',
},
],
done,
);
});
});
describe('when feedback path errors', () => {
it('should dispatch `receiveDastError`', done => {
mock.onGet('dast_diff.json').reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'dast',
},
})
.reply(500);
testAction(
fetchDastDiff,
null,
mockedState,
[],
[
{
type: 'requestDastReports',
},
{
type: 'receiveDastDiffError',
},
],
done,
......
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