Commit 8e193a3a authored by Yevgeny Name's avatar Yevgeny Name

Add Coverage Fuzzing to DB & Security Dashboard

This adds frontend

Related MR https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34648
Backend MR https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37173
parent 497620d5
...@@ -237,7 +237,13 @@ export default { ...@@ -237,7 +237,13 @@ export default {
}; };
}, },
}, },
securityReportTypes: ['dast', 'sast', 'dependencyScanning', 'containerScanning'], securityReportTypes: [
'dast',
'sast',
'dependencyScanning',
'containerScanning',
'coverageFuzzing',
],
}; };
</script> </script>
<template> <template>
...@@ -311,6 +317,7 @@ export default { ...@@ -311,6 +317,7 @@ export default {
:enabled-reports="mr.enabledReports" :enabled-reports="mr.enabledReports"
:sast-help-path="mr.sastHelp" :sast-help-path="mr.sastHelp"
:dast-help-path="mr.dastHelp" :dast-help-path="mr.dastHelp"
:coverage-fuzzing-help-path="mr.coverageFuzzingHelp"
:container-scanning-help-path="mr.containerScanningHelp" :container-scanning-help-path="mr.containerScanningHelp"
:dependency-scanning-help-path="mr.dependencyScanningHelp" :dependency-scanning-help-path="mr.dependencyScanningHelp"
:secret-scanning-help-path="mr.secretScanningHelp" :secret-scanning-help-path="mr.secretScanningHelp"
......
...@@ -10,6 +10,7 @@ export default class MergeRequestStore extends CEMergeRequestStore { ...@@ -10,6 +10,7 @@ export default class MergeRequestStore extends CEMergeRequestStore {
this.sastHelp = data.sast_help_path; this.sastHelp = data.sast_help_path;
this.containerScanningHelp = data.container_scanning_help_path; this.containerScanningHelp = data.container_scanning_help_path;
this.dastHelp = data.dast_help_path; this.dastHelp = data.dast_help_path;
this.coverageFuzzingHelp = data.coverage_fuzzing_help_path;
this.secretScanningHelp = data.secret_scanning_help_path; this.secretScanningHelp = data.secret_scanning_help_path;
this.dependencyScanningHelp = data.dependency_scanning_help_path; this.dependencyScanningHelp = data.dependency_scanning_help_path;
this.vulnerabilityFeedbackPath = data.vulnerability_feedback_path; this.vulnerabilityFeedbackPath = data.vulnerability_feedback_path;
......
...@@ -55,9 +55,6 @@ export default { ...@@ -55,9 +55,6 @@ export default {
crashAddress() { crashAddress() {
return this.vulnerability.location?.crash_address; return this.vulnerability.location?.crash_address;
}, },
crashState() {
return this.vulnerability.location?.crash_state;
},
className() { className() {
return this.vulnerability.location?.class; return this.vulnerability.location?.class;
}, },
...@@ -108,7 +105,7 @@ export default { ...@@ -108,7 +105,7 @@ export default {
}); });
}, },
stacktraceSnippet() { stacktraceSnippet() {
return this.vulnerability.stacktrace_snippet; return this.vulnerability.location?.stacktrace_snippet;
}, },
}, },
methods: { methods: {
...@@ -182,10 +179,6 @@ export default { ...@@ -182,10 +179,6 @@ export default {
<gl-friendly-wrap ref="crashAddress" :text="crashAddress" /> <gl-friendly-wrap ref="crashAddress" :text="crashAddress" />
</vulnerability-detail> </vulnerability-detail>
<vulnerability-detail v-if="crashState" :label="s__('Crash State')">
<code-block ref="crashState" :code="crashState" max-height="225px" />
</vulnerability-detail>
<vulnerability-detail v-if="stacktraceSnippet" :label="s__('Stacktrace snippet')"> <vulnerability-detail v-if="stacktraceSnippet" :label="s__('Stacktrace snippet')">
<code-block ref="stacktraceSnippet" :code="stacktraceSnippet" max-height="225px" /> <code-block ref="stacktraceSnippet" :code="stacktraceSnippet" max-height="225px" />
</vulnerability-detail> </vulnerability-detail>
......
...@@ -92,6 +92,11 @@ export default { ...@@ -92,6 +92,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
coverageFuzzingHelpPath: {
type: String,
required: false,
default: '',
},
dependencyScanningHelpPath: { dependencyScanningHelpPath: {
type: String, type: String,
required: false, required: false,
...@@ -178,6 +183,7 @@ export default { ...@@ -178,6 +183,7 @@ export default {
'sast', 'sast',
'containerScanning', 'containerScanning',
'dast', 'dast',
'coverageFuzzing',
'dependencyScanning', 'dependencyScanning',
'secretScanning', 'secretScanning',
'summaryCounts', 'summaryCounts',
...@@ -193,10 +199,12 @@ export default { ...@@ -193,10 +199,12 @@ export default {
'groupedDastText', 'groupedDastText',
'groupedDependencyText', 'groupedDependencyText',
'groupedSecretScanningText', 'groupedSecretScanningText',
'groupedCoverageFuzzingText',
'containerScanningStatusIcon', 'containerScanningStatusIcon',
'dastStatusIcon', 'dastStatusIcon',
'dependencyScanningStatusIcon', 'dependencyScanningStatusIcon',
'secretScanningStatusIcon', 'secretScanningStatusIcon',
'coverageFuzzingStatusIcon',
'isBaseSecurityReportOutOfDate', 'isBaseSecurityReportOutOfDate',
'canCreateIssue', 'canCreateIssue',
'canCreateMergeRequest', 'canCreateMergeRequest',
...@@ -215,6 +223,9 @@ export default { ...@@ -215,6 +223,9 @@ export default {
hasDastReports() { hasDastReports() {
return this.enabledReports.dast; return this.enabledReports.dast;
}, },
hasCoverageFuzzingReports() {
return this.enabledReports.coverageFuzzing;
},
hasSastReports() { hasSastReports() {
return this.enabledReports.sast; return this.enabledReports.sast;
}, },
...@@ -239,6 +250,9 @@ export default { ...@@ -239,6 +250,9 @@ export default {
dastDownloadLink() { dastDownloadLink() {
return this.dastSummary?.scannedResourcesCsvPath || ''; return this.dastSummary?.scannedResourcesCsvPath || '';
}, },
coverageFuzzingShowIssues() {
return this.coverageFuzzing.newIssues || this.coverageFuzzing.resolvedIssues;
},
}, },
created() { created() {
...@@ -289,6 +303,13 @@ export default { ...@@ -289,6 +303,13 @@ export default {
this.setSecretScanningDiffEndpoint(secretScanningDiffEndpoint); this.setSecretScanningDiffEndpoint(secretScanningDiffEndpoint);
this.fetchSecretScanningDiff(); this.fetchSecretScanningDiff();
} }
const coverageFuzzingDiffEndpoint = gl?.mrWidgetData?.coverage_fuzzing_comparison_path;
if (coverageFuzzingDiffEndpoint && this.hasCoverageFuzzingReports) {
this.setCoverageFuzzingDiffEndpoint(coverageFuzzingDiffEndpoint);
this.fetchCoverageFuzzingDiff();
}
}, },
methods: { methods: {
...mapActions([ ...mapActions([
...@@ -322,6 +343,8 @@ export default { ...@@ -322,6 +343,8 @@ export default {
'setDastDiffEndpoint', 'setDastDiffEndpoint',
'fetchSecretScanningDiff', 'fetchSecretScanningDiff',
'setSecretScanningDiffEndpoint', 'setSecretScanningDiffEndpoint',
'fetchCoverageFuzzingDiff',
'setCoverageFuzzingDiffEndpoint',
]), ]),
...mapActions('sast', { ...mapActions('sast', {
setSastDiffEndpoint: 'setDiffEndpoint', setSastDiffEndpoint: 'setDiffEndpoint',
...@@ -514,6 +537,29 @@ export default { ...@@ -514,6 +537,29 @@ export default {
/> />
</template> </template>
<template v-if="hasCoverageFuzzingReports">
<summary-row
:summary="groupedCoverageFuzzingText"
:status-icon="coverageFuzzingStatusIcon"
:popover-options="coverageFuzzingPopover"
class="js-coverage-fuzzing-widget"
data-qa-selector="coverage_fuzzing_report"
>
<template #summary>
<security-summary :message="groupedCoverageFuzzingText" />
</template>
</summary-row>
<grouped-issues-list
v-if="coverageFuzzingShowIssues"
:unresolved-issues="coverageFuzzing.newIssues"
:resolved-issues="coverageFuzzing.resolvedIssues"
:component="$options.componentNames.SecurityIssueBody"
class="report-block-group-list"
data-testid="coverage-fuzzing-issues-list"
/>
</template>
<issue-modal <issue-modal
:modal="modal" :modal="modal"
:vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath" :vulnerability-feedback-help-path="vulnerabilityFeedbackHelpPath"
......
...@@ -85,5 +85,18 @@ export default { ...@@ -85,5 +85,18 @@ export default {
), ),
}; };
}, },
coverageFuzzingPopover() {
return {
title: s__('ciReport|Coverage Fuzzing Title'),
content: sprintf(
s__('ciReport|%{linkStartTag}Learn more about Coverage Fuzzing %{linkEndTag}'),
{
linkStartTag: getLinkStartTag(this.coverageFuzzingHelpPath),
linkEndTag,
},
false,
),
};
},
}, },
}; };
...@@ -145,6 +145,46 @@ export const fetchDependencyScanningDiff = ({ state, dispatch }) => { ...@@ -145,6 +145,46 @@ export const fetchDependencyScanningDiff = ({ state, dispatch }) => {
export const updateDependencyScanningIssue = ({ commit }, issue) => export const updateDependencyScanningIssue = ({ commit }, issue) =>
commit(types.UPDATE_DEPENDENCY_SCANNING_ISSUE, issue); commit(types.UPDATE_DEPENDENCY_SCANNING_ISSUE, issue);
/**
* COVERAGE FUZZING
*/
export const setCoverageFuzzingDiffEndpoint = ({ commit }, path) =>
commit(types.SET_COVERAGE_FUZZING_DIFF_ENDPOINT, path);
export const requestCoverageFuzzingDiff = ({ commit }) =>
commit(types.REQUEST_COVERAGE_FUZZING_DIFF);
export const receiveCoverageFuzzingDiffSuccess = ({ commit }, response) =>
commit(types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS, response);
export const receiveCoverageFuzzingDiffError = ({ commit }) =>
commit(types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR);
export const fetchCoverageFuzzingDiff = ({ state, dispatch }) => {
dispatch('requestCoverageFuzzingDiff');
return Promise.all([
pollUntilComplete(state.coverageFuzzing.paths.diffEndpoint),
axios.get(state.vulnerabilityFeedbackPath, {
params: {
category: 'coverage_fuzzing',
},
}),
])
.then(values => {
dispatch('receiveCoverageFuzzingDiffSuccess', {
diff: values[0].data,
enrichData: values[1].data,
});
})
.catch(() => {
dispatch('receiveCoverageFuzzingDiffError');
});
};
export const updateCoverageFuzzingIssue = ({ commit }, issue) =>
commit(types.UPDATE_COVERAGE_FUZZING_ISSUE, issue);
/** /**
* SECRET SCANNING * SECRET SCANNING
*/ */
......
...@@ -30,6 +30,14 @@ export const groupedDependencyText = ({ dependencyScanning }) => ...@@ -30,6 +30,14 @@ export const groupedDependencyText = ({ dependencyScanning }) =>
messages.DEPENDENCY_SCANNING_IS_LOADING, messages.DEPENDENCY_SCANNING_IS_LOADING,
); );
export const groupedCoverageFuzzingText = ({ coverageFuzzing }) =>
groupedReportText(
coverageFuzzing,
messages.COVERAGE_FUZZING,
messages.COVERAGE_FUZZING_HAS_ERROR,
messages.COVERAGE_FUZZING_IS_LOADING,
);
export const summaryCounts = ({ export const summaryCounts = ({
containerScanning, containerScanning,
dast, dast,
...@@ -107,6 +115,9 @@ export const dependencyScanningStatusIcon = ({ dependencyScanning }) => ...@@ -107,6 +115,9 @@ export const dependencyScanningStatusIcon = ({ dependencyScanning }) =>
export const secretScanningStatusIcon = ({ secretScanning }) => export const secretScanningStatusIcon = ({ secretScanning }) =>
statusIcon(secretScanning.isLoading, secretScanning.hasError, secretScanning.newIssues.length); statusIcon(secretScanning.isLoading, secretScanning.hasError, secretScanning.newIssues.length);
export const coverageFuzzingStatusIcon = ({ coverageFuzzing }) =>
statusIcon(coverageFuzzing.isLoading, coverageFuzzing.hasError, coverageFuzzing.newIssues.length);
export const areReportsLoading = state => export const areReportsLoading = state =>
state.sast.isLoading || state.sast.isLoading ||
state.dast.isLoading || state.dast.isLoading ||
......
...@@ -6,6 +6,7 @@ const updateIssueActionsMap = { ...@@ -6,6 +6,7 @@ const updateIssueActionsMap = {
container_scanning: 'updateContainerScanningIssue', container_scanning: 'updateContainerScanningIssue',
dast: 'updateDastIssue', dast: 'updateDastIssue',
secret_scanning: 'updateSecretScanningIssue', secret_scanning: 'updateSecretScanningIssue',
coverage_fuzzing: 'updateCoverageFuzzingIssue',
}; };
export default function configureMediator(store) { export default function configureMediator(store) {
......
...@@ -8,6 +8,7 @@ const DAST = s__('ciReport|DAST'); ...@@ -8,6 +8,7 @@ const DAST = s__('ciReport|DAST');
const CONTAINER_SCANNING = s__('ciReport|Container scanning'); const CONTAINER_SCANNING = s__('ciReport|Container scanning');
const DEPENDENCY_SCANNING = s__('ciReport|Dependency scanning'); const DEPENDENCY_SCANNING = s__('ciReport|Dependency scanning');
const SECRET_SCANNING = s__('ciReport|Secret scanning'); const SECRET_SCANNING = s__('ciReport|Secret scanning');
const COVERAGE_FUZZING = s__('ciReport|Coverage fuzzing');
export default { export default {
SAST, SAST,
...@@ -15,6 +16,7 @@ export default { ...@@ -15,6 +16,7 @@ export default {
CONTAINER_SCANNING, CONTAINER_SCANNING,
DEPENDENCY_SCANNING, DEPENDENCY_SCANNING,
SECRET_SCANNING, SECRET_SCANNING,
COVERAGE_FUZZING,
TRANSLATION_IS_LOADING, TRANSLATION_IS_LOADING,
TRANSLATION_HAS_ERROR, TRANSLATION_HAS_ERROR,
SAST_IS_LOADING: sprintf(TRANSLATION_IS_LOADING, { reportType: SAST }), SAST_IS_LOADING: sprintf(TRANSLATION_IS_LOADING, { reportType: SAST }),
...@@ -35,4 +37,8 @@ export default { ...@@ -35,4 +37,8 @@ export default {
reportType: SECRET_SCANNING, reportType: SECRET_SCANNING,
}), }),
SECRET_SCANNING_HAS_ERROR: sprintf(TRANSLATION_HAS_ERROR, { reportType: SECRET_SCANNING }), SECRET_SCANNING_HAS_ERROR: sprintf(TRANSLATION_HAS_ERROR, { reportType: SECRET_SCANNING }),
COVERAGE_FUZZING_IS_LOADING: sprintf(TRANSLATION_IS_LOADING, {
reportType: COVERAGE_FUZZING,
}),
COVERAGE_FUZZING_HAS_ERROR: sprintf(TRANSLATION_HAS_ERROR, { reportType: COVERAGE_FUZZING }),
}; };
...@@ -36,6 +36,12 @@ export const REQUEST_SECRET_SCANNING_DIFF = 'REQUEST_SECRET_SCANNING_DIFF'; ...@@ -36,6 +36,12 @@ 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_SUCCESS = 'RECEIVE_SECRET_SCANNING_DIFF_SUCCESS';
export const RECEIVE_SECRET_SCANNING_DIFF_ERROR = 'RECEIVE_SECRET_SCANNING_DIFF_ERROR'; 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';
export const RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS = 'RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS';
export const RECEIVE_COVERAGE_FUZZING_DIFF_ERROR = 'RECEIVE_COVERAGE_FUZZING_DIFF_ERROR';
// Dismiss security issue // Dismiss security issue
export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA'; export const SET_ISSUE_MODAL_DATA = 'SET_ISSUE_MODAL_DATA';
export const REQUEST_DISMISS_VULNERABILITY = 'REQUEST_DISMISS_VULNERABILITY'; export const REQUEST_DISMISS_VULNERABILITY = 'REQUEST_DISMISS_VULNERABILITY';
...@@ -64,6 +70,7 @@ export const UPDATE_DEPENDENCY_SCANNING_ISSUE = 'UPDATE_DEPENDENCY_SCANNING_ISSU ...@@ -64,6 +70,7 @@ export const UPDATE_DEPENDENCY_SCANNING_ISSUE = 'UPDATE_DEPENDENCY_SCANNING_ISSU
export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE'; export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE';
export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE'; export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE';
export const UPDATE_SECRET_SCANNING_ISSUE = 'UPDATE_SECRET_SCANNING_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 '; export const OPEN_DISMISSAL_COMMENT_BOX = 'OPEN_DISMISSAL_COMMENT_BOX ';
export const CLOSE_DISMISSAL_COMMENT_BOX = 'CLOSE_DISMISSAL_COMMENT_BOX'; export const CLOSE_DISMISSAL_COMMENT_BOX = 'CLOSE_DISMISSAL_COMMENT_BOX';
...@@ -101,6 +101,36 @@ export default { ...@@ -101,6 +101,36 @@ export default {
Vue.set(state.dast, 'hasError', true); Vue.set(state.dast, 'hasError', true);
}, },
// COVERAGE_FUZZING
[types.SET_COVERAGE_FUZZING_DIFF_ENDPOINT](state, path) {
Vue.set(state.coverageFuzzing.paths, 'diffEndpoint', path);
},
[types.REQUEST_COVERAGE_FUZZING_DIFF](state) {
Vue.set(state.coverageFuzzing, 'isLoading', true);
},
[types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS](state, { diff, enrichData }) {
const { added, fixed, existing } = parseDiff(diff, enrichData);
const baseReportOutofDate = diff.base_report_out_of_date || false;
const scans = diff.scans || [];
const hasBaseReport = Boolean(diff.base_report_created_at);
Vue.set(state.coverageFuzzing, 'isLoading', false);
Vue.set(state.coverageFuzzing, 'newIssues', added);
Vue.set(state.coverageFuzzing, 'resolvedIssues', fixed);
Vue.set(state.coverageFuzzing, 'allIssues', existing);
Vue.set(state.coverageFuzzing, 'baseReportOutofDate', baseReportOutofDate);
Vue.set(state.coverageFuzzing, 'hasBaseReport', hasBaseReport);
Vue.set(state.coverageFuzzing, 'scans', scans);
},
[types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR](state) {
Vue.set(state.coverageFuzzing, 'isLoading', false);
Vue.set(state.coverageFuzzing, 'hasError', true);
},
// DEPENDECY SCANNING // DEPENDECY SCANNING
[types.SET_DEPENDENCY_SCANNING_DIFF_ENDPOINT](state, path) { [types.SET_DEPENDENCY_SCANNING_DIFF_ENDPOINT](state, path) {
......
...@@ -44,7 +44,22 @@ export default () => ({ ...@@ -44,7 +44,22 @@ export default () => ({
hasBaseReport: false, hasBaseReport: false,
scans: [], scans: [],
}, },
coverageFuzzing: {
paths: {
head: null,
base: null,
diffEndpoint: null,
},
isLoading: false,
hasError: false,
newIssues: [],
resolvedIssues: [],
allIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
},
dependencyScanning: { dependencyScanning: {
paths: { paths: {
head: null, head: null,
......
...@@ -18,6 +18,9 @@ export default { ...@@ -18,6 +18,9 @@ export default {
location() { location() {
return this.vulnerability.location || {}; return this.vulnerability.location || {};
}, },
stacktraceSnippet() {
return this.vulnerability.stacktrace_snippet || '';
},
scanner() { scanner() {
return this.vulnerability.scanner || {}; return this.vulnerability.scanner || {};
}, },
...@@ -86,6 +89,11 @@ export default { ...@@ -86,6 +89,11 @@ export default {
}, },
].filter(x => x.content); ].filter(x => x.content);
}, },
shouldShowLocation() {
return (
this.location.crash_address || this.location.crash_type || this.location.stacktrace_snippet
);
},
}, },
methods: { methods: {
getHeadersAsCodeBlockLines(headers) { getHeadersAsCodeBlockLines(headers) {
...@@ -154,6 +162,23 @@ export default { ...@@ -154,6 +162,23 @@ export default {
</detail-item> </detail-item>
</ul> </ul>
<template v-if="shouldShowLocation">
<h3>{{ __('Location') }}</h3>
<ul>
<detail-item
v-if="location.crash_address"
:sprintf-message="__('%{labelStart}Crash Address:%{labelEnd} %{crash_address}')"
>{{ location.crash_address }}
</detail-item>
<detail-item
v-if="location.stacktrace_snippet"
:sprintf-message="__('%{labelStart}Crash State:%{labelEnd} %{stacktrace_snippet}')"
>
<code-block :code="location.stacktrace_snippet" max-height="225px" />
</detail-item>
</ul>
</template>
<template v-if="location.file"> <template v-if="location.file">
<h3>{{ __('Location') }}</h3> <h3>{{ __('Location') }}</h3>
<ul> <ul>
......
---
title: add add coverage fuzzing results to security dashboard
merge_request: 36011
author:
type: added
...@@ -23,6 +23,7 @@ import { ...@@ -23,6 +23,7 @@ import {
containerScanningDiffSuccessMock, containerScanningDiffSuccessMock,
dependencyScanningDiffSuccessMock, dependencyScanningDiffSuccessMock,
secretScanningDiffSuccessMock, secretScanningDiffSuccessMock,
coverageFuzzingDiffSuccessMock,
} from 'ee_jest/vue_shared/security_reports/mock_data'; } from 'ee_jest/vue_shared/security_reports/mock_data';
const SAST_SELECTOR = '.js-sast-widget'; const SAST_SELECTOR = '.js-sast-widget';
...@@ -30,6 +31,7 @@ const DAST_SELECTOR = '.js-dast-widget'; ...@@ -30,6 +31,7 @@ const DAST_SELECTOR = '.js-dast-widget';
const DEPENDENCY_SCANNING_SELECTOR = '.js-dependency-scanning-widget'; const DEPENDENCY_SCANNING_SELECTOR = '.js-dependency-scanning-widget';
const CONTAINER_SCANNING_SELECTOR = '.js-container-scanning'; const CONTAINER_SCANNING_SELECTOR = '.js-container-scanning';
const SECRET_SCANNING_SELECTOR = '.js-secret-scanning'; const SECRET_SCANNING_SELECTOR = '.js-secret-scanning';
const COVERAGE_FUZZING_SELECTOR = '.js-coverage-fuzzing-widget';
describe('ee merge request widget options', () => { describe('ee merge request widget options', () => {
let vm; let vm;
...@@ -742,6 +744,78 @@ describe('ee merge request widget options', () => { ...@@ -742,6 +744,78 @@ describe('ee merge request widget options', () => {
}); });
}); });
describe('Coverage Fuzzing', () => {
const COVERAGE_FUZZING_ENDPOINT = 'coverage_fuzzing_report';
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
enabled_reports: {
coverage_fuzzing: true,
},
coverage_fuzzing_comparison_path: COVERAGE_FUZZING_ENDPOINT,
vulnerability_feedback_path: VULNERABILITY_FEEDBACK_ENDPOINT,
};
});
describe('when it is loading', () => {
it('should render loading indicator', () => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
expect(
findSecurityWidget()
.querySelector(COVERAGE_FUZZING_SELECTOR)
.textContent.trim(),
).toContain('Coverage fuzzing is loading');
});
});
describe('with successful request', () => {
beforeEach(() => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('should render provided data', done => {
setImmediate(() => {
expect(
findSecurityWidget()
.querySelector(`${COVERAGE_FUZZING_SELECTOR} .report-block-list-issue-description`)
.textContent.trim(),
).toEqual(
'Coverage fuzzing detected 1 new critical and 1 new high severity vulnerabilities.',
);
done();
});
});
});
describe('with failed request', () => {
beforeEach(() => {
mock.onGet(COVERAGE_FUZZING_ENDPOINT).reply(500, {});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(500, {});
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('should render error indicator', done => {
setImmediate(() => {
expect(
findSecurityWidget()
.querySelector(COVERAGE_FUZZING_SELECTOR)
.textContent.trim(),
).toContain('Coverage fuzzing: Loading resulted in an error');
done();
});
});
});
});
describe('Secret Scanning', () => { describe('Secret Scanning', () => {
const SECRET_SCANNING_ENDPOINT = 'secret_scanning'; const SECRET_SCANNING_ENDPOINT = 'secret_scanning';
......
...@@ -99,8 +99,6 @@ key2: value2" ...@@ -99,8 +99,6 @@ key2: value2"
<!----> <!---->
<!---->
<vulnerability-detail-stub <vulnerability-detail-stub
label="Identifiers" label="Identifiers"
> >
......
...@@ -33,7 +33,6 @@ describe('VulnerabilityDetails component', () => { ...@@ -33,7 +33,6 @@ describe('VulnerabilityDetails component', () => {
const findRequestHeaders = () => wrapper.find({ ref: 'requestHeaders' }); const findRequestHeaders = () => wrapper.find({ ref: 'requestHeaders' });
const findResponseHeaders = () => wrapper.find({ ref: 'responseHeaders' }); const findResponseHeaders = () => wrapper.find({ ref: 'responseHeaders' });
const findResponseStatusCode = () => wrapper.find({ ref: 'responseStatusCode' }); const findResponseStatusCode = () => wrapper.find({ ref: 'responseStatusCode' });
const findCrashState = () => wrapper.find({ ref: 'crashState' });
const findCrashAddress = () => wrapper.find({ ref: 'crashAddress' }); const findCrashAddress = () => wrapper.find({ ref: 'crashAddress' });
const findStacktraceSnippet = () => wrapper.find({ ref: 'stacktraceSnippet' }); const findStacktraceSnippet = () => wrapper.find({ ref: 'stacktraceSnippet' });
...@@ -161,26 +160,18 @@ describe('VulnerabilityDetails component', () => { ...@@ -161,26 +160,18 @@ describe('VulnerabilityDetails component', () => {
describe('with coverage fuzzing information', () => { describe('with coverage fuzzing information', () => {
beforeEach(() => { beforeEach(() => {
const vulnerability = makeVulnerability({ const vulnerability = makeVulnerability({
stacktrace_snippet: 'test snippet',
location: { location: {
crash_address: '0x602000001573', crash_address: '0x602000001573',
crash_state: 'FuzzMe\nstart\nstart+0x0\n\n', crash_state: 'FuzzMe\nstart\nstart+0x0\n\n',
crash_type: 'Heap-buffer-overflow\nREAD 1', crash_type: 'Heap-buffer-overflow\nREAD 1',
stacktrace_snippet: 'test snippet',
}, },
}); });
componentFactory(vulnerability); componentFactory(vulnerability);
}); });
it('renders a code-block containing the crash_state', () => {
expect(findCrashState().is(CodeBlock)).toBe(true);
expect(findCrashState().text()).toBe('FuzzMe\nstart\nstart+0x0');
});
it('renders crash_address', () => { it('renders crash_address', () => {
expect(findCrashAddress().exists()).toBe(true); expect(findCrashAddress().exists()).toBe(true);
});
it('renders stacktrace_snippet', () => {
expect(findStacktraceSnippet().exists()).toBe(true); expect(findStacktraceSnippet().exists()).toBe(true);
}); });
}); });
......
...@@ -22,6 +22,7 @@ import { ...@@ -22,6 +22,7 @@ import {
containerScanningDiffSuccessMock, containerScanningDiffSuccessMock,
dependencyScanningDiffSuccessMock, dependencyScanningDiffSuccessMock,
secretScanningDiffSuccessMock, secretScanningDiffSuccessMock,
coverageFuzzingDiffSuccessMock,
mockFindings, mockFindings,
} from './mock_data'; } from './mock_data';
...@@ -30,6 +31,7 @@ const DEPENDENCY_SCANNING_DIFF_ENDPOINT = 'dependency_scanning.json'; ...@@ -30,6 +31,7 @@ const DEPENDENCY_SCANNING_DIFF_ENDPOINT = 'dependency_scanning.json';
const DAST_DIFF_ENDPOINT = 'dast.json'; const DAST_DIFF_ENDPOINT = 'dast.json';
const SAST_DIFF_ENDPOINT = 'sast.json'; const SAST_DIFF_ENDPOINT = 'sast.json';
const SECRET_SCANNING_DIFF_ENDPOINT = 'secret_scanning.json'; const SECRET_SCANNING_DIFF_ENDPOINT = 'secret_scanning.json';
const COVERAGE_FUZZING_DIFF_ENDPOINT = 'coverage_fuzzing.json';
describe('Grouped security reports app', () => { describe('Grouped security reports app', () => {
let wrapper; let wrapper;
...@@ -48,6 +50,7 @@ describe('Grouped security reports app', () => { ...@@ -48,6 +50,7 @@ describe('Grouped security reports app', () => {
canReadVulnerabilityFeedbackPath: true, canReadVulnerabilityFeedbackPath: true,
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json', vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path', vulnerabilityFeedbackHelpPath: 'path',
coverageFuzzingHelpPath: 'path',
pipelineId: 123, pipelineId: 123,
projectFullPath: 'path', projectFullPath: 'path',
}; };
...@@ -96,6 +99,7 @@ describe('Grouped security reports app', () => { ...@@ -96,6 +99,7 @@ describe('Grouped security reports app', () => {
containerScanning: true, containerScanning: true,
dependencyScanning: true, dependencyScanning: true,
secretScanning: true, secretScanning: true,
coverageFuzzing: true,
}, },
}; };
...@@ -106,6 +110,7 @@ describe('Grouped security reports app', () => { ...@@ -106,6 +110,7 @@ describe('Grouped security reports app', () => {
gl.mrWidgetData.dast_comparison_path = DAST_DIFF_ENDPOINT; gl.mrWidgetData.dast_comparison_path = DAST_DIFF_ENDPOINT;
gl.mrWidgetData.sast_comparison_path = SAST_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_SCANNING_DIFF_ENDPOINT;
gl.mrWidgetData.coverage_fuzzing_comparison_path = COVERAGE_FUZZING_DIFF_ENDPOINT;
}); });
describe('with error', () => { describe('with error', () => {
...@@ -115,6 +120,7 @@ describe('Grouped security reports app', () => { ...@@ -115,6 +120,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DAST_DIFF_ENDPOINT).reply(500); mock.onGet(DAST_DIFF_ENDPOINT).reply(500);
mock.onGet(SAST_DIFF_ENDPOINT).reply(500); mock.onGet(SAST_DIFF_ENDPOINT).reply(500);
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(500); mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(500);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(500);
createWrapper(allReportProps); createWrapper(allReportProps);
...@@ -124,6 +130,7 @@ describe('Grouped security reports app', () => { ...@@ -124,6 +130,7 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_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_DEPENDENCY_SCANNING_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_ERROR), waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR),
]); ]);
}); });
...@@ -162,6 +169,7 @@ describe('Grouped security reports app', () => { ...@@ -162,6 +169,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, {}); mock.onGet(DAST_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, {}); mock.onGet(SAST_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, {}); mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, {});
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, {});
createWrapper(allReportProps); createWrapper(allReportProps);
}); });
...@@ -180,6 +188,7 @@ describe('Grouped security reports app', () => { ...@@ -180,6 +188,7 @@ describe('Grouped security reports app', () => {
expect(wrapper.vm.$el.textContent).toContain('Dependency scanning is loading'); expect(wrapper.vm.$el.textContent).toContain('Dependency scanning is loading');
expect(wrapper.vm.$el.textContent).toContain('Container scanning is loading'); expect(wrapper.vm.$el.textContent).toContain('Container scanning is loading');
expect(wrapper.vm.$el.textContent).toContain('DAST is loading'); expect(wrapper.vm.$el.textContent).toContain('DAST is loading');
expect(wrapper.vm.$el.textContent).toContain('Coverage fuzzing is loading');
}); });
}); });
...@@ -191,6 +200,7 @@ describe('Grouped security reports app', () => { ...@@ -191,6 +200,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, emptyResponse); mock.onGet(DAST_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(SAST_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_SCANNING_DIFF_ENDPOINT).reply(200, emptyResponse);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, emptyResponse);
createWrapper(allReportProps); createWrapper(allReportProps);
...@@ -200,6 +210,7 @@ describe('Grouped security reports app', () => { ...@@ -200,6 +210,7 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_CONTAINER_SCANNING_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_DEPENDENCY_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS), waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS),
]); ]);
}); });
...@@ -239,6 +250,7 @@ describe('Grouped security reports app', () => { ...@@ -239,6 +250,7 @@ describe('Grouped security reports app', () => {
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, dastDiffSuccessMock); mock.onGet(DAST_DIFF_ENDPOINT).reply(200, dastDiffSuccessMock);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, sastDiffSuccessMock); mock.onGet(SAST_DIFF_ENDPOINT).reply(200, sastDiffSuccessMock);
mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock); mock.onGet(SECRET_SCANNING_DIFF_ENDPOINT).reply(200, secretScanningDiffSuccessMock);
mock.onGet(COVERAGE_FUZZING_DIFF_ENDPOINT).reply(200, coverageFuzzingDiffSuccessMock);
createWrapper(allReportProps); createWrapper(allReportProps);
...@@ -248,6 +260,7 @@ describe('Grouped security reports app', () => { ...@@ -248,6 +260,7 @@ describe('Grouped security reports app', () => {
waitForMutation(wrapper.vm.$store, types.RECEIVE_CONTAINER_SCANNING_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_DEPENDENCY_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS), waitForMutation(wrapper.vm.$store, types.RECEIVE_SECRET_SCANNING_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS),
]); ]);
}); });
...@@ -284,6 +297,11 @@ describe('Grouped security reports app', () => { ...@@ -284,6 +297,11 @@ describe('Grouped security reports app', () => {
expect(wrapper.vm.$el.textContent).toContain( expect(wrapper.vm.$el.textContent).toContain(
'DAST detected 1 new critical severity vulnerability.', 'DAST detected 1 new critical severity vulnerability.',
); );
// Renders container scanning result
expect(wrapper.vm.$el.textContent).toContain(
'Coverage fuzzing detected 1 new critical and 1 new high severity vulnerabilities.',
);
}); });
it('opens modal with more information', () => { it('opens modal with more information', () => {
...@@ -307,6 +325,7 @@ describe('Grouped security reports app', () => { ...@@ -307,6 +325,7 @@ describe('Grouped security reports app', () => {
${'container-scanning'} | ${containerScanningDiffSuccessMock.fixed} | ${containerScanningDiffSuccessMock.added} ${'container-scanning'} | ${containerScanningDiffSuccessMock.fixed} | ${containerScanningDiffSuccessMock.added}
${'dast'} | ${dastDiffSuccessMock.fixed} | ${dastDiffSuccessMock.added} ${'dast'} | ${dastDiffSuccessMock.fixed} | ${dastDiffSuccessMock.added}
${'secret-scanning'} | ${secretScanningDiffSuccessMock.fixed} | ${secretScanningDiffSuccessMock.added} ${'secret-scanning'} | ${secretScanningDiffSuccessMock.fixed} | ${secretScanningDiffSuccessMock.added}
${'coverage-fuzzing'} | ${coverageFuzzingDiffSuccessMock.fixed} | ${coverageFuzzingDiffSuccessMock.added}
`( `(
'renders a grouped-issues-list with the correct props for "$reportType" issues', 'renders a grouped-issues-list with the correct props for "$reportType" issues',
({ reportType, resolvedIssues, unresolvedIssues }) => { ({ reportType, resolvedIssues, unresolvedIssues }) => {
......
...@@ -302,6 +302,31 @@ export const secretScanningFeedbacks = [ ...@@ -302,6 +302,31 @@ export const secretScanningFeedbacks = [
}, },
]; ];
export const coverageFuzzingFeedbacks = [
{
id: 3,
project_id: 17,
author_id: 1,
issue_iid: null,
pipeline_id: 132,
category: 'coverage_fuzzing',
feedback_type: 'dismissal',
branch: 'try_new_coverage_fuzzing',
project_fingerprint: 'f55331d66fd4f3bfb4237d48e9c9fa8704bd33c6',
},
{
id: 4,
project_id: 17,
author_id: 1,
issue_iid: 123,
pipeline_id: 132,
category: 'coverage_fuzzing',
feedback_type: 'issue',
branch: 'try_new_coverage_fuzzing',
project_fingerprint: 'f55331d66fd4f3bfb4237d48e9c9fa8704bd33c6',
},
];
export const mockFindings = [ export const mockFindings = [
{ {
id: null, id: null,
...@@ -644,3 +669,11 @@ export const secretScanningDiffSuccessMock = { ...@@ -644,3 +669,11 @@ export const secretScanningDiffSuccessMock = {
base_report_out_of_date: false, base_report_out_of_date: false,
head_report_created_at: '2020-01-10T10:00:00.000Z', head_report_created_at: '2020-01-10T10:00:00.000Z',
}; };
export const coverageFuzzingDiffSuccessMock = {
added: [mockFindings[0], mockFindings[1]],
fixed: [mockFindings[2]],
base_report_created_at: '2020-01-01T10:00:00.000Z',
base_report_out_of_date: false,
head_report_created_at: '2020-01-10T10:00:00.000Z',
};
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
requestContainerScanningDiff, requestContainerScanningDiff,
requestDastDiff, requestDastDiff,
requestDependencyScanningDiff, requestDependencyScanningDiff,
requestCoverageFuzzingDiff,
openModal, openModal,
setModalData, setModalData,
requestDismissVulnerability, requestDismissVulnerability,
...@@ -29,6 +30,7 @@ import { ...@@ -29,6 +30,7 @@ import {
updateContainerScanningIssue, updateContainerScanningIssue,
updateDastIssue, updateDastIssue,
updateSecretScanningIssue, updateSecretScanningIssue,
updateCoverageFuzzingIssue,
addDismissalComment, addDismissalComment,
receiveAddDismissalCommentError, receiveAddDismissalCommentError,
receiveAddDismissalCommentSuccess, receiveAddDismissalCommentSuccess,
...@@ -55,6 +57,10 @@ import { ...@@ -55,6 +57,10 @@ import {
receiveSecretScanningDiffSuccess, receiveSecretScanningDiffSuccess,
receiveSecretScanningDiffError, receiveSecretScanningDiffError,
fetchSecretScanningDiff, fetchSecretScanningDiff,
setCoverageFuzzingDiffEndpoint,
receiveCoverageFuzzingDiffSuccess,
receiveCoverageFuzzingDiffError,
fetchCoverageFuzzingDiff,
} from 'ee/vue_shared/security_reports/store/actions'; } from 'ee/vue_shared/security_reports/store/actions';
import * as types from 'ee/vue_shared/security_reports/store/mutation_types'; import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import state from 'ee/vue_shared/security_reports/store/state'; import state from 'ee/vue_shared/security_reports/store/state';
...@@ -65,6 +71,7 @@ import { ...@@ -65,6 +71,7 @@ import {
containerScanningFeedbacks, containerScanningFeedbacks,
dependencyScanningFeedbacks, dependencyScanningFeedbacks,
secretScanningFeedbacks, secretScanningFeedbacks,
coverageFuzzingFeedbacks,
} from '../mock_data'; } from '../mock_data';
import toasted from '~/vue_shared/plugins/global_toast'; import toasted from '~/vue_shared/plugins/global_toast';
...@@ -272,6 +279,23 @@ describe('security reports actions', () => { ...@@ -272,6 +279,23 @@ describe('security reports actions', () => {
}); });
}); });
describe('requestCoverageFuzzingDiff', () => {
it('should commit request mutation', done => {
testAction(
requestCoverageFuzzingDiff,
null,
mockedState,
[
{
type: types.REQUEST_COVERAGE_FUZZING_DIFF,
},
],
[],
done,
);
});
});
describe('openModal', () => { describe('openModal', () => {
it('dispatches setModalData action', done => { it('dispatches setModalData action', done => {
testAction( testAction(
...@@ -1102,6 +1126,26 @@ describe('security reports actions', () => { ...@@ -1102,6 +1126,26 @@ describe('security reports actions', () => {
}); });
}); });
describe('updateCoverageFuzzingIssue', () => {
it('commits update coverageFuzzing issue', done => {
const payload = { foo: 'bar' };
testAction(
updateCoverageFuzzingIssue,
payload,
mockedState,
[
{
type: types.UPDATE_COVERAGE_FUZZING_ISSUE,
payload,
},
],
[],
done,
);
});
});
describe('setContainerScanningDiffEndpoint', () => { describe('setContainerScanningDiffEndpoint', () => {
it('should pass down the endpoint to the mutation', done => { it('should pass down the endpoint to the mutation', done => {
const payload = '/container_scanning_endpoint.json'; const payload = '/container_scanning_endpoint.json';
...@@ -1851,4 +1895,161 @@ describe('security reports actions', () => { ...@@ -1851,4 +1895,161 @@ describe('security reports actions', () => {
}); });
}); });
}); });
describe('setCoverageFuzzingDiffEndpoint', () => {
it('should pass down the endpoint to the mutation', done => {
const payload = '/coverage_fuzzing_endpoint.json';
testAction(
setCoverageFuzzingDiffEndpoint,
payload,
mockedState,
[
{
type: types.SET_COVERAGE_FUZZING_DIFF_ENDPOINT,
payload,
},
],
[],
done,
);
});
});
describe('receiveCoverageFuzzingDiffSuccess', () => {
it('should pass down the response to the mutation', done => {
const payload = { data: 'Effort yields its own rewards.' };
testAction(
receiveCoverageFuzzingDiffSuccess,
payload,
mockedState,
[
{
type: types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS,
payload,
},
],
[],
done,
);
});
});
describe('receiveCoverageFuzzingDiffError', () => {
it('should commit coverage fuzzing diff error mutation', done => {
testAction(
receiveCoverageFuzzingDiffError,
undefined,
mockedState,
[
{
type: types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR,
},
],
[],
done,
);
});
});
describe('fetcCoverageFuzzingDiff', () => {
const diff = { foo: {} };
beforeEach(() => {
mockedState.vulnerabilityFeedbackPath = 'vulnerabilities_feedback';
mockedState.coverageFuzzing.paths.diffEndpoint = 'coverage_fuzzing_diff.json';
});
describe('on success', () => {
it('should dispatch `receiveCoverageFuzzingDiffSuccess`', done => {
mock.onGet('coverage_fuzzing_diff.json').reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'coverage_fuzzing',
},
})
.reply(200, coverageFuzzingFeedbacks);
testAction(
fetchCoverageFuzzingDiff,
null,
mockedState,
[],
[
{
type: 'requestCoverageFuzzingDiff',
},
{
type: 'receiveCoverageFuzzingDiffSuccess',
payload: {
diff,
enrichData: coverageFuzzingFeedbacks,
},
},
],
done,
);
});
});
describe('when vulnerabilities path errors', () => {
it('should dispatch `receiveCoverageFuzzingError`', done => {
mock.onGet('coverage_fuzzing_diff.json').reply(500);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'coverage_fuzzing',
},
})
.reply(200, coverageFuzzingFeedbacks);
testAction(
fetchCoverageFuzzingDiff,
null,
mockedState,
[],
[
{
type: 'requestCoverageFuzzingDiff',
},
{
type: 'receiveCoverageFuzzingDiffError',
},
],
done,
);
});
});
describe('when feedback path errors', () => {
it('should dispatch `receiveCoverageFuzzingError`', done => {
mock.onGet('coverage_fuzzing_diff.json').reply(200, diff);
mock
.onGet('vulnerabilities_feedback', {
params: {
category: 'coverage_fuzzing',
},
})
.reply(500);
testAction(
fetchCoverageFuzzingDiff,
null,
mockedState,
[],
[
{
type: 'requestCoverageFuzzingDiff',
},
{
type: 'receiveCoverageFuzzingDiffError',
},
],
done,
);
});
});
});
}); });
...@@ -806,4 +806,91 @@ describe('security reports mutations', () => { ...@@ -806,4 +806,91 @@ describe('security reports mutations', () => {
expect(stateCopy.secretScanning.hasError).toEqual(true); expect(stateCopy.secretScanning.hasError).toEqual(true);
}); });
}); });
describe('SET_COVERAGE_FUZZING_DIFF_ENDPOINT', () => {
const endpoint = 'coverage_fuzzing_diff_endpoint.json';
beforeEach(() => {
mutations[types.SET_COVERAGE_FUZZING_DIFF_ENDPOINT](stateCopy, endpoint);
});
it('should set the correct endpoint', () => {
expect(stateCopy.coverageFuzzing.paths.diffEndpoint).toEqual(endpoint);
});
});
describe('RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS', () => {
let reports = {};
const scans = [
{
scanned_resources_count: 123,
job_path: '/group/project/-/jobs/123546789',
},
{
scanned_resources_count: 321,
job_path: '/group/project/-/jobs/987654321',
},
];
beforeEach(() => {
reports = {
diff: {
added: [
{ name: 'added vuln 1', report_type: 'coverage_fuzzing' },
{ name: 'added vuln 2', report_type: 'coverage_fuzzing' },
],
fixed: [{ name: 'fixed vuln 1', report_type: 'coverage_fuzzing' }],
existing: [{ name: 'existing vuln 1', report_type: 'coverage_fuzzing' }],
base_report_out_of_date: true,
scans,
},
};
mutations[types.RECEIVE_COVERAGE_FUZZING_DIFF_SUCCESS](stateCopy, reports);
});
it('should set isLoading to false', () => {
expect(stateCopy.coverageFuzzing.isLoading).toBe(false);
});
it('should set scans', () => {
expect(stateCopy.coverageFuzzing.scans).toEqual(scans);
});
it('should set baseReportOutofDate to true', () => {
expect(stateCopy.coverageFuzzing.baseReportOutofDate).toBe(true);
});
it('should parse and set the added vulnerabilities', () => {
reports.diff.added.forEach((vuln, i) => {
expect(stateCopy.coverageFuzzing.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.coverageFuzzing.resolvedIssues[i]).toEqual(
expect.objectContaining({
name: vuln.name,
title: vuln.name,
category: vuln.report_type,
}),
);
});
});
});
describe('RECEIVE_COVERAGE_FUZZING_DIFF_ERROR', () => {
it('should set coverage fuzzing loading flag to false and error flag to true', () => {
mutations[types.RECEIVE_COVERAGE_FUZZING_DIFF_ERROR](stateCopy);
expect(stateCopy.coverageFuzzing.isLoading).toEqual(false);
expect(stateCopy.coverageFuzzing.hasError).toEqual(true);
});
});
}); });
...@@ -420,6 +420,12 @@ msgstr "" ...@@ -420,6 +420,12 @@ msgstr ""
msgid "%{labelStart}Class:%{labelEnd} %{class}" msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "" msgstr ""
msgid "%{labelStart}Crash Address:%{labelEnd} %{crash_address}"
msgstr ""
msgid "%{labelStart}Crash State:%{labelEnd} %{stacktrace_snippet}"
msgstr ""
msgid "%{labelStart}Evidence:%{labelEnd} %{evidence}" msgid "%{labelStart}Evidence:%{labelEnd} %{evidence}"
msgstr "" msgstr ""
...@@ -6824,9 +6830,6 @@ msgstr "" ...@@ -6824,9 +6830,6 @@ msgstr ""
msgid "Coverage Fuzzing" msgid "Coverage Fuzzing"
msgstr "" msgstr ""
msgid "Crash State"
msgstr ""
msgid "Create" msgid "Create"
msgstr "" msgstr ""
...@@ -27719,6 +27722,9 @@ msgstr "" ...@@ -27719,6 +27722,9 @@ msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}" msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
msgstr "" msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about Coverage Fuzzing %{linkEndTag}"
msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}" msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
msgstr "" msgstr ""
...@@ -27794,6 +27800,12 @@ msgstr "" ...@@ -27794,6 +27800,12 @@ msgstr ""
msgid "ciReport|Coverage Fuzzing" msgid "ciReport|Coverage Fuzzing"
msgstr "" msgstr ""
msgid "ciReport|Coverage Fuzzing Title"
msgstr ""
msgid "ciReport|Coverage fuzzing"
msgstr ""
msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually." msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually."
msgstr "" 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