Commit 2cbdd7ab authored by David O'Regan's avatar David O'Regan

Merge branch...

Merge branch '273423-implement-vulnerability-counts-in-security-mr-widget-for-non-ultimate-users-secret-detection-vuex' into 'master'

Extract and move Secret Detection Vuex module to CE

See merge request gitlab-org/gitlab!47307
parents 5b591e25 b9493ba0
export const FEEDBACK_TYPE_DISMISSAL = 'dismissal';
export const FEEDBACK_TYPE_ISSUE = 'issue';
export const FEEDBACK_TYPE_MERGE_REQUEST = 'merge_request';
import { fetchDiffData } from '../../utils';
import * as types from './mutation_types';
export const setDiffEndpoint = ({ commit }, path) => commit(types.SET_DIFF_ENDPOINT, path);
export const requestDiff = ({ commit }) => commit(types.REQUEST_DIFF);
export const receiveDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_DIFF_SUCCESS, response);
export const receiveDiffError = ({ commit }, response) =>
commit(types.RECEIVE_DIFF_ERROR, response);
export const fetchDiff = ({ state, rootState, dispatch }) => {
dispatch('requestDiff');
return fetchDiffData(rootState, state.paths.diffEndpoint, 'secret_detection')
.then(data => {
dispatch('receiveDiffSuccess', data);
})
.catch(() => {
dispatch('receiveDiffError');
});
};
import state from './state';
import mutations from './mutations';
import * as actions from './actions';
export default {
namespaced: true,
state,
mutations,
actions,
};
export const RECEIVE_DIFF_SUCCESS = 'RECEIVE_DIFF_SUCCESS';
export const RECEIVE_DIFF_ERROR = 'RECEIVE_DIFF_ERROR';
export const REQUEST_DIFF = 'REQUEST_DIFF';
export const SET_DIFF_ENDPOINT = 'SET_DIFF_ENDPOINT';
import { parseDiff } from '~/vue_shared/security_reports/store/utils';
import * as types from './mutation_types';
export default {
[types.SET_DIFF_ENDPOINT](state, path) {
state.paths.diffEndpoint = path;
},
[types.REQUEST_DIFF](state) {
state.isLoading = true;
},
[types.RECEIVE_DIFF_SUCCESS](state, { diff, enrichData }) {
const { added, fixed, existing } = parseDiff(diff, enrichData);
const baseReportOutofDate = diff.base_report_out_of_date || false;
const hasBaseReport = Boolean(diff.base_report_created_at);
state.isLoading = false;
state.newIssues = added;
state.resolvedIssues = fixed;
state.allIssues = existing;
state.baseReportOutofDate = baseReportOutofDate;
state.hasBaseReport = hasBaseReport;
},
[types.RECEIVE_DIFF_ERROR](state) {
state.isLoading = false;
state.hasError = true;
},
};
export default () => ({
paths: {
head: null,
base: null,
diffEndpoint: null,
},
isLoading: false,
hasError: false,
newIssues: [],
resolvedIssues: [],
allIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
});
import pollUntilComplete from '~/lib/utils/poll_until_complete';
import axios from '~/lib/utils/axios_utils';
import {
FEEDBACK_TYPE_DISMISSAL,
FEEDBACK_TYPE_ISSUE,
FEEDBACK_TYPE_MERGE_REQUEST,
} from '../constants';
export const fetchDiffData = (state, endpoint, category) => {
const requests = [pollUntilComplete(endpoint)];
......@@ -24,21 +29,21 @@ export const enrichVulnerabilityWithFeedback = (vulnerability, feedback = []) =>
feedback
.filter(fb => fb.project_fingerprint === vulnerability.project_fingerprint)
.reduce((vuln, fb) => {
if (fb.feedback_type === 'dismissal') {
if (fb.feedback_type === FEEDBACK_TYPE_DISMISSAL) {
return {
...vuln,
isDismissed: true,
dismissalFeedback: fb,
};
}
if (fb.feedback_type === 'issue' && fb.issue_iid) {
if (fb.feedback_type === FEEDBACK_TYPE_ISSUE && fb.issue_iid) {
return {
...vuln,
hasIssue: true,
issue_feedback: fb,
};
}
if (fb.feedback_type === 'merge_request' && fb.merge_request_iid) {
if (fb.feedback_type === FEEDBACK_TYPE_MERGE_REQUEST && fb.merge_request_iid) {
return {
...vuln,
hasMergeRequest: true,
......
......@@ -6,6 +6,11 @@ import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { s__, n__, sprintf } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import toast from '~/vue_shared/plugins/global_toast';
import {
FEEDBACK_TYPE_DISMISSAL,
FEEDBACK_TYPE_ISSUE,
FEEDBACK_TYPE_MERGE_REQUEST,
} from '~/vue_shared/security_reports/constants';
import * as types from './mutation_types';
/**
......@@ -83,7 +88,7 @@ export const createIssue = ({ dispatch }, { vulnerability, flashError }) => {
axios
.post(vulnerability.create_vulnerability_feedback_issue_path, {
vulnerability_feedback: {
feedback_type: 'issue',
feedback_type: FEEDBACK_TYPE_ISSUE,
category: vulnerability.report_type,
project_fingerprint: vulnerability.project_fingerprint,
vulnerability_data: {
......@@ -147,7 +152,7 @@ export const dismissSelectedVulnerabilities = ({ dispatch, state }, { comment }
vulnerability_feedback: {
category: vulnerability.report_type,
comment,
feedback_type: 'dismissal',
feedback_type: FEEDBACK_TYPE_DISMISSAL,
project_fingerprint: vulnerability.project_fingerprint,
vulnerability_data: {
id: vulnerability.id,
......@@ -232,7 +237,7 @@ export const dismissVulnerability = (
vulnerability_feedback: {
category: vulnerability.report_type,
comment,
feedback_type: 'dismissal',
feedback_type: FEEDBACK_TYPE_DISMISSAL,
pipeline_id: state.pipelineId,
project_fingerprint: vulnerability.project_fingerprint,
vulnerability_data: {
......@@ -434,7 +439,7 @@ export const createMergeRequest = ({ state, dispatch }, { vulnerability, flashEr
axios
.post(create_vulnerability_feedback_merge_request_path, {
vulnerability_feedback: {
feedback_type: 'merge_request',
feedback_type: FEEDBACK_TYPE_MERGE_REQUEST,
category: report_type,
project_fingerprint,
vulnerability_data: {
......
......@@ -191,7 +191,7 @@ export default {
'dast',
'coverageFuzzing',
'dependencyScanning',
'secretScanning',
'secretDetection',
'summaryCounts',
'modal',
'isCreatingIssue',
......@@ -204,12 +204,10 @@ export default {
'groupedContainerScanningText',
'groupedDastText',
'groupedDependencyText',
'groupedSecretScanningText',
'groupedCoverageFuzzingText',
'containerScanningStatusIcon',
'dastStatusIcon',
'dependencyScanningStatusIcon',
'secretScanningStatusIcon',
'coverageFuzzingStatusIcon',
'isBaseSecurityReportOutOfDate',
'canCreateIssue',
......@@ -217,6 +215,7 @@ export default {
'canDismissVulnerability',
]),
...mapGetters('sast', ['groupedSastText', 'sastStatusIcon']),
...mapGetters('secretDetection', ['groupedSecretDetectionText', 'secretDetectionStatusIcon']),
...mapGetters('pipelineJobs', ['hasFuzzingArtifacts', 'fuzzingJobsWithArtifact']),
securityTab() {
return `${this.pipelinePath}/security`;
......@@ -237,7 +236,7 @@ export default {
hasSastReports() {
return this.enabledReports.sast;
},
hasSecretScanningReports() {
hasSecretDetectionReports() {
return this.enabledReports.secretDetection;
},
isMRActive() {
......@@ -258,8 +257,23 @@ export default {
dastDownloadLink() {
return this.dastSummary?.scannedResourcesCsvPath || '';
},
coverageFuzzingShowIssues() {
return this.coverageFuzzing.newIssues || this.coverageFuzzing.resolvedIssues;
hasCoverageFuzzingIssues() {
return this.hasIssuesForReportType('coverageFuzzing');
},
hasSastIssues() {
return this.hasIssuesForReportType('sast');
},
hasDependencyScanningIssues() {
return this.hasIssuesForReportType('dependencyScanning');
},
hasContainerScanningIssues() {
return this.hasIssuesForReportType('containerScanning');
},
hasDastIssues() {
return this.hasIssuesForReportType('dast');
},
hasSecretDetectionIssues() {
return this.hasIssuesForReportType('secretDetection');
},
},
......@@ -308,10 +322,10 @@ export default {
this.fetchDependencyScanningDiff();
}
const secretScanningDiffEndpoint = gl?.mrWidgetData?.secret_scanning_comparison_path;
if (secretScanningDiffEndpoint && this.hasSecretScanningReports) {
this.setSecretScanningDiffEndpoint(secretScanningDiffEndpoint);
this.fetchSecretScanningDiff();
const secretDetectionDiffEndpoint = gl?.mrWidgetData?.secret_scanning_comparison_path;
if (secretDetectionDiffEndpoint && this.hasSecretDetectionReports) {
this.setSecretDetectionDiffEndpoint(secretDetectionDiffEndpoint);
this.fetchSecretDetectionDiff();
}
const coverageFuzzingDiffEndpoint = gl?.mrWidgetData?.coverage_fuzzing_comparison_path;
......@@ -352,8 +366,6 @@ export default {
'setDependencyScanningDiffEndpoint',
'fetchDastDiff',
'setDastDiffEndpoint',
'fetchSecretScanningDiff',
'setSecretScanningDiffEndpoint',
'fetchCoverageFuzzingDiff',
'setCoverageFuzzingDiffEndpoint',
]),
......@@ -361,10 +373,17 @@ export default {
setSastDiffEndpoint: 'setDiffEndpoint',
fetchSastDiff: 'fetchDiff',
}),
...mapActions('secretDetection', {
setSecretDetectionDiffEndpoint: 'setDiffEndpoint',
fetchSecretDetectionDiff: 'fetchDiff',
}),
...mapActions('pipelineJobs', ['fetchPipelineJobs', 'setPipelineJobsPath', 'setProjectId']),
...mapActions('pipelineJobs', {
setPipelineJobsId: 'setPipelineId',
}),
hasIssuesForReportType(reportType) {
return Boolean(this[reportType]?.newIssues.length || this[reportType]?.resolvedIssues.length);
},
},
summarySlots: ['success', 'error', 'loading'],
};
......@@ -443,11 +462,10 @@ export default {
</summary-row>
<grouped-issues-list
v-if="sast.newIssues.length || sast.resolvedIssues.length"
v-if="hasSastIssues"
:unresolved-issues="sast.newIssues"
:resolved-issues="sast.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="sast-issues-list"
/>
</template>
......@@ -465,11 +483,10 @@ export default {
</summary-row>
<grouped-issues-list
v-if="dependencyScanning.newIssues.length || dependencyScanning.resolvedIssues.length"
v-if="hasDependencyScanningIssues"
:unresolved-issues="dependencyScanning.newIssues"
:resolved-issues="dependencyScanning.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="dependency-scanning-issues-list"
/>
</template>
......@@ -487,11 +504,10 @@ export default {
</summary-row>
<grouped-issues-list
v-if="containerScanning.newIssues.length || containerScanning.resolvedIssues.length"
v-if="hasContainerScanningIssues"
:unresolved-issues="containerScanning.newIssues"
:resolved-issues="containerScanning.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="container-scanning-issues-list"
/>
</template>
......@@ -522,33 +538,31 @@ export default {
</template>
</summary-row>
<grouped-issues-list
v-if="dast.newIssues.length || dast.resolvedIssues.length"
v-if="hasDastIssues"
:unresolved-issues="dast.newIssues"
:resolved-issues="dast.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="dast-issues-list"
/>
</template>
<template v-if="hasSecretScanningReports">
<template v-if="hasSecretDetectionReports">
<summary-row
:status-icon="secretScanningStatusIcon"
:status-icon="secretDetectionStatusIcon"
:popover-options="secretScanningPopover"
class="js-secret-scanning"
data-qa-selector="secret_scan_report"
>
<template #summary>
<security-summary :message="groupedSecretScanningText" />
<security-summary :message="groupedSecretDetectionText" />
</template>
</summary-row>
<grouped-issues-list
v-if="secretScanning.newIssues.length || secretScanning.resolvedIssues.length"
:unresolved-issues="secretScanning.newIssues"
:resolved-issues="secretScanning.resolvedIssues"
v-if="hasSecretDetectionIssues"
:unresolved-issues="secretDetection.newIssues"
:resolved-issues="secretDetection.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="secret-scanning-issues-list"
/>
</template>
......@@ -571,11 +585,10 @@ export default {
</summary-row>
<grouped-issues-list
v-if="coverageFuzzingShowIssues"
v-if="hasCoverageFuzzingIssues"
:unresolved-issues="coverageFuzzing.newIssues"
:resolved-issues="coverageFuzzing.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="coverage-fuzzing-issues-list"
/>
</template>
......
......@@ -6,6 +6,11 @@ import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import toast from '~/vue_shared/plugins/global_toast';
import { fetchDiffData } from '~/vue_shared/security_reports/store/utils';
import {
FEEDBACK_TYPE_DISMISSAL,
FEEDBACK_TYPE_ISSUE,
FEEDBACK_TYPE_MERGE_REQUEST,
} from '~/vue_shared/security_reports/constants';
import * as types from './mutation_types';
/**
......@@ -173,36 +178,6 @@ export const fetchCoverageFuzzingDiff = ({ state, dispatch }) => {
export const updateCoverageFuzzingIssue = ({ commit }, issue) =>
commit(types.UPDATE_COVERAGE_FUZZING_ISSUE, issue);
/**
* SECRET SCANNING
*/
export const setSecretScanningDiffEndpoint = ({ commit }, path) =>
commit(types.SET_SECRET_SCANNING_DIFF_ENDPOINT, path);
export const requestSecretScanningDiff = ({ commit }) => commit(types.REQUEST_SECRET_SCANNING_DIFF);
export const receiveSecretScanningDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS, response);
export const receiveSecretScanningDiffError = ({ commit }) =>
commit(types.RECEIVE_SECRET_SCANNING_DIFF_ERROR);
export const fetchSecretScanningDiff = ({ state, dispatch }) => {
dispatch('requestSecretScanningDiff');
return fetchDiffData(state, state.secretScanning.paths.diffEndpoint, 'secret_detection')
.then(data => {
dispatch('receiveSecretScanningDiffSuccess', data);
})
.catch(() => {
dispatch('receiveSecretScanningDiffError');
});
};
export const updateSecretScanningIssue = ({ commit }, issue) =>
commit(types.UPDATE_SECRET_SCANNING_ISSUE, issue);
export const openModal = ({ dispatch }, payload) => {
dispatch('setModalData', payload);
......@@ -229,7 +204,7 @@ export const dismissVulnerability = ({ state, dispatch }, comment) => {
vulnerability_feedback: {
category: state.modal.vulnerability.category,
comment,
feedback_type: 'dismissal',
feedback_type: FEEDBACK_TYPE_DISMISSAL,
pipeline_id: state.pipelineId,
project_fingerprint: state.modal.vulnerability.project_fingerprint,
vulnerability_data: state.modal.vulnerability,
......@@ -392,7 +367,7 @@ export const createNewIssue = ({ state, dispatch }) => {
axios
.post(state.createVulnerabilityFeedbackIssuePath, {
vulnerability_feedback: {
feedback_type: 'issue',
feedback_type: FEEDBACK_TYPE_ISSUE,
category: state.modal.vulnerability.category,
project_fingerprint: state.modal.vulnerability.project_fingerprint,
pipeline_id: state.pipelineId,
......@@ -423,7 +398,7 @@ export const createMergeRequest = ({ state, dispatch }) => {
axios
.post(state.createVulnerabilityFeedbackMergeRequestPath, {
vulnerability_feedback: {
feedback_type: 'merge_request',
feedback_type: FEEDBACK_TYPE_MERGE_REQUEST,
category,
project_fingerprint,
vulnerability_data: vulnerability,
......
......@@ -11,14 +11,6 @@ export const groupedContainerScanningText = ({ containerScanning }) =>
messages.CONTAINER_SCANNING_IS_LOADING,
);
export const groupedSecretScanningText = ({ secretScanning }) =>
groupedReportText(
secretScanning,
messages.SECRET_SCANNING,
messages.SECRET_SCANNING_HAS_ERROR,
messages.SECRET_SCANNING_IS_LOADING,
);
export const groupedDastText = ({ dast }) =>
groupedReportText(dast, messages.DAST, messages.DAST_HAS_ERROR, messages.DAST_IS_LOADING);
......@@ -43,7 +35,7 @@ export const summaryCounts = ({
dast,
dependencyScanning,
sast,
secretScanning,
secretDetection,
coverageFuzzing,
} = {}) => {
const allNewVulns = [
......@@ -51,7 +43,7 @@ export const summaryCounts = ({
...dast.newIssues,
...dependencyScanning.newIssues,
...sast.newIssues,
...secretScanning.newIssues,
...secretDetection.newIssues,
...coverageFuzzing.newIssues,
];
......@@ -114,9 +106,6 @@ export const dependencyScanningStatusIcon = ({ dependencyScanning }) =>
dependencyScanning.newIssues.length,
);
export const secretScanningStatusIcon = ({ secretScanning }) =>
statusIcon(secretScanning.isLoading, secretScanning.hasError, secretScanning.newIssues.length);
export const coverageFuzzingStatusIcon = ({ coverageFuzzing }) =>
statusIcon(coverageFuzzing.isLoading, coverageFuzzing.hasError, coverageFuzzing.newIssues.length);
......@@ -125,7 +114,7 @@ export const areReportsLoading = state =>
state.dast.isLoading ||
state.containerScanning.isLoading ||
state.dependencyScanning.isLoading ||
state.secretScanning.isLoading ||
state.secretDetection.isLoading ||
state.coverageFuzzing.isLoading;
export const areAllReportsLoading = state =>
......@@ -133,7 +122,7 @@ export const areAllReportsLoading = state =>
state.dast.isLoading &&
state.containerScanning.isLoading &&
state.dependencyScanning.isLoading &&
state.secretScanning.isLoading &&
state.secretDetection.isLoading &&
state.coverageFuzzing.isLoading;
export const allReportsHaveError = state =>
......@@ -141,7 +130,7 @@ export const allReportsHaveError = state =>
state.dast.hasError &&
state.containerScanning.hasError &&
state.dependencyScanning.hasError &&
state.secretScanning.hasError &&
state.secretDetection.hasError &&
state.coverageFuzzing.hasError;
export const anyReportHasError = state =>
......@@ -149,7 +138,7 @@ export const anyReportHasError = state =>
state.dast.hasError ||
state.containerScanning.hasError ||
state.dependencyScanning.hasError ||
state.secretScanning.hasError ||
state.secretDetection.hasError ||
state.coverageFuzzing.hasError;
export const noBaseInAllReports = state =>
......@@ -157,7 +146,7 @@ export const noBaseInAllReports = state =>
!state.dast.hasBaseReport &&
!state.containerScanning.hasBaseReport &&
!state.dependencyScanning.hasBaseReport &&
!state.secretScanning.hasBaseReport &&
!state.secretDetection.hasBaseReport &&
!state.coverageFuzzing.hasBaseReport;
export const anyReportHasIssues = state =>
......@@ -165,7 +154,7 @@ export const anyReportHasIssues = state =>
state.dast.newIssues.length > 0 ||
state.containerScanning.newIssues.length > 0 ||
state.dependencyScanning.newIssues.length > 0 ||
state.secretScanning.newIssues.length > 0 ||
state.secretDetection.newIssues.length > 0 ||
state.coverageFuzzing.newIssues.length > 0;
export const isBaseSecurityReportOutOfDate = state =>
......@@ -173,7 +162,7 @@ export const isBaseSecurityReportOutOfDate = state =>
state.dast.baseReportOutofDate ||
state.containerScanning.baseReportOutofDate ||
state.dependencyScanning.baseReportOutofDate ||
state.secretScanning.baseReportOutofDate ||
state.secretDetection.baseReportOutofDate ||
state.coverageFuzzing.baseReportOutofDate;
export const canCreateIssue = state => Boolean(state.createVulnerabilityFeedbackIssuePath);
......
......@@ -8,6 +8,7 @@ import mutations from './mutations';
import state from './state';
import sast from './modules/sast';
import secretDetection from './modules/secret_detection';
Vue.use(Vuex);
......@@ -15,6 +16,7 @@ export default () =>
new Vuex.Store({
modules: {
sast,
secretDetection,
pipelineJobs,
},
actions,
......
import * as types from './mutation_types';
const updateIssueActionsMap = {
export const updateIssueActionsMap = {
sast: 'sast/updateVulnerability',
dependency_scanning: 'updateDependencyScanningIssue',
container_scanning: 'updateContainerScanningIssue',
dast: 'updateDastIssue',
secret_scanning: 'updateSecretScanningIssue',
secret_detection: 'secretDetection/updateVulnerability',
coverage_fuzzing: 'updateCoverageFuzzingIssue',
};
......
import messages from '../../messages';
export const { SAST, SAST_HAS_ERROR, SAST_IS_LOADING } = messages;
import { statusIcon, groupedReportText } from '../../utils';
import { SAST, SAST_HAS_ERROR, SAST_IS_LOADING } from './constants';
import messages from '../../messages';
export const groupedSastText = state =>
groupedReportText(state, SAST, SAST_HAS_ERROR, SAST_IS_LOADING);
groupedReportText(state, messages.SAST, messages.SAST_HAS_ERROR, messages.SAST_IS_LOADING);
export const sastStatusIcon = ({ isLoading, hasError, newIssues }) =>
statusIcon(isLoading, hasError, newIssues.length);
import * as types from './mutation_types';
export * from '~/vue_shared/security_reports/store/modules/secret_detection/actions';
export const updateVulnerability = ({ commit }, vulnerability) =>
commit(types.UPDATE_VULNERABILITY, vulnerability);
import { statusIcon, groupedReportText } from '../../utils';
import messages from '../../messages';
export const groupedSecretDetectionText = state =>
groupedReportText(
state,
messages.SECRET_SCANNING,
messages.SECRET_SCANNING_HAS_ERROR,
messages.SECRET_SCANNING_IS_LOADING,
);
export const secretDetectionStatusIcon = ({ isLoading, hasError, newIssues }) =>
statusIcon(isLoading, hasError, newIssues.length);
import state from './state';
import mutations from './mutations';
import * as getters from './getters';
import * as actions from './actions';
export default {
namespaced: true,
state,
mutations,
getters,
actions,
};
export * from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
export const UPDATE_VULNERABILITY = 'UPDATE_VULNERABILITY';
import ceMutations from '~/vue_shared/security_reports/store/modules/secret_detection/mutations';
import { findIssueIndex } from '../../utils';
import * as types from './mutation_types';
export default {
...ceMutations,
[types.UPDATE_VULNERABILITY](state, issue) {
const newIssuesIndex = findIssueIndex(state.newIssues, issue);
if (newIssuesIndex !== -1) {
state.newIssues.splice(newIssuesIndex, 1, issue);
return;
}
const resolvedIssuesIndex = findIssueIndex(state.resolvedIssues, issue);
if (resolvedIssuesIndex !== -1) {
state.resolvedIssues.splice(resolvedIssuesIndex, 1, issue);
return;
}
const allIssuesIndex = findIssueIndex(state.allIssues, issue);
if (allIssuesIndex !== -1) {
state.allIssues.splice(allIssuesIndex, 1, issue);
}
},
};
export { default } from '~/vue_shared/security_reports/store/modules/secret_detection/state';
......@@ -30,12 +30,6 @@ export const REQUEST_DEPENDENCY_SCANNING_DIFF = 'REQUEST_DEPENDENCY_SCANNING_DIF
export const RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS = 'RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS';
export const RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR = 'RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR';
// SECRET SCANNING
export const SET_SECRET_SCANNING_DIFF_ENDPOINT = 'SET_SECRET_SCANNING_DIFF_ENDPOINT';
export const REQUEST_SECRET_SCANNING_DIFF = 'REQUEST_SECRET_SCANNING_DIFF';
export const RECEIVE_SECRET_SCANNING_DIFF_SUCCESS = 'RECEIVE_SECRET_SCANNING_DIFF_SUCCESS';
export const RECEIVE_SECRET_SCANNING_DIFF_ERROR = 'RECEIVE_SECRET_SCANNING_DIFF_ERROR';
// COVERAGE FUZZING
export const SET_COVERAGE_FUZZING_DIFF_ENDPOINT = 'SET_COVERAGE_FUZZING_DIFF_ENDPOINT';
export const REQUEST_COVERAGE_FUZZING_DIFF = 'REQUEST_COVERAGE_FUZZING_DIFF';
......@@ -69,7 +63,6 @@ export const HIDE_DISMISSAL_DELETE_BUTTONS = 'HIDE_DISMISSAL_DELETE_BUTTONS';
export const UPDATE_DEPENDENCY_SCANNING_ISSUE = 'UPDATE_DEPENDENCY_SCANNING_ISSUE';
export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE';
export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE';
export const UPDATE_SECRET_SCANNING_ISSUE = 'UPDATE_SECRET_SCANNING_ISSUE';
export const UPDATE_COVERAGE_FUZZING_ISSUE = 'UPDATE_COVERAGE_FUZZING_ISSUE';
export const OPEN_DISMISSAL_COMMENT_BOX = 'OPEN_DISMISSAL_COMMENT_BOX ';
......
......@@ -160,33 +160,6 @@ export default {
Vue.set(state.dependencyScanning, 'hasError', true);
},
// SECRET SCANNING
[types.SET_SECRET_SCANNING_DIFF_ENDPOINT](state, path) {
Vue.set(state.secretScanning.paths, 'diffEndpoint', path);
},
[types.REQUEST_SECRET_SCANNING_DIFF](state) {
Vue.set(state.secretScanning, 'isLoading', true);
},
[types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS](state, { diff, enrichData }) {
const { added, fixed, existing } = parseDiff(diff, enrichData);
const baseReportOutofDate = diff.base_report_out_of_date || false;
const hasBaseReport = Boolean(diff.base_report_created_at);
Vue.set(state.secretScanning, 'isLoading', false);
Vue.set(state.secretScanning, 'newIssues', added);
Vue.set(state.secretScanning, 'resolvedIssues', fixed);
Vue.set(state.secretScanning, 'allIssues', existing);
Vue.set(state.secretScanning, 'baseReportOutofDate', baseReportOutofDate);
Vue.set(state.secretScanning, 'hasBaseReport', hasBaseReport);
},
[types.RECEIVE_SECRET_SCANNING_DIFF_ERROR](state) {
Vue.set(state.secretScanning, 'isLoading', false);
Vue.set(state.secretScanning, 'hasError', true);
},
[types.SET_ISSUE_MODAL_DATA](state, payload) {
const { issue, status } = payload;
......@@ -298,26 +271,6 @@ export default {
}
},
[types.UPDATE_SECRET_SCANNING_ISSUE](state, issue) {
// Find issue in the correct list and update it
const newIssuesIndex = findIssueIndex(state.secretScanning.newIssues, issue);
if (newIssuesIndex !== -1) {
state.secretScanning.newIssues.splice(newIssuesIndex, 1, issue);
return;
}
const resolvedIssuesIndex = findIssueIndex(state.secretScanning.resolvedIssues, issue);
if (resolvedIssuesIndex !== -1) {
state.secretScanning.resolvedIssues.splice(resolvedIssuesIndex, 1, issue);
}
const allIssuesIndex = findIssueIndex(state.secretScanning.allIssues, issue);
if (allIssuesIndex !== -1) {
state.secretScanning.allIssues.splice(allIssuesIndex, 1, issue);
}
},
[types.REQUEST_CREATE_ISSUE](state) {
state.isCreatingIssue = true;
// reset error in case previous state was error
......
......@@ -76,22 +76,6 @@ export default () => ({
baseReportOutofDate: false,
hasBaseReport: false,
},
secretScanning: {
paths: {
head: null,
base: null,
diffEndpoint: null,
},
isLoading: false,
hasError: false,
newIssues: [],
resolvedIssues: [],
allIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
},
modal: {
title: null,
......
import { s__ } from '~/locale';
import {
FEEDBACK_TYPE_ISSUE,
FEEDBACK_TYPE_MERGE_REQUEST,
} from '~/vue_shared/security_reports/constants';
export const VULNERABILITY_STATE_OBJECTS = {
detected: {
......@@ -49,8 +53,8 @@ export const HEADER_ACTION_BUTTONS = {
};
export const FEEDBACK_TYPES = {
ISSUE: 'issue',
MERGE_REQUEST: 'merge_request',
ISSUE: FEEDBACK_TYPE_ISSUE,
MERGE_REQUEST: FEEDBACK_TYPE_MERGE_REQUEST,
};
export const RELATED_ISSUES_ERRORS = {
......
---
title: Fix real-time update of dismissal status of vulnerabilities found by Secret Detection
merge_request: 47307
author:
type: fixed
......@@ -10,7 +10,7 @@ export default {
dast: false,
dependency_scanning: false,
license_management: false,
secret_scanning: false,
secret_detection: false,
},
};
......
......@@ -4,6 +4,7 @@ import GroupedSecurityReportsApp from 'ee/vue_shared/security_reports/grouped_se
import appStore from 'ee/vue_shared/security_reports/store';
import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import * as sastTypes from 'ee/vue_shared/security_reports/store/modules/sast/mutation_types';
import * as secretDetectionTypes from 'ee/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import { mount } from '@vue/test-utils';
import { waitForMutation } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
......@@ -29,7 +30,7 @@ const DEPENDENCY_SCANNING_DIFF_ENDPOINT = 'dependency_scanning.json';
const DAST_DIFF_ENDPOINT = 'dast.json';
const SAST_DIFF_ENDPOINT = 'sast.json';
const PIPELINE_JOBS_ENDPOINT = 'jobs.json';
const SECRET_SCANNING_DIFF_ENDPOINT = 'secret_detection.json';
const SECRET_DETECTION_DIFF_ENDPOINT = 'secret_detection.json';
const COVERAGE_FUZZING_DIFF_ENDPOINT = 'coverage_fuzzing.json';
describe('Grouped security reports app', () => {
......@@ -117,7 +118,7 @@ describe('Grouped security reports app', () => {
gl.mrWidgetData.dependency_scanning_comparison_path = DEPENDENCY_SCANNING_DIFF_ENDPOINT;
gl.mrWidgetData.dast_comparison_path = DAST_DIFF_ENDPOINT;
gl.mrWidgetData.sast_comparison_path = SAST_DIFF_ENDPOINT;
gl.mrWidgetData.secret_scanning_comparison_path = SECRET_SCANNING_DIFF_ENDPOINT;
gl.mrWidgetData.secret_scanning_comparison_path = SECRET_DETECTION_DIFF_ENDPOINT;
gl.mrWidgetData.coverage_fuzzing_comparison_path = COVERAGE_FUZZING_DIFF_ENDPOINT;
});
......@@ -127,7 +128,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(500);
mock.onGet(DAST_DIFF_ENDPOINT).reply(500);
mock.onGet(SAST_DIFF_ENDPOINT).reply(500);
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(500);
mock.onGet(SECRET_DETECTION_DIFF_ENDPOINT).reply(500);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(500);
createWrapper(allReportProps);
......@@ -137,7 +138,10 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_CONTAINER_SCANNING_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_ERROR),
waitForMutation(
wrapper.vm.$store,
`secretDetection/${secretDetectionTypes.RECEIVE_DIFF_ERROR}`,
),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR),
]);
});
......@@ -179,7 +183,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(200, {});
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SECRET_DETECTION_DIFF_ENDPOINT).reply(200, {});
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, {});
createWrapper(allReportProps);
......@@ -212,7 +216,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(SECRET_DETECTION_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, emptyResponse);
createWrapper(allReportProps);
......@@ -222,7 +226,10 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_CONTAINER_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS),
waitForMutation(
wrapper.vm.$store,
`secretDetection/${secretDetectionTypes.RECEIVE_DIFF_SUCCESS}`,
),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS),
]);
});
......@@ -262,7 +269,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(200, dependencyScanningDiffSuccessMock);
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, dastDiffSuccessMock);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, sastDiffSuccessMock);
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock);
mock.onGet(SECRET_DETECTION_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
createWrapper(allReportProps);
......@@ -272,7 +279,10 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_CONTAINER_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS),
waitForMutation(
wrapper.vm.$store,
`secretDetection/${secretDetectionTypes.RECEIVE_DIFF_SUCCESS}`,
),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS),
]);
});
......@@ -553,9 +563,9 @@ describe('Grouped security reports app', () => {
describe('secret scanning reports', () => {
const initSecretScan = (isEnabled = true) => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.secret_scanning_comparison_path = SECRET_SCANNING_DIFF_ENDPOINT;
gl.mrWidgetData.secret_scanning_comparison_path = SECRET_DETECTION_DIFF_ENDPOINT;
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock);
mock.onGet(SECRET_DETECTION_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock);
createWrapper({
...props,
......@@ -564,7 +574,10 @@ describe('Grouped security reports app', () => {
},
});
return waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS);
return waitForMutation(
wrapper.vm.$store,
`secretDetection/${secretDetectionTypes.RECEIVE_DIFF_SUCCESS}`,
);
};
describe('enabled', () => {
......@@ -576,8 +589,10 @@ describe('Grouped security reports app', () => {
expect(wrapper.find('[data-qa-selector="secret_scan_report"]').exists()).toBe(true);
});
it('should set setSecretScanningDiffEndpoint', () => {
expect(wrapper.vm.secretScanning.paths.diffEndpoint).toEqual(SECRET_SCANNING_DIFF_ENDPOINT);
it('should set diffEndpoint', () => {
expect(wrapper.vm.secretDetection.paths.diffEndpoint).toEqual(
SECRET_DETECTION_DIFF_ENDPOINT,
);
});
it('should display the correct numbers of vulnerabilities', () => {
......
......@@ -277,31 +277,6 @@ export const containerScanningFeedbacks = [
},
];
export const secretScanningFeedbacks = [
{
id: 3,
project_id: 17,
author_id: 1,
issue_iid: null,
pipeline_id: 132,
category: 'secret_detection',
feedback_type: 'dismissal',
branch: 'try_new_secret_scanning',
project_fingerprint: libTiffCveFingerprint2,
},
{
id: 4,
project_id: 17,
author_id: 1,
issue_iid: 123,
pipeline_id: 132,
category: 'secret_detection',
feedback_type: 'issue',
branch: 'try_new_secret_scanning',
project_fingerprint: libTiffCveFingerprint2,
},
];
export const coverageFuzzingFeedbacks = [
{
id: 3,
......
......@@ -29,7 +29,6 @@ import {
updateDependencyScanningIssue,
updateContainerScanningIssue,
updateDastIssue,
updateSecretScanningIssue,
updateCoverageFuzzingIssue,
addDismissalComment,
receiveAddDismissalCommentError,
......@@ -53,10 +52,6 @@ import {
receiveDastDiffSuccess,
receiveDastDiffError,
fetchDastDiff,
setSecretScanningDiffEndpoint,
receiveSecretScanningDiffSuccess,
receiveSecretScanningDiffError,
fetchSecretScanningDiff,
setCoverageFuzzingDiffEndpoint,
receiveCoverageFuzzingDiffSuccess,
receiveCoverageFuzzingDiffError,
......@@ -70,7 +65,6 @@ import {
dastFeedbacks,
containerScanningFeedbacks,
dependencyScanningFeedbacks,
secretScanningFeedbacks,
coverageFuzzingFeedbacks,
} from '../mock_data';
import toasted from '~/vue_shared/plugins/global_toast';
......@@ -1087,26 +1081,6 @@ describe('security reports actions', () => {
});
});
describe('updateSecretScanningIssue', () => {
it('commits update secret scanning issue', done => {
const payload = { foo: 'bar' };
testAction(
updateSecretScanningIssue,
payload,
mockedState,
[
{
type: types.UPDATE_SECRET_SCANNING_ISSUE,
payload,
},
],
[],
done,
);
});
});
describe('updateDastIssue', () => {
it('commits update dast issue', done => {
const payload = { foo: 'bar' };
......@@ -1709,194 +1683,6 @@ describe('security reports actions', () => {
});
});
describe('setSecretScanningDiffEndpoint', () => {
it('should pass down the endpoint to the mutation', done => {
const payload = '/secret_scanning_endpoint.json';
testAction(
setSecretScanningDiffEndpoint,
payload,
mockedState,
[
{
type: types.SET_SECRET_SCANNING_DIFF_ENDPOINT,
payload,
},
],
[],
done,
);
});
});
describe('receiveSecretScanningDiffSuccess', () => {
it('should pass down the response to the mutation', done => {
const payload = { data: 'Effort yields its own rewards.' };
testAction(
receiveSecretScanningDiffSuccess,
payload,
mockedState,
[
{
type: types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS,
payload,
},
],
[],
done,
);
});
});
describe('receiveSecretScanningDiffError', () => {
it('should commit secret diff error mutation', done => {
testAction(
receiveSecretScanningDiffError,
undefined,
mockedState,
[
{
type: types.RECEIVE_SECRET_SCANNING_DIFF_ERROR,
},
],
[],
done,
);
});
});
describe('fetchSecretScanningDiff', () => {
const diff = { vulnerabilities: [] };
const endpoint = 'secret_scanning_diff.json';
beforeEach(() => {
mockedState.vulnerabilityFeedbackPath = 'vulnerabilities_feedback';
mockedState.canReadVulnerabilityFeedback = true;
mockedState.secretScanning.paths.diffEndpoint = endpoint;
});
describe('on success', () => {
it('should dispatch `receiveSecretScanningDiffSuccess`', done => {
mock.onGet(endpoint).reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'secret_detection',
},
})
.reply(200, secretScanningFeedbacks);
testAction(
fetchSecretScanningDiff,
null,
mockedState,
[],
[
{
type: 'requestSecretScanningDiff',
},
{
type: 'receiveSecretScanningDiffSuccess',
payload: {
diff,
enrichData: secretScanningFeedbacks,
},
},
],
done,
);
});
});
describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
beforeEach(() => {
mockedState.canReadVulnerabilityFeedback = false;
mock.onGet(endpoint).reply(200, diff);
});
it('should dispatch `secret_scanning`', done => {
testAction(
fetchSecretScanningDiff,
null,
mockedState,
[],
[
{
type: 'requestSecretScanningDiff',
},
{
type: 'receiveSecretScanningDiffSuccess',
payload: {
diff,
enrichData: [],
},
},
],
done,
);
});
});
describe('when vulnerabilities path errors', () => {
it('should dispatch `receiveSecretScanningError`', done => {
mock.onGet(endpoint).reply(500);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'secret_scanning',
},
})
.reply(200, secretScanningFeedbacks);
testAction(
fetchSecretScanningDiff,
null,
mockedState,
[],
[
{
type: 'requestSecretScanningDiff',
},
{
type: 'receiveSecretScanningDiffError',
},
],
done,
);
});
});
describe('when feedback path errors', () => {
it('should dispatch `receiveSecretScanningError`', done => {
mock.onGet(endpoint).reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'secret_scanning',
},
})
.reply(500);
testAction(
fetchSecretScanningDiff,
null,
mockedState,
[],
[
{
type: 'requestSecretScanningDiff',
},
{
type: 'receiveSecretScanningDiffError',
},
],
done,
);
});
});
});
describe('setCoverageFuzzingDiffEndpoint', () => {
it('should pass down the endpoint to the mutation', done => {
const payload = '/coverage_fuzzing_endpoint.json';
......
import createState from 'ee/vue_shared/security_reports/store/state';
import createSastState from 'ee/vue_shared/security_reports/store/modules/sast/state';
import createSecretScanningState from 'ee/vue_shared/security_reports/store/modules/secret_detection/state';
import { groupedTextBuilder } from 'ee/vue_shared/security_reports/store/utils';
import {
groupedContainerScanningText,
groupedDastText,
groupedDependencyText,
groupedSecretScanningText,
groupedCoverageFuzzingText,
groupedSummaryText,
allReportsHaveError,
......@@ -38,11 +38,11 @@ describe('Security reports getters', () => {
beforeEach(() => {
state = createState();
state.sast = createSastState();
state.secretDetection = createSecretScanningState();
});
describe.each`
name | scanner | getter
${'Secret scanning'} | ${'secretScanning'} | ${groupedSecretScanningText}
${'Dependency scanning'} | ${'dependencyScanning'} | ${groupedDependencyText}
${'Container scanning'} | ${'containerScanning'} | ${groupedContainerScanningText}
${'DAST'} | ${'dast'} | ${groupedDastText}
......@@ -220,7 +220,7 @@ describe('Security reports getters', () => {
state.dast.hasError = true;
state.containerScanning.hasError = true;
state.dependencyScanning.hasError = true;
state.secretScanning.hasError = true;
state.secretDetection.hasError = true;
state.coverageFuzzing.hasError = true;
expect(allReportsHaveError(state)).toEqual(true);
......@@ -234,7 +234,7 @@ describe('Security reports getters', () => {
state.dast.hasError = false;
state.containerScanning.hasError = true;
state.dependencyScanning.hasError = true;
state.secretScanning.hasError = true;
state.secretDetection.hasError = true;
expect(allReportsHaveError(state)).toEqual(false);
});
......
import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import configureMediator from 'ee/vue_shared/security_reports/store/mediator';
import configureMediator, {
updateIssueActionsMap,
} from 'ee/vue_shared/security_reports/store/mediator';
const mockedStore = {
dispatch: jest.fn(),
......@@ -17,18 +19,14 @@ describe('security reports mediator', () => {
describe(types.RECEIVE_DISMISS_VULNERABILITY_SUCCESS, () => {
const type = types.RECEIVE_DISMISS_VULNERABILITY_SUCCESS;
it.each`
action | category
${'sast/updateVulnerability'} | ${'sast'}
${'updateDastIssue'} | ${'dast'}
${'updateDependencyScanningIssue'} | ${'dependency_scanning'}
${'updateContainerScanningIssue'} | ${'container_scanning'}
`(`should trigger $action on when a $category is updated`, data => {
const { action, category } = data;
const payload = { category };
mockedStore.commit({ type, payload });
it.each(Object.entries(updateIssueActionsMap).map(entry => entry.reverse()))(
`should trigger %s on when a %s is updated`,
(action, category) => {
const payload = { category };
mockedStore.commit({ type, payload });
expect(mockedStore.dispatch).toHaveBeenCalledWith(action, payload);
});
expect(mockedStore.dispatch).toHaveBeenCalledWith(action, payload);
},
);
});
});
import {
SAST_HAS_ERROR,
SAST_IS_LOADING,
} from 'ee/vue_shared/security_reports/store/modules/sast/constants';
import messages from 'ee/vue_shared/security_reports/store/messages';
import * as getters from 'ee/vue_shared/security_reports/store/modules/sast/getters';
const createReport = (config = {}) => ({
......@@ -15,14 +12,14 @@ describe('groupedSastText', () => {
const sast = createReport({ hasError: true });
const result = getters.groupedSastText(sast);
expect(result).toStrictEqual({ message: SAST_HAS_ERROR });
expect(result).toStrictEqual({ message: messages.SAST_HAS_ERROR });
});
it("should return the loading message if it's still loading", () => {
const sast = createReport({ isLoading: true });
const result = getters.groupedSastText(sast);
expect(result).toStrictEqual({ message: SAST_IS_LOADING });
expect(result).toStrictEqual({ message: messages.SAST_IS_LOADING });
});
it('should call groupedTextBuilder if everything is fine', () => {
......
import testAction from 'helpers/vuex_action_helper';
import createState from 'ee/vue_shared/security_reports/store/modules/secret_detection/state';
import * as types from 'ee/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import * as actions from 'ee/vue_shared/security_reports/store/modules/secret_detection/actions';
const issue = {};
let state;
// See also the corresponding CE specs in
// spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
describe('EE secret detection report actions', () => {
beforeEach(() => {
state = createState();
});
describe('updateVulnerability', () => {
it(`should commit ${types.UPDATE_VULNERABILITY} with the correct response`, done => {
testAction(
actions.updateVulnerability,
issue,
state,
[
{
type: types.UPDATE_VULNERABILITY,
payload: issue,
},
],
[],
done,
);
});
});
});
import messages from 'ee/vue_shared/security_reports/store/messages';
import * as getters from 'ee/vue_shared/security_reports/store/modules/secret_detection/getters';
const createReport = (config = {}) => ({
paths: [],
newIssues: [],
...config,
});
describe('groupedSecretDetectionText', () => {
it("should return the error message if there's an error", () => {
const report = createReport({ hasError: true });
const result = getters.groupedSecretDetectionText(report);
expect(result).toStrictEqual({ message: messages.SECRET_SCANNING_HAS_ERROR });
});
it("should return the loading message if it's still loading", () => {
const report = createReport({ isLoading: true });
const result = getters.groupedSecretDetectionText(report);
expect(result).toStrictEqual({ message: messages.SECRET_SCANNING_IS_LOADING });
});
it('should call groupedTextBuilder if everything is fine', () => {
const report = createReport();
const result = getters.groupedSecretDetectionText(report);
expect(result).toStrictEqual({
countMessage: '',
critical: 0,
high: 0,
message: 'Secret scanning detected %{totalStart}no%{totalEnd} vulnerabilities.',
other: 0,
status: '',
total: 0,
});
});
});
describe('secretDetectionStatusIcon', () => {
it("should return `loading` when we're still loading", () => {
const report = createReport({ isLoading: true });
const result = getters.secretDetectionStatusIcon(report);
expect(result).toBe('loading');
});
it("should return `warning` when there's an issue", () => {
const report = createReport({ hasError: true });
const result = getters.secretDetectionStatusIcon(report);
expect(result).toBe('warning');
});
it('should return `success` when nothing is wrong', () => {
const report = createReport();
const result = getters.secretDetectionStatusIcon(report);
expect(result).toBe('success');
});
});
import * as types from 'ee/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import createState from 'ee/vue_shared/security_reports/store/modules/secret_detection/state';
import mutations from 'ee/vue_shared/security_reports/store/modules/secret_detection/mutations';
const createIssue = ({ ...config }) => ({ changed: false, ...config });
// See also the corresponding CE specs in
// spec/frontend/vue_shared/security_reports/store/modules/secret_detection/mutations_spec.js
describe('EE secret detection module mutations', () => {
let state;
beforeEach(() => {
state = createState();
});
describe(types.UPDATE_VULNERABILITY, () => {
let newIssue;
let resolvedIssue;
let allIssue;
beforeEach(() => {
newIssue = createIssue({ project_fingerprint: 'new' });
resolvedIssue = createIssue({ project_fingerprint: 'resolved' });
allIssue = createIssue({ project_fingerprint: 'all' });
state.newIssues.push(newIssue);
state.resolvedIssues.push(resolvedIssue);
state.allIssues.push(allIssue);
});
describe('with a `new` issue', () => {
beforeEach(() => {
mutations[types.UPDATE_VULNERABILITY](state, { ...newIssue, changed: true });
});
it('should update the correct issue', () => {
expect(state.newIssues[0].changed).toBe(true);
});
});
describe('with a `resolved` issue', () => {
beforeEach(() => {
mutations[types.UPDATE_VULNERABILITY](state, { ...resolvedIssue, changed: true });
});
it('should update the correct issue', () => {
expect(state.resolvedIssues[0].changed).toBe(true);
});
});
describe('with an `all` issue', () => {
beforeEach(() => {
mutations[types.UPDATE_VULNERABILITY](state, { ...allIssue, changed: true });
});
it('should update the correct issue', () => {
expect(state.allIssues[0].changed).toBe(true);
});
});
describe('with an invalid issue', () => {
beforeEach(() => {
mutations[types.UPDATE_VULNERABILITY](
state,
createIssue({ project_fingerprint: 'invalid', changed: true }),
);
});
it('should ignore the issue', () => {
expect(state.newIssues[0].changed).toBe(false);
expect(state.resolvedIssues[0].changed).toBe(false);
expect(state.allIssues[0].changed).toBe(false);
});
});
});
});
......@@ -89,14 +89,6 @@ describe('EE sast reports mutations', () => {
});
});
describe('REQUEST_SECRET_SCANNING_DIFF', () => {
it('should set secret scanning loading flag to true', () => {
mutations[types.REQUEST_SECRET_SCANNING_DIFF](stateCopy);
expect(stateCopy.secretScanning.isLoading).toEqual(true);
});
});
describe('SET_ISSUE_MODAL_DATA', () => {
it('has default data', () => {
expect(stateCopy.modal.vulnerability.isDismissed).toEqual(false);
......@@ -486,34 +478,6 @@ describe('EE sast reports mutations', () => {
});
});
describe('UPDATE_SECRET_SCANNING_ISSUE', () => {
it('updates issue in the new issues list', () => {
stateCopy.secretScanning.newIssues = mockFindings;
stateCopy.secretScanning.resolvedIssues = [];
const updatedIssue = {
...mockFindings[0],
foo: 'bar',
};
mutations[types.UPDATE_SECRET_SCANNING_ISSUE](stateCopy, updatedIssue);
expect(stateCopy.secretScanning.newIssues[0]).toEqual(updatedIssue);
});
it('updates issue in the resolved issues list', () => {
stateCopy.secretScanning.newIssues = [];
stateCopy.secretScanning.resolvedIssues = mockFindings;
const updatedIssue = {
...mockFindings[0],
foo: 'bar',
};
mutations[types.UPDATE_SECRET_SCANNING_ISSUE](stateCopy, updatedIssue);
expect(stateCopy.secretScanning.resolvedIssues[0]).toEqual(updatedIssue);
});
});
describe('SET_CONTAINER_SCANNING_DIFF_ENDPOINT', () => {
const endpoint = 'container_scanning_diff_endpoint.json';
......@@ -743,72 +707,6 @@ describe('EE sast reports mutations', () => {
});
});
describe('SET_SECRET_SCANNING_DIFF_ENDPOINT', () => {
const endpoint = 'secret_scanning_diff_endpoint.json';
beforeEach(() => {
mutations[types.SET_SECRET_SCANNING_DIFF_ENDPOINT](stateCopy, endpoint);
});
it('should set the correct endpoint', () => {
expect(stateCopy.secretScanning.paths.diffEndpoint).toEqual(endpoint);
});
});
describe('RECEIVE_SECRET_SCANNING_DIFF_SUCCESS', () => {
const reports = {
diff: {
added: [
{ name: 'added vuln 1', report_type: 'secret_scanning' },
{ name: 'added vuln 2', report_type: 'secret_scanning' },
],
fixed: [{ name: 'fixed vuln 1', report_type: 'secret_scanning' }],
base_report_out_of_date: true,
},
};
beforeEach(() => {
mutations[types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS](stateCopy, reports);
});
it('should set isLoading to false', () => {
expect(stateCopy.secretScanning.isLoading).toBe(false);
});
it('should set baseReportOutofDate to true', () => {
expect(stateCopy.secretScanning.baseReportOutofDate).toBe(true);
});
it('should parse and set the added vulnerabilities', () => {
reports.diff.added.forEach((vuln, i) => {
expect(stateCopy.secretScanning.newIssues[i]).toMatchObject({
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.secretScanning.resolvedIssues[i]).toMatchObject({
name: vuln.name,
title: vuln.name,
category: vuln.report_type,
});
});
});
});
describe('RECEIVE_SECRET_SCANNING_DIFF_ERROR', () => {
it('should set secret scanning loading flag to false and error flag to true', () => {
mutations[types.RECEIVE_SECRET_SCANNING_DIFF_ERROR](stateCopy);
expect(stateCopy.secretScanning.isLoading).toEqual(false);
expect(stateCopy.secretScanning.hasError).toEqual(true);
});
});
describe('SET_COVERAGE_FUZZING_DIFF_ENDPOINT', () => {
const endpoint = 'coverage_fuzzing_diff_endpoint.json';
......
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import createState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
import * as types from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import * as actions from '~/vue_shared/security_reports/store/modules/secret_detection/actions';
import axios from '~/lib/utils/axios_utils';
const diffEndpoint = 'diff-endpoint.json';
const blobPath = 'blob-path.json';
const reports = {
base: 'base',
head: 'head',
enrichData: 'enrichData',
diff: 'diff',
};
const error = 'Something went wrong';
const vulnerabilityFeedbackPath = 'vulnerability-feedback-path';
const rootState = { vulnerabilityFeedbackPath, blobPath };
let state;
describe('secret detection report actions', () => {
beforeEach(() => {
state = createState();
});
describe('setDiffEndpoint', () => {
it(`should commit ${types.SET_DIFF_ENDPOINT} with the correct path`, done => {
testAction(
actions.setDiffEndpoint,
diffEndpoint,
state,
[
{
type: types.SET_DIFF_ENDPOINT,
payload: diffEndpoint,
},
],
[],
done,
);
});
});
describe('requestDiff', () => {
it(`should commit ${types.REQUEST_DIFF}`, done => {
testAction(actions.requestDiff, {}, state, [{ type: types.REQUEST_DIFF }], [], done);
});
});
describe('receiveDiffSuccess', () => {
it(`should commit ${types.RECEIVE_DIFF_SUCCESS} with the correct response`, done => {
testAction(
actions.receiveDiffSuccess,
reports,
state,
[
{
type: types.RECEIVE_DIFF_SUCCESS,
payload: reports,
},
],
[],
done,
);
});
});
describe('receiveDiffError', () => {
it(`should commit ${types.RECEIVE_DIFF_ERROR} with the correct response`, done => {
testAction(
actions.receiveDiffError,
error,
state,
[
{
type: types.RECEIVE_DIFF_ERROR,
payload: error,
},
],
[],
done,
);
});
});
describe('fetchDiff', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
state.paths.diffEndpoint = diffEndpoint;
rootState.canReadVulnerabilityFeedback = true;
});
afterEach(() => {
mock.restore();
});
describe('when diff and vulnerability feedback endpoints respond successfully', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
.replyOnce(200, reports.diff)
.onGet(vulnerabilityFeedbackPath)
.replyOnce(200, reports.enrichData);
});
it('should dispatch the `receiveDiffSuccess` action', done => {
const { diff, enrichData } = reports;
testAction(
actions.fetchDiff,
{},
{ ...rootState, ...state },
[],
[
{ type: 'requestDiff' },
{
type: 'receiveDiffSuccess',
payload: {
diff,
enrichData,
},
},
],
done,
);
});
});
describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
beforeEach(() => {
rootState.canReadVulnerabilityFeedback = false;
mock.onGet(diffEndpoint).replyOnce(200, reports.diff);
});
it('should dispatch the `receiveDiffSuccess` action with empty enrich data', done => {
const { diff } = reports;
const enrichData = [];
testAction(
actions.fetchDiff,
{},
{ ...rootState, ...state },
[],
[
{ type: 'requestDiff' },
{
type: 'receiveDiffSuccess',
payload: {
diff,
enrichData,
},
},
],
done,
);
});
});
describe('when the vulnerability feedback endpoint fails', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
.replyOnce(200, reports.diff)
.onGet(vulnerabilityFeedbackPath)
.replyOnce(404);
});
it('should dispatch the `receiveDiffError` action', done => {
testAction(
actions.fetchDiff,
{},
{ ...rootState, ...state },
[],
[{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
done,
);
});
});
describe('when the diff endpoint fails', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
.replyOnce(404)
.onGet(vulnerabilityFeedbackPath)
.replyOnce(200, reports.enrichData);
});
it('should dispatch the `receiveDiffError` action', done => {
testAction(
actions.fetchDiff,
{},
{ ...rootState, ...state },
[],
[{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
done,
);
});
});
});
});
import * as types from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import createState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
import mutations from '~/vue_shared/security_reports/store/modules/secret_detection/mutations';
const createIssue = ({ ...config }) => ({ changed: false, ...config });
describe('secret detection module mutations', () => {
const path = 'path';
let state;
beforeEach(() => {
state = createState();
});
describe(types.SET_DIFF_ENDPOINT, () => {
it('should set the secret detection diff endpoint', () => {
mutations[types.SET_DIFF_ENDPOINT](state, path);
expect(state.paths.diffEndpoint).toBe(path);
});
});
describe(types.REQUEST_DIFF, () => {
it('should set the `isLoading` status to `true`', () => {
mutations[types.REQUEST_DIFF](state);
expect(state.isLoading).toBe(true);
});
});
describe(types.RECEIVE_DIFF_SUCCESS, () => {
beforeEach(() => {
const reports = {
diff: {
added: [
createIssue({ cve: 'CVE-1' }),
createIssue({ cve: 'CVE-2' }),
createIssue({ cve: 'CVE-3' }),
],
fixed: [createIssue({ cve: 'CVE-4' }), createIssue({ cve: 'CVE-5' })],
existing: [createIssue({ cve: 'CVE-6' })],
base_report_out_of_date: true,
},
};
state.isLoading = true;
mutations[types.RECEIVE_DIFF_SUCCESS](state, reports);
});
it('should set the `isLoading` status to `false`', () => {
expect(state.isLoading).toBe(false);
});
it('should set the `baseReportOutofDate` status to `true`', () => {
expect(state.baseReportOutofDate).toBe(true);
});
it('should have the relevant `new` issues', () => {
expect(state.newIssues).toHaveLength(3);
});
it('should have the relevant `resolved` issues', () => {
expect(state.resolvedIssues).toHaveLength(2);
});
it('should have the relevant `all` issues', () => {
expect(state.allIssues).toHaveLength(1);
});
});
describe(types.RECEIVE_DIFF_ERROR, () => {
beforeEach(() => {
state.isLoading = true;
mutations[types.RECEIVE_DIFF_ERROR](state);
});
it('should set the `isLoading` status to `false`', () => {
expect(state.isLoading).toBe(false);
});
it('should set the `hasError` status to `true`', () => {
expect(state.hasError).toBe(true);
});
});
});
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