Commit d88b3b5d authored by Lukas 'Eipi' Eipert's avatar Lukas 'Eipi' Eipert Committed by Mark Florian

Removed deprecated Dependency Scanning from MR widget

Removed old code paths related to Frontend parsing of security scanning
results, see: https://gitlab.com/groups/gitlab-org/-/epics/1425
parent bcf1d813
......@@ -72,10 +72,8 @@ export default {
},
shouldRenderSecurityReport() {
return (
(this.mr.sast && this.mr.sast.head_path) ||
(this.mr.sastContainer && this.mr.sastContainer.head_path) ||
(this.mr.dast && this.mr.dast.head_path) ||
(this.mr.dependencyScanning && this.mr.dependencyScanning.head_path)
this.mr.enabledSecurityReports &&
Object.values(this.mr.enabledSecurityReports).some(isReportEnabled => isReportEnabled)
);
},
codequalityText() {
......@@ -304,17 +302,9 @@ export default {
:target-branch="mr.targetBranch"
:base-blob-path="mr.baseBlobPath"
:enabled-reports="mr.enabledSecurityReports"
:sast-head-path="mr.sast.head_path"
:sast-base-path="mr.sast.base_path"
:sast-help-path="mr.sastHelp"
:dast-head-path="mr.dast.head_path"
:dast-base-path="mr.dast.base_path"
:dast-help-path="mr.dastHelp"
:sast-container-head-path="mr.sastContainer.head_path"
:sast-container-base-path="mr.sastContainer.base_path"
:sast-container-help-path="mr.sastContainerHelp"
:dependency-scanning-head-path="mr.dependencyScanning.head_path"
:dependency-scanning-base-path="mr.dependencyScanning.base_path"
:dependency-scanning-help-path="mr.dependencyScanningHelp"
:vulnerability-feedback-path="mr.vulnerabilityFeedbackPath"
:vulnerability-feedback-help-path="mr.vulnerabilityFeedbackHelpPath"
......
......@@ -10,10 +10,6 @@ export default class MergeRequestStore extends CEMergeRequestStore {
const blobPath = data.blob_path || {};
this.headBlobPath = blobPath.head_path || '';
this.baseBlobPath = blobPath.base_path || '';
this.sast = data.sast || {};
this.sastContainer = data.sast_container || {};
this.dast = data.dast || {};
this.dependencyScanning = data.dependency_scanning || {};
this.sastHelp = data.sast_help_path;
this.sastContainerHelp = data.sast_container_help_path;
this.dastHelp = data.dast_help_path;
......
......@@ -46,46 +46,6 @@ export default {
required: false,
default: null,
},
sastHeadPath: {
type: String,
required: false,
default: null,
},
sastBasePath: {
type: String,
required: false,
default: null,
},
dastHeadPath: {
type: String,
required: false,
default: null,
},
dastBasePath: {
type: String,
required: false,
default: null,
},
sastContainerHeadPath: {
type: String,
required: false,
default: null,
},
sastContainerBasePath: {
type: String,
required: false,
default: null,
},
dependencyScanningHeadPath: {
type: String,
required: false,
default: null,
},
dependencyScanningBasePath: {
type: String,
required: false,
default: null,
},
sastHelpPath: {
type: String,
required: false,
......@@ -182,21 +142,16 @@ export default {
return `${this.pipelinePath}/security`;
},
hasContainerScanningReports() {
const type = 'containerScanning';
if (this.isMergeRequestReportApiEnabled(type)) {
return this.enabledReports[type];
}
const { head, diffEndpoint } = this.sastContainer.paths;
return Boolean(head || diffEndpoint);
return this.enabledReports.containerScanning;
},
hasDependencyScanningReports() {
return this.hasReportsType('dependencyScanning');
return this.enabledReports.dependencyScanning;
},
hasDastReports() {
return this.hasReportsType('dast');
return this.enabledReports.dast;
},
hasSastReports() {
return this.hasReportsType('sast');
return this.enabledReports.sast;
},
subHeadingText() {
const mrDivergedCommitsCount =
......@@ -236,70 +191,36 @@ export default {
this.setCanCreateIssuePermission(this.canCreateIssue);
this.setCanCreateFeedbackPermission(this.canCreateFeedback);
const sastDiffEndpoint = gl && gl.mrWidgetData && gl.mrWidgetData.sast_comparison_path;
// eslint-disable-next-line camelcase
const sastDiffEndpoint = gl?.mrWidgetData?.sast_comparison_path;
if (this.isMergeRequestReportApiEnabled('sast') && sastDiffEndpoint && this.hasSastReports) {
if (sastDiffEndpoint && this.hasSastReports) {
this.setSastDiffEndpoint(sastDiffEndpoint);
this.fetchSastDiff();
} else if (this.sastHeadPath) {
this.setSastHeadPath(this.sastHeadPath);
if (this.sastBasePath) {
this.setSastBasePath(this.sastBasePath);
}
this.fetchSastReports();
}
const sastContainerDiffEndpoint =
gl && gl.mrWidgetData && gl.mrWidgetData.container_scanning_comparison_path;
// eslint-disable-next-line camelcase
const containerScanningDiffEndpoint = gl?.mrWidgetData?.container_scanning_comparison_path;
if (
this.isMergeRequestReportApiEnabled('containerScanning') &&
sastContainerDiffEndpoint &&
this.hasContainerScanningReports
) {
this.setSastContainerDiffEndpoint(sastContainerDiffEndpoint);
if (containerScanningDiffEndpoint && this.hasContainerScanningReports) {
this.setSastContainerDiffEndpoint(containerScanningDiffEndpoint);
this.fetchSastContainerDiff();
} else if (this.sastContainerHeadPath) {
this.setSastContainerHeadPath(this.sastContainerHeadPath);
if (this.sastContainerBasePath) {
this.setSastContainerBasePath(this.sastContainerBasePath);
}
this.fetchSastContainerReports();
}
const dastDiffEndpoint = gl && gl.mrWidgetData && gl.mrWidgetData.dast_comparison_path;
// eslint-disable-next-line camelcase
const dastDiffEndpoint = gl?.mrWidgetData?.dast_comparison_path;
if (this.isMergeRequestReportApiEnabled('dast') && dastDiffEndpoint && this.hasDastReports) {
if (dastDiffEndpoint && this.hasDastReports) {
this.setDastDiffEndpoint(dastDiffEndpoint);
this.fetchDastDiff();
} else if (this.dastHeadPath) {
this.setDastHeadPath(this.dastHeadPath);
if (this.dastBasePath) {
this.setDastBasePath(this.dastBasePath);
}
this.fetchDastReports();
}
const dependencyScanningDiffEndpoint =
gl && gl.mrWidgetData && gl.mrWidgetData.dependency_scanning_comparison_path;
// eslint-disable-next-line camelcase
const dependencyScanningDiffEndpoint = gl?.mrWidgetData?.dependency_scanning_comparison_path;
if (
this.isMergeRequestReportApiEnabled('dependencyScanning') &&
dependencyScanningDiffEndpoint &&
this.hasDependencyScanningReports
) {
if (dependencyScanningDiffEndpoint && this.hasDependencyScanningReports) {
this.setDependencyScanningDiffEndpoint(dependencyScanningDiffEndpoint);
this.fetchDependencyScanningDiff();
} else if (this.dependencyScanningHeadPath) {
this.setDependencyScanningHeadPath(this.dependencyScanningHeadPath);
if (this.dependencyScanningBasePath) {
this.setDependencyScanningBasePath(this.dependencyScanningBasePath);
}
this.fetchDependencyScanningReports();
}
},
methods: {
......@@ -308,15 +229,6 @@ export default {
'setHeadBlobPath',
'setBaseBlobPath',
'setSourceBranch',
'setSastContainerHeadPath',
'setSastContainerBasePath',
'setDastHeadPath',
'setDastBasePath',
'setDependencyScanningHeadPath',
'setDependencyScanningBasePath',
'fetchSastContainerReports',
'fetchDastReports',
'fetchDependencyScanningReports',
'setVulnerabilityFeedbackPath',
'setVulnerabilityFeedbackHelpPath',
'setCreateVulnerabilityFeedbackIssuePath',
......@@ -344,22 +256,9 @@ export default {
'setDastDiffEndpoint',
]),
...mapActions('sast', {
setSastHeadPath: 'setHeadPath',
setSastBasePath: 'setBasePath',
setSastDiffEndpoint: 'setDiffEndpoint',
fetchSastReports: 'fetchReports',
fetchSastDiff: 'fetchDiff',
}),
isMergeRequestReportApiEnabled(type) {
return Boolean(this.glFeatures[`${type}MergeRequestReportApi`]);
},
hasReportsType(type) {
if (this.isMergeRequestReportApiEnabled(type)) {
return this.enabledReports[type];
}
const { head, diffEndpoint } = this[type].paths;
return Boolean(head || diffEndpoint);
},
},
};
</script>
......
......@@ -119,10 +119,10 @@ export const anyReportHasError = state =>
state.dependencyScanning.hasError;
export const noBaseInAllReports = state =>
!state.sast.paths.base &&
!state.dast.paths.base &&
!state.sastContainer.paths.base &&
!state.dependencyScanning.paths.base;
!state.sast.hasBaseReport &&
!state.dast.hasBaseReport &&
!state.sastContainer.hasBaseReport &&
!state.dependencyScanning.hasBaseReport;
export const anyReportHasIssues = state =>
state.sast.newIssues.length > 0 ||
......
......@@ -62,12 +62,14 @@ export default {
[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) {
......
......@@ -12,4 +12,5 @@ export default () => ({
resolvedIssues: [],
allIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
});
......@@ -109,12 +109,14 @@ export default {
[types.RECEIVE_SAST_CONTAINER_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.sastContainer, 'isLoading', false);
Vue.set(state.sastContainer, 'newIssues', added);
Vue.set(state.sastContainer, 'resolvedIssues', fixed);
Vue.set(state.sastContainer, 'allIssues', existing);
Vue.set(state.sastContainer, 'baseReportOutofDate', baseReportOutofDate);
Vue.set(state.sastContainer, 'hasBaseReport', hasBaseReport);
},
[types.RECEIVE_SAST_CONTAINER_DIFF_ERROR](state) {
......@@ -167,12 +169,14 @@ export default {
[types.RECEIVE_DAST_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.dast, 'isLoading', false);
Vue.set(state.dast, 'newIssues', added);
Vue.set(state.dast, 'resolvedIssues', fixed);
Vue.set(state.dast, 'allIssues', existing);
Vue.set(state.dast, 'baseReportOutofDate', baseReportOutofDate);
Vue.set(state.dast, 'hasBaseReport', hasBaseReport);
},
[types.RECEIVE_DAST_DIFF_ERROR](state) {
......@@ -256,12 +260,14 @@ export default {
[types.RECEIVE_DEPENDENCY_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.dependencyScanning, 'isLoading', false);
Vue.set(state.dependencyScanning, 'newIssues', added);
Vue.set(state.dependencyScanning, 'resolvedIssues', fixed);
Vue.set(state.dependencyScanning, 'allIssues', existing);
Vue.set(state.dependencyScanning, 'baseReportOutofDate', baseReportOutofDate);
Vue.set(state.dependencyScanning, 'hasBaseReport', hasBaseReport);
},
[types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR](state) {
......
......@@ -29,6 +29,7 @@ export default () => ({
newIssues: [],
resolvedIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
},
dast: {
paths: {
......@@ -43,6 +44,7 @@ export default () => ({
newIssues: [],
resolvedIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
},
dependencyScanning: {
......@@ -59,6 +61,7 @@ export default () => ({
resolvedIssues: [],
allIssues: [],
baseReportOutofDate: false,
hasBaseReport: false,
},
modal: {
......
const libTiffCveFingerprint = 'e503c23a7776dd5e2c35ac63c8cce6b6468be9ba';
const libTiffCveFingerprint2 = '29af456d1107381bc2511646e2ae488ddfe9a8ed';
export const baseIssues = [
{
categories: ['Security'],
check_name: 'Insecure Dependency',
description: 'Insecure Dependency',
location: {
path: 'Gemfile.lock',
lines: {
begin: 22,
end: 22,
},
},
fingerprint: 'ca2e59451e98ae60ba2f54e3857c50e5',
},
{
categories: ['Security'],
check_name: 'Insecure Dependency',
description: 'Insecure Dependency',
location: {
path: 'Gemfile.lock',
lines: {
begin: 21,
end: 21,
},
},
fingerprint: 'ca2354534dee94ae60ba2f54e3857c50e5',
},
];
export const sastParsedIssues = [
{
title: 'Arbitrary file existence disclosure in Action Pack',
......@@ -186,187 +157,6 @@ export const sastIssuesBase = [
},
];
export const parsedSastIssuesStore = [
{
tool: 'bundler_audit',
message: 'Arbitrary file existence disclosure in Action Pack',
cve: 'CVE-2014-7829',
solution: 'upgrade to ~> 3.2.21, ~> 4.0.11.1, ~> 4.0.12, ~> 4.1.7.1, >= 4.1.8',
title: 'Arbitrary file existence disclosure in Action Pack',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock#L5-10',
category: 'sast',
project_fingerprint: 'f55331d66fd4f3bfb4237d48e9c9fa8704bd33c6',
location: {
file: 'Gemfile.lock',
start_line: 5,
end_line: 10,
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/rMTQy4oRCGk',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2014-7829',
value: 'CVE-2014-7829',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-7829',
},
],
},
{
tool: 'bundler_audit',
message: 'Possible Information Leak Vulnerability in Action View',
cve: 'CVE-2016-0752',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
title: 'Possible Information Leak Vulnerability in Action View',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock',
category: 'sast',
project_fingerprint: 'a6b61a2eba59071178d5899b26dd699fb880de1e',
location: {
file: 'Gemfile.lock',
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2016-0752',
value: 'CVE-2016-0752',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0752',
},
],
},
{
tool: 'bundler_audit',
message: 'Possible Object Leak and Denial of Service attack in Action Pack',
cve: 'CVE-2016-0751',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
title: 'Possible Object Leak and Denial of Service attack in Action Pack',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock',
category: 'sast',
project_fingerprint: '830f85e5fb011408bab365eb809cd97a45b0aa17',
location: {
file: 'Gemfile.lock',
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/9oLY_FCzvoc',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2016-0751',
value: 'CVE-2016-0751',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0751',
},
],
},
];
export const parsedSastIssuesHead = [
{
tool: 'bundler_audit',
message: 'Arbitrary file existence disclosure in Action Pack',
cve: 'CVE-2014-7829',
solution: 'upgrade to ~> 3.2.21, ~> 4.0.11.1, ~> 4.0.12, ~> 4.1.7.1, >= 4.1.8',
title: 'Arbitrary file existence disclosure in Action Pack',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock#L5-10',
category: 'sast',
project_fingerprint: 'f55331d66fd4f3bfb4237d48e9c9fa8704bd33c6',
location: {
file: 'Gemfile.lock',
start_line: 5,
end_line: 10,
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/rMTQy4oRCGk',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2014-7829',
value: 'CVE-2014-7829',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-7829',
},
],
},
{
tool: 'bundler_audit',
message: 'Possible Object Leak and Denial of Service attack in Action Pack',
cve: 'CVE-2016-0751',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
title: 'Possible Object Leak and Denial of Service attack in Action Pack',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock',
category: 'sast',
project_fingerprint: '830f85e5fb011408bab365eb809cd97a45b0aa17',
location: {
file: 'Gemfile.lock',
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/9oLY_FCzvoc',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2016-0751',
value: 'CVE-2016-0751',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0751',
},
],
},
];
export const parsedSastBaseStore = [
{
title: 'Test Information Leak Vulnerability in Action View',
tool: 'bundler_audit',
message: 'Test Information Leak Vulnerability in Action View',
cve: 'CVE-2016-9999',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock#L5-10',
category: 'sast',
project_fingerprint: '3f5608c99f0c7442ba59bc6c0c1864d0000f8e1a',
location: {
file: 'Gemfile.lock',
start_line: 5,
end_line: 10,
},
links: [
{
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
},
],
identifiers: [
{
type: 'CVE',
name: 'CVE-2016-9999',
value: 'CVE-2016-9999',
link: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-9999',
},
],
},
];
export const dependencyScanningIssuesOld = [
{
tool: 'bundler_audit',
......@@ -706,21 +496,6 @@ export const parsedSastContainerBaseStore = [
},
];
export const allIssuesParsed = [
{
title: 'Possible Information Leak Vulnerability in Action View',
tool: 'bundler_audit',
message: 'Possible Information Leak Vulnerability in Action View',
url: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
cve: 'CVE-2016-0752',
file: 'Gemfile.lock',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
path: 'Gemfile.lock',
urlPath: 'path/Gemfile.lock',
},
];
export const dockerReport = {
image: 'registry.example.com/example/master:1234',
unapproved: ['CVE-2017-12944', 'CVE-2017-16232'],
......@@ -1287,103 +1062,6 @@ export const parsedDastNewIssues = [
},
];
/**
* SAST report API response for no added & fixed issues but with security issues
*/
export const sastHeadAllIssues = [
{
cve: 'CVE-2014-7829',
tool: 'retire',
url: 'https://github.com/jquery/jquery/issues/2432',
file: '/builds/gonzoyumo/test-package-lock/node_modules/tinycolor2/demo/jquery-1.9.1.js',
priority: 'medium',
message: '3rd party CORS request may execute',
},
{
cve: 'CVE-2014-7828',
tool: 'retire',
url: 'https://bugs.jquery.com/ticket/11974',
file: '/builds/gonzoyumo/test-package-lock/node_modules/tinycolor2/demo/jquery-1.9.1.js',
priority: 'medium',
message: 'parseHTML() executes scripts in event handlers',
},
{
cve: 'CVE-2014-7827',
tool: 'retire',
url: 'https://nodesecurity.io/advisories/146',
priority: 'high',
message: 'growl_command-injection',
},
{
cve: 'CVE-2014-7826',
tool: 'retire',
url: 'https://nodesecurity.io/advisories/146',
priority: 'high',
message: 'growl_command-injection',
},
];
export const sastBaseAllIssues = [
{
cve: 'CVE-2014-7829',
tool: 'gemnasium',
message: 'Command Injection for growl',
url: 'https://github.com/tj/node-growl/pull/61',
file: 'package-lock.json',
},
{
cve: 'CVE-2014-7828',
tool: 'gemnasium',
message: 'Regular Expression Denial of Service for tough-cookie',
url: 'https://github.com/salesforce/tough-cookie/issues/92',
file: 'package-lock.json',
},
{
cve: 'CVE-2014-7827',
tool: 'gemnasium',
message: 'Regular Expression Denial of Service for string',
url: 'https://github.com/jprichardson/string.js/issues/212',
file: 'package-lock.json',
},
{
cve: 'CVE-2014-7826',
tool: 'gemnasium',
message: 'Regular Expression Denial of Service for debug',
url: 'https://nodesecurity.io/advisories/534',
file: 'package-lock.json',
},
{
cve: 'CVE-2014-7825',
tool: 'retire',
message: '3rd party CORS request may execute',
url: 'https://github.com/jquery/jquery/issues/2432',
file: '/code/node_modules/tinycolor2/demo/jquery-1.9.1.js',
priority: 'medium',
},
{
cve: 'CVE-2014-7824',
tool: 'retire',
message: 'parseHTML() executes scripts in event handlers',
url: 'https://bugs.jquery.com/ticket/11974',
file: '/code/node_modules/tinycolor2/demo/jquery-1.9.1.js',
priority: 'medium',
},
{
cve: 'CVE-2014-7823',
tool: 'retire',
message: 'growl_command-injection',
url: 'https://nodesecurity.io/advisories/146',
priority: 'high',
},
{
cve: 'CVE-2014-7822',
tool: 'retire',
message: 'growl_command-injection',
url: 'https://nodesecurity.io/advisories/146',
priority: 'high',
},
];
export const sastFeedbacks = [
{
id: 3,
......@@ -1483,3 +1161,336 @@ export const containerScanningFeedbacks = [
project_fingerprint: libTiffCveFingerprint2,
},
];
export const mockFindings = [
{
id: null,
report_type: 'dependency_scanning',
name: 'Cross-site Scripting in serialize-javascript',
severity: 'unknown',
confidence: 'undefined',
scanner: { external_id: 'gemnasium', name: 'Gemnasium' },
identifiers: [
{
external_type: 'gemnasium',
external_id: '58caa017-9a9a-46d6-bab2-ec930f46833c',
name: 'Gemnasium-58caa017-9a9a-46d6-bab2-ec930f46833c',
url:
'https://deps.sec.gitlab.com/packages/npm/serialize-javascript/versions/1.7.0/advisories',
},
{
external_type: 'cve',
external_id: 'CVE-2019-16769',
name: 'CVE-2019-16769',
url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16769',
},
],
project_fingerprint: '09df9f4d11c8deb93d81bdcc39f7667b44143298',
create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_merge_request_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
project: {
id: 7071551,
name: 'gitlab-ui',
full_path: '/gitlab-org/gitlab-ui',
full_name: 'GitLab.org / gitlab-ui',
},
dismissal_feedback: null,
issue_feedback: null,
merge_request_feedback: null,
description:
'The serialize-javascript npm package is vulnerable to Cross-site Scripting (XSS). It does not properly mitigate against unsafe characters in serialized regular expressions. If serialized data of regular expression objects are used in an environment other than Node.js, it is affected by this vulnerability.',
links: [{ url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-16769' }],
location: {
file: 'yarn.lock',
dependency: { package: { name: 'serialize-javascript' }, version: '1.7.0' },
},
remediations: [null],
solution: 'Upgrade to version 2.1.1 or above.',
state: 'opened',
blob_path: '/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/yarn.lock',
},
{
id: null,
report_type: 'dependency_scanning',
name: '3rd party CORS request may execute in jquery',
severity: 'medium',
confidence: 'undefined',
scanner: { external_id: 'retire.js', name: 'Retire.js' },
identifiers: [
{
external_type: 'cve',
external_id: 'CVE-2015-9251',
name: 'CVE-2015-9251',
url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9251',
},
],
project_fingerprint: '1ecd3b214cf39c0b9ad23a0a9679778d7cf55876',
create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_merge_request_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
project: {
id: 7071551,
name: 'gitlab-ui',
full_path: '/gitlab-org/gitlab-ui',
full_name: 'GitLab.org / gitlab-ui',
},
dismissal_feedback: {
id: 2528,
created_at: '2019-08-26T12:30:32.349Z',
project_id: 7071551,
author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
comment_details: {
comment: 'This particular jQuery version appears in a test path of tinycolor2.\n',
comment_timestamp: '2019-08-26T12:30:37.610Z',
comment_author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
},
pipeline: { id: 78375355, path: '/gitlab-org/gitlab-ui/pipelines/78375355' },
destroy_vulnerability_feedback_dismissal_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback/2528',
category: 'dependency_scanning',
feedback_type: 'dismissal',
branch: 'leipert-dogfood-secure',
project_fingerprint: '1ecd3b214cf39c0b9ad23a0a9679778d7cf55876',
},
issue_feedback: null,
merge_request_feedback: null,
description: null,
links: [
{ url: 'https://github.com/jquery/jquery/issues/2432' },
{ url: 'http://blog.jquery.com/2016/01/08/jquery-2-2-and-1-12-released/' },
{ url: 'https://nvd.nist.gov/vuln/detail/CVE-2015-9251' },
{ url: 'http://research.insecurelabs.org/jquery/test/' },
],
location: {
file: 'node_modules/tinycolor2/demo/jquery-1.9.1.js',
dependency: { package: { name: 'jquery' }, version: '1.9.1' },
},
remediations: [null],
solution: null,
state: 'dismissed',
blob_path:
'/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/tinycolor2/demo/jquery-1.9.1.js',
},
{
id: null,
report_type: 'dependency_scanning',
name:
'jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution in jquery',
severity: 'low',
confidence: 'undefined',
scanner: { external_id: 'retire.js', name: 'Retire.js' },
identifiers: [
{
external_type: 'cve',
external_id: 'CVE-2019-11358',
name: 'CVE-2019-11358',
url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358',
},
],
project_fingerprint: 'aeb4b2442d92d0ccf7023f0c220bda8b4ba910e3',
create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_merge_request_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
project: {
id: 7071551,
name: 'gitlab-ui',
full_path: '/gitlab-org/gitlab-ui',
full_name: 'GitLab.org / gitlab-ui',
},
dismissal_feedback: {
id: 4197,
created_at: '2019-11-14T11:03:18.472Z',
project_id: 7071551,
author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
comment_details: {
comment:
'This is a false positive, as it just part of some documentation assets of sass-true.',
comment_timestamp: '2019-11-14T11:03:18.464Z',
comment_author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
},
destroy_vulnerability_feedback_dismissal_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback/4197',
category: 'dependency_scanning',
feedback_type: 'dismissal',
branch: null,
project_fingerprint: 'aeb4b2442d92d0ccf7023f0c220bda8b4ba910e3',
},
issue_feedback: null,
merge_request_feedback: null,
description: null,
links: [
{ url: 'https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/' },
{ url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-11358' },
{ url: 'https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b' },
],
location: {
file: 'node_modules/sass-true/docs/assets/webpack/common.min.js',
dependency: { package: { name: 'jquery' }, version: '3.3.1' },
},
remediations: [null],
solution: null,
state: 'dismissed',
blob_path:
'/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/sass-true/docs/assets/webpack/common.min.js',
},
{
id: null,
report_type: 'dependency_scanning',
name:
'jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution in jquery',
severity: 'low',
confidence: 'undefined',
scanner: { external_id: 'retire.js', name: 'Retire.js' },
identifiers: [
{
external_type: 'cve',
external_id: 'CVE-2019-11358',
name: 'CVE-2019-11358',
url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358',
},
],
project_fingerprint: 'eb86aa13eb9d897a083ead6e134aa78aa9cadd52',
create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_merge_request_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback',
create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
project: {
id: 7071551,
name: 'gitlab-ui',
full_path: '/gitlab-org/gitlab-ui',
full_name: 'GitLab.org / gitlab-ui',
},
dismissal_feedback: {
id: 2527,
created_at: '2019-08-26T12:29:43.624Z',
project_id: 7071551,
author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
comment_details: {
comment: 'This particular jQuery version appears in a test path of tinycolor2.',
comment_timestamp: '2019-08-26T12:30:14.840Z',
comment_author: {
id: 181229,
name: "Lukas 'Eipi' Eipert",
username: 'leipert',
state: 'active',
avatar_url:
'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
web_url: 'https://gitlab.com/leipert',
status_tooltip_html: null,
path: '/leipert',
},
},
pipeline: { id: 78375355, path: '/gitlab-org/gitlab-ui/pipelines/78375355' },
destroy_vulnerability_feedback_dismissal_path:
'/gitlab-org/gitlab-ui/vulnerability_feedback/2527',
category: 'dependency_scanning',
feedback_type: 'dismissal',
branch: 'leipert-dogfood-secure',
project_fingerprint: 'eb86aa13eb9d897a083ead6e134aa78aa9cadd52',
},
issue_feedback: null,
merge_request_feedback: null,
description: null,
links: [
{ url: 'https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/' },
{ url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-11358' },
{ url: 'https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b' },
],
location: {
file: 'node_modules/tinycolor2/demo/jquery-1.9.1.js',
dependency: { package: { name: 'jquery' }, version: '1.9.1' },
},
remediations: [null],
solution: null,
state: 'dismissed',
blob_path:
'/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/tinycolor2/demo/jquery-1.9.1.js',
},
];
export const sastDiffSuccessMock = {
added: [mockFindings[0]],
fixed: [mockFindings[1], mockFindings[2]],
existing: [mockFindings[3]],
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',
};
export const dastDiffSuccessMock = {
added: [mockFindings[0]],
fixed: [mockFindings[1], 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',
};
export const containerScanningDiffSuccessMock = {
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',
};
export const dependencyScanningDiffSuccessMock = {
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',
};
......@@ -525,8 +525,8 @@ describe('Security reports getters', () => {
expect(noBaseInAllReports(state)).toEqual(true);
});
it('returns false when any of the reports has base', () => {
state.dast.paths.base = BASE_PATH;
it('returns false when any of the reports has a base', () => {
state.dast.hasBaseReport = true;
expect(noBaseInAllReports(state)).toEqual(false);
});
......
......@@ -15,20 +15,21 @@ import mockData, {
parsedHeadIssues,
} from 'ee_spec/vue_mr_widget/mock_data';
import {
sastIssues,
sastIssuesBase,
dockerReport,
dockerBaseReport,
dast,
dastBase,
sastBaseAllIssues,
sastHeadAllIssues,
} from 'ee_spec/vue_shared/security_reports/mock_data';
import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
import { MTWPS_MERGE_STRATEGY, MT_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
import {
sastDiffSuccessMock,
dastDiffSuccessMock,
containerScanningDiffSuccessMock,
dependencyScanningDiffSuccessMock,
} from 'ee_spec/vue_shared/security_reports/mock_data';
const SAST_SELECTOR = '.js-sast-widget';
const DAST_SELECTOR = '.js-dast-widget';
const DEPENDENCY_SCANNING_SELECTOR = '.js-dependency-scanning-widget';
const CONTAINER_SCANNING_SELECTOR = '.js-sast-container';
describe('ee merge request widget options', () => {
let vm;
......@@ -60,37 +61,37 @@ describe('ee merge request widget options', () => {
gon.features = {};
});
describe('security widget', () => {
const VULNERABILITY_FEEDBACK_ENDPOINT = 'vulnerability_feedback_path';
describe('SAST', () => {
const SAST_DIFF_ENDPOINT = 'sast_diff_endpoint';
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
sast: {
base_path: 'path.json',
head_path: 'head_path.json',
enabled_reports: {
sast: true,
},
vulnerability_feedback_path: 'vulnerability_feedback_path',
sast_comparison_path: SAST_DIFF_ENDPOINT,
vulnerability_feedback_path: VULNERABILITY_FEEDBACK_ENDPOINT,
};
});
describe('when it is loading', () => {
it('should render loading indicator', () => {
mock.onGet('path.json').reply(200, sastBaseAllIssues);
mock.onGet('head_path.json').reply(200, sastHeadAllIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, sastDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
expect(vm.$el.querySelector('.js-sast-widget').textContent.trim()).toContain(
'SAST is loading',
);
expect(vm.$el.querySelector(SAST_SELECTOR).textContent.trim()).toContain('SAST is loading');
});
});
describe('with successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, sastIssuesBase);
mock.onGet('head_path.json').reply(200, sastIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, sastDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -98,32 +99,10 @@ describe('ee merge request widget options', () => {
setTimeout(() => {
expect(
removeBreakLine(
vm.$el.querySelector('.js-sast-widget .report-block-list-issue-description')
vm.$el.querySelector(`${SAST_SELECTOR} .report-block-list-issue-description`)
.textContent,
),
).toEqual('SAST detected 2 new, and 1 fixed vulnerabilities');
done();
}, 0);
});
});
describe('with full report and no added or fixed issues', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, sastBaseAllIssues);
mock.onGet('head_path.json').reply(200, sastBaseAllIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('renders no new vulnerabilities message', done => {
setTimeout(() => {
expect(
removeBreakLine(
vm.$el.querySelector('.js-sast-widget .report-block-list-issue-description')
.textContent,
),
).toEqual('SAST detected no new vulnerabilities');
).toEqual('SAST detected 1 new, and 2 fixed vulnerabilities');
done();
}, 0);
});
......@@ -131,9 +110,8 @@ describe('ee merge request widget options', () => {
describe('with empty successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, []);
mock.onGet('head_path.json').reply(200, []);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, { added: [], existing: [] });
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -142,7 +120,7 @@ describe('ee merge request widget options', () => {
setTimeout(() => {
expect(
removeBreakLine(
vm.$el.querySelector('.js-sast-widget .report-block-list-issue-description')
vm.$el.querySelector(`${SAST_SELECTOR} .report-block-list-issue-description`)
.textContent,
).trim(),
).toEqual('SAST detected no vulnerabilities');
......@@ -153,16 +131,15 @@ describe('ee merge request widget options', () => {
describe('with failed request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(500, []);
mock.onGet('head_path.json').reply(500, []);
mock.onGet('vulnerability_feedback_path').reply(500, []);
mock.onGet(SAST_DIFF_ENDPOINT).reply(500, {});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(500, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('should render error indicator', done => {
setTimeout(() => {
expect(removeBreakLine(vm.$el.querySelector('.js-sast-widget').textContent)).toContain(
expect(removeBreakLine(vm.$el.querySelector(SAST_SELECTOR).textContent)).toContain(
'SAST: Loading resulted in an error',
);
done();
......@@ -171,37 +148,37 @@ describe('ee merge request widget options', () => {
});
});
describe('dependency scanning widget', () => {
describe('Dependency Scanning', () => {
const DEPENDENCY_SCANNING_ENDPOINT = 'dependency_scanning_diff_endpoint';
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
dependency_scanning: {
base_path: 'path.json',
head_path: 'head_path.json',
enabled_reports: {
dependency_scanning: true,
},
vulnerability_feedback_path: 'vulnerability_feedback_path',
dependency_scanning_comparison_path: DEPENDENCY_SCANNING_ENDPOINT,
vulnerability_feedback_path: VULNERABILITY_FEEDBACK_ENDPOINT,
};
});
describe('when it is loading', () => {
it('should render loading indicator', () => {
mock.onGet('path.json').reply(200, sastIssuesBase);
mock.onGet('head_path.json').reply(200, sastIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DEPENDENCY_SCANNING_ENDPOINT).reply(200, dependencyScanningDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
expect(
removeBreakLine(vm.$el.querySelector('.js-dependency-scanning-widget').textContent),
removeBreakLine(vm.$el.querySelector(DEPENDENCY_SCANNING_SELECTOR).textContent),
).toContain('Dependency scanning is loading');
});
});
describe('with successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, sastIssuesBase);
mock.onGet('head_path.json').reply(200, sastIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DEPENDENCY_SCANNING_ENDPOINT).reply(200, dependencyScanningDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -211,7 +188,7 @@ describe('ee merge request widget options', () => {
expect(
removeBreakLine(
vm.$el.querySelector(
'.js-dependency-scanning-widget .report-block-list-issue-description',
`${DEPENDENCY_SCANNING_SELECTOR} .report-block-list-issue-description`,
).textContent,
),
).toEqual('Dependency scanning detected 2 new, and 1 fixed vulnerabilities');
......@@ -222,9 +199,12 @@ describe('ee merge request widget options', () => {
describe('with full report and no added or fixed issues', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, sastBaseAllIssues);
mock.onGet('head_path.json').reply(200, sastBaseAllIssues);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DEPENDENCY_SCANNING_ENDPOINT).reply(200, {
added: [],
fixed: [],
existing: [{ title: 'Mock finding' }],
});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -234,7 +214,7 @@ describe('ee merge request widget options', () => {
expect(
removeBreakLine(
vm.$el.querySelector(
'.js-dependency-scanning-widget .report-block-list-issue-description',
`${DEPENDENCY_SCANNING_SELECTOR} .report-block-list-issue-description`,
).textContent,
),
).toEqual('Dependency scanning detected no new vulnerabilities');
......@@ -245,9 +225,8 @@ describe('ee merge request widget options', () => {
describe('with empty successful request', () => {
beforeEach(() => {
mock.onGet('path.json').reply(200, []);
mock.onGet('head_path.json').reply(200, []);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DEPENDENCY_SCANNING_ENDPOINT).reply(200, { added: [], fixed: [], existing: [] });
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -257,7 +236,7 @@ describe('ee merge request widget options', () => {
expect(
removeBreakLine(
vm.$el.querySelector(
'.js-dependency-scanning-widget .report-block-list-issue-description',
`${DEPENDENCY_SCANNING_SELECTOR} .report-block-list-issue-description`,
).textContent,
),
).toEqual('Dependency scanning detected no vulnerabilities');
......@@ -278,7 +257,7 @@ describe('ee merge request widget options', () => {
it('should render error indicator', done => {
setTimeout(() => {
expect(
removeBreakLine(vm.$el.querySelector('.js-dependency-scanning-widget').textContent),
removeBreakLine(vm.$el.querySelector(DEPENDENCY_SCANNING_SELECTOR).textContent),
).toContain('Dependency scanning: Loading resulted in an error');
done();
}, 0);
......@@ -649,37 +628,37 @@ describe('ee merge request widget options', () => {
});
});
describe('sast container report', () => {
describe('Container Scanning', () => {
const CONTAINER_SCANNING_ENDPOINT = 'container_scanning';
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
sast_container: {
head_path: 'gl-sast-container.json',
base_path: 'sast-container-base.json',
enabled_reports: {
container_scanning: true,
},
vulnerability_feedback_path: 'vulnerability_feedback_path',
container_scanning_comparison_path: CONTAINER_SCANNING_ENDPOINT,
vulnerability_feedback_path: VULNERABILITY_FEEDBACK_ENDPOINT,
};
});
describe('when it is loading', () => {
it('should render loading indicator', () => {
mock.onGet('gl-sast-container.json').reply(200, dockerReport);
mock.onGet('sast-container-base.json').reply(200, dockerBaseReport);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(CONTAINER_SCANNING_ENDPOINT).reply(200, containerScanningDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
expect(removeBreakLine(vm.$el.querySelector('.js-sast-container').textContent)).toContain(
'Container scanning is loading',
);
expect(
removeBreakLine(vm.$el.querySelector(CONTAINER_SCANNING_SELECTOR).textContent),
).toContain('Container scanning is loading');
});
});
describe('with successful request', () => {
beforeEach(() => {
mock.onGet('gl-sast-container.json').reply(200, dockerReport);
mock.onGet('sast-container-base.json').reply(200, dockerBaseReport);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(CONTAINER_SCANNING_ENDPOINT).reply(200, containerScanningDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -688,10 +667,11 @@ describe('ee merge request widget options', () => {
setTimeout(() => {
expect(
removeBreakLine(
vm.$el.querySelector('.js-sast-container .report-block-list-issue-description')
.textContent,
vm.$el.querySelector(
`${CONTAINER_SCANNING_SELECTOR} .report-block-list-issue-description`,
).textContent,
),
).toEqual('Container scanning detected 1 new, and 1 fixed vulnerabilities');
).toEqual('Container scanning detected 2 new, and 1 fixed vulnerabilities');
done();
}, 0);
});
......@@ -699,16 +679,15 @@ describe('ee merge request widget options', () => {
describe('with failed request', () => {
beforeEach(() => {
mock.onGet('gl-sast-container.json').reply(500, {});
mock.onGet('sast-container-base.json').reply(500, {});
mock.onGet('vulnerability_feedback_path').reply(500, []);
mock.onGet(CONTAINER_SCANNING_ENDPOINT).reply(500, {});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(500, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('should render error indicator', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.js-sast-container').textContent.trim()).toContain(
expect(vm.$el.querySelector(CONTAINER_SCANNING_SELECTOR).textContent.trim()).toContain(
'Container scanning: Loading resulted in an error',
);
done();
......@@ -717,37 +696,35 @@ describe('ee merge request widget options', () => {
});
});
describe('dast report', () => {
describe('DAST', () => {
const DAST_ENDPOINT = 'dast_report';
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
dast: {
head_path: 'dast.json',
base_path: 'dast_base.json',
enabled_reports: {
dast: true,
},
vulnerability_feedback_path: 'vulnerability_feedback_path',
dast_comparison_path: DAST_ENDPOINT,
vulnerability_feedback_path: VULNERABILITY_FEEDBACK_ENDPOINT,
};
});
describe('when it is loading', () => {
it('should render loading indicator', () => {
mock.onGet('dast.json').reply(200, dast);
mock.onGet('dast_base.json').reply(200, dastBase);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DAST_ENDPOINT).reply(200, dastDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
expect(vm.$el.querySelector('.js-dast-widget').textContent.trim()).toContain(
'DAST is loading',
);
expect(vm.$el.querySelector(DAST_SELECTOR).textContent.trim()).toContain('DAST is loading');
});
});
describe('with successful request', () => {
beforeEach(() => {
mock.onGet('dast.json').reply(200, dast);
mock.onGet('dast_base.json').reply(200, dastBase);
mock.onGet('vulnerability_feedback_path').reply(200, []);
mock.onGet(DAST_ENDPOINT).reply(200, dastDiffSuccessMock);
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(200, []);
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
......@@ -756,9 +733,9 @@ describe('ee merge request widget options', () => {
setTimeout(() => {
expect(
vm.$el
.querySelector('.js-dast-widget .report-block-list-issue-description')
.querySelector(`${DAST_SELECTOR} .report-block-list-issue-description`)
.textContent.trim(),
).toEqual('DAST detected 1 new vulnerability');
).toEqual('DAST detected 1 new, and 2 fixed vulnerabilities');
done();
}, 0);
});
......@@ -766,16 +743,15 @@ describe('ee merge request widget options', () => {
describe('with failed request', () => {
beforeEach(() => {
mock.onGet('dast.json').reply(500, {});
mock.onGet('dast_base.json').reply(500, {});
mock.onGet('vulnerability_feedback_path').reply(500, []);
mock.onGet(DAST_ENDPOINT).reply(500, {});
mock.onGet(VULNERABILITY_FEEDBACK_ENDPOINT).reply(500, {});
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('should render error indicator', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.js-dast-widget').textContent.trim()).toContain(
expect(vm.$el.querySelector(DAST_SELECTOR).textContent.trim()).toContain(
'DAST: Loading resulted in an error',
);
done();
......@@ -1095,4 +1071,41 @@ describe('ee merge request widget options', () => {
expect(vm.service).toEqual(jasmine.objectContaining(convertObjectPropsToCamelCase(paths)));
});
});
describe('when no security reports are enabled', () => {
const noSecurityReportsEnabledCases = [
undefined,
{},
{
dast: false,
sast: false,
container_scanning: false,
dependency_scanning: false,
},
];
noSecurityReportsEnabledCases.forEach(noSecurityReportsEnabled => {
beforeEach(() => {
gl.mrWidgetData = {
...mockData,
enabled_reports: noSecurityReportsEnabled,
};
vm = mountComponent(Component, { mrData: gl.mrWidgetData });
});
it('does not render the security reports', () => {
const selectors = [
SAST_SELECTOR,
DAST_SELECTOR,
DEPENDENCY_SCANNING_SELECTOR,
CONTAINER_SCANNING_SELECTOR,
];
const securityWidgets = selectors.map(selector => vm.$el.querySelector(selector));
expect(securityWidgets).toEqual([null, null, null, null]);
});
});
});
});
......@@ -11,9 +11,9 @@ export default Object.assign({}, mockData, {
},
vulnerability_feedback_help_path: '/help/user/application_security/index',
enabled_reports: {
sast: true,
sast: false,
container_scanning: false,
dast: true,
dast: false,
dependency_scanning: false,
license_management: true,
},
......
......@@ -10,18 +10,37 @@ import { waitForMutation } from 'spec/helpers/vue_test_utils_helper';
import { trimText } from 'spec/helpers/text_helper';
import axios from '~/lib/utils/axios_utils';
import {
sastIssues,
sastIssuesBase,
dockerReport,
dockerBaseReport,
dast,
dastBase,
sastDiffSuccessMock,
dastDiffSuccessMock,
containerScanningDiffSuccessMock,
dependencyScanningDiffSuccessMock,
mockFindings,
} from './mock_data';
const CONTAINER_SCANNING_DIFF_ENDPOINT = 'container_scanning.json';
const DEPENDENCY_SCANNING_DIFF_ENDPOINT = 'dependency_scanning.json';
const DAST_DIFF_ENDPOINT = 'dast.json';
const SAST_DIFF_ENDPOINT = 'sast.json';
describe('Grouped security reports app', () => {
let wrapper;
let mock;
const props = {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
};
const createWrapper = (propsData, provide = {}) => {
wrapper = mount(GroupedSecurityReportsApp, {
propsData,
......@@ -31,6 +50,7 @@ describe('Grouped security reports app', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
});
afterEach(() => {
......@@ -42,227 +62,179 @@ describe('Grouped security reports app', () => {
mock.restore();
});
describe('with error', () => {
beforeEach(done => {
mock.onGet('sast_head.json').reply(500);
mock.onGet('sast_base.json').reply(500);
mock.onGet('dast_head.json').reply(500);
mock.onGet('dast_base.json').reply(500);
mock.onGet('sast_container_head.json').reply(500);
mock.onGet('sast_container_base.json').reply(500);
mock.onGet('dss_head.json').reply(500);
mock.onGet('dss_base.json').reply(500);
mock.onGet('vulnerability_feedback_path.json').reply(500, []);
createWrapper({
headBlobPath: 'path',
baseBlobPath: 'path',
sastHeadPath: 'sast_head.json',
sastBasePath: 'sast_base.json',
dastHeadPath: 'dast_head.json',
dastBasePath: 'dast_base.json',
sastContainerHeadPath: 'sast_container_head.json',
sastContainerBasePath: 'sast_container_base.json',
dependencyScanningHeadPath: 'dss_head.json',
dependencyScanningBasePath: 'dss_base.json',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
});
describe('all reports', () => {
const allReportProps = {
...props,
enabledReports: {
sast: true,
dast: true,
containerScanning: true,
dependencyScanning: true,
},
};
Promise.all([
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_REPORTS_ERROR}`),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_ERROR),
])
.then(done)
.catch(done.fail);
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.container_scanning_comparison_path = CONTAINER_SCANNING_DIFF_ENDPOINT;
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;
});
it('renders error state', () => {
expect(wrapper.vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning failed loading any results',
);
describe('with error', () => {
beforeEach(done => {
mock.onGet(CONTAINER_SCANNING_DIFF_ENDPOINT).reply(500);
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(500);
mock.onGet(DAST_DIFF_ENDPOINT).reply(500);
mock.onGet(SAST_DIFF_ENDPOINT).reply(500);
createWrapper(allReportProps);
Promise.all([
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_DIFF_ERROR}`),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_ERROR),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_ERROR),
])
.then(done)
.catch(done.fail);
});
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
it('renders error state', () => {
expect(wrapper.vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning failed loading any results',
);
expect(trimText(wrapper.vm.$el.textContent)).toContain('SAST: Loading resulted in an error');
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual(
'Expand',
);
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'Dependency scanning: Loading resulted in an error',
);
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'SAST: Loading resulted in an error',
);
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning: Loading resulted in an error',
);
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'Dependency scanning: Loading resulted in an error',
);
expect(wrapper.vm.$el.textContent).toContain('DAST: Loading resulted in an error');
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning: Loading resulted in an error',
);
expect(wrapper.vm.$el.textContent).toContain('DAST: Loading resulted in an error');
});
});
});
describe('while loading', () => {
beforeEach(() => {
mock.onGet('sast_head.json').reply(200, sastIssues);
mock.onGet('sast_base.json').reply(200, sastIssuesBase);
mock.onGet('dast_head.json').reply(200, dast);
mock.onGet('dast_base.json').reply(200, dastBase);
mock.onGet('sast_container_head.json').reply(200, dockerReport);
mock.onGet('sast_container_base.json').reply(200, dockerBaseReport);
mock.onGet('dss_head.json').reply(200, sastIssues);
mock.onGet('dss_base.json').reply(200, sastIssuesBase);
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
describe('while loading', () => {
beforeEach(() => {
mock.onGet(CONTAINER_SCANNING_DIFF_ENDPOINT).reply(200, {});
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(200, {});
mock.onGet(DAST_DIFF_ENDPOINT).reply(200, {});
mock.onGet(SAST_DIFF_ENDPOINT).reply(200, {});
createWrapper({
headBlobPath: 'path',
baseBlobPath: 'path',
sastHeadPath: 'sast_head.json',
sastBasePath: 'sast_base.json',
dastHeadPath: 'dast_head.json',
dastBasePath: 'dast_base.json',
sastContainerHeadPath: 'sast_container_head.json',
sastContainerBasePath: 'sast_container_base.json',
dependencyScanningHeadPath: 'dss_head.json',
dependencyScanningBasePath: 'dss_base.json',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
createWrapper(allReportProps);
});
});
it('renders loading summary text + spinner', () => {
expect(wrapper.vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning is loading',
);
it('renders loading summary text + spinner', () => {
expect(wrapper.vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning is loading',
);
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual(
'Expand',
);
expect(wrapper.vm.$el.textContent).toContain('SAST 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('DAST is loading');
expect(wrapper.vm.$el.textContent).toContain('SAST 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('DAST is loading');
});
});
});
describe('with all reports', () => {
beforeEach(done => {
mock.onGet('sast_head.json').reply(200, sastIssues);
mock.onGet('sast_base.json').reply(200, sastIssuesBase);
mock.onGet('dast_head.json').reply(200, dast);
mock.onGet('dast_base.json').reply(200, dastBase);
mock.onGet('sast_container_head.json').reply(200, dockerReport);
mock.onGet('sast_container_base.json').reply(200, dockerBaseReport);
mock.onGet('dss_head.json').reply(200, sastIssues);
mock.onGet('dss_base.json').reply(200, sastIssuesBase);
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
createWrapper({
headBlobPath: 'path',
baseBlobPath: 'path',
sastHeadPath: 'sast_head.json',
sastBasePath: 'sast_base.json',
dastHeadPath: 'dast_head.json',
dastBasePath: 'dast_base.json',
sastContainerHeadPath: 'sast_container_head.json',
sastContainerBasePath: 'sast_container_base.json',
dependencyScanningHeadPath: 'dss_head.json',
dependencyScanningBasePath: 'dss_base.json',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
describe('with successful responses', () => {
beforeEach(done => {
mock.onGet(CONTAINER_SCANNING_DIFF_ENDPOINT).reply(200, containerScanningDiffSuccessMock);
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);
createWrapper(allReportProps);
Promise.all([
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_DIFF_SUCCESS}`),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_DIFF_SUCCESS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS),
])
.then(done)
.catch(done.fail);
});
Promise.all([
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_REPORTS}`),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_REPORTS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_REPORTS),
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_REPORTS),
])
.then(done)
.catch(done.fail);
});
it('renders reports', () => {
// It's not loading
expect(wrapper.vm.$el.querySelector('.gl-spinner')).toBeNull();
it('renders reports', () => {
// It's not loading
expect(wrapper.vm.$el.querySelector('.gl-spinner')).toBeNull();
// Renders the summary text
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning detected 6 new, and 6 fixed vulnerabilities',
);
// Renders the summary text
expect(wrapper.vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Security scanning detected 6 new, and 3 fixed vulnerabilities',
);
// Renders the expand button
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual(
'Expand',
);
// Renders the expand button
expect(wrapper.vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
// Renders Sast result
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'SAST detected 1 new, and 2 fixed vulnerabilities',
);
// Renders Sast result
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'SAST detected 2 new, and 1 fixed vulnerabilities',
);
// Renders DSS result
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'Dependency scanning detected 2 new, and 1 fixed vulnerabilities',
);
// Renders DSS result
expect(trimText(wrapper.vm.$el.textContent)).toContain(
'Dependency scanning detected 2 new, and 1 fixed vulnerabilities',
);
// Renders container scanning result
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning detected 2 new, and 1 fixed vulnerabilities',
);
// Renders container scanning result
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning detected 1 new, and 1 fixed vulnerabilities',
);
// Renders DAST result
expect(wrapper.vm.$el.textContent).toContain(
'DAST detected 1 new, and 2 fixed vulnerabilities',
);
});
// Renders DAST result
expect(wrapper.vm.$el.textContent).toContain('DAST detected 1 new vulnerability');
});
it('opens modal with more information', done => {
setTimeout(() => {
wrapper.vm.$el.querySelector('.break-link').click();
it('opens modal with more information', done => {
setTimeout(() => {
wrapper.vm.$el.querySelector('.break-link').click();
Vue.nextTick(() => {
expect(wrapper.vm.$el.querySelector('.modal-title').textContent.trim()).toEqual(
mockFindings[0].name,
);
Vue.nextTick(() => {
expect(wrapper.vm.$el.querySelector('.modal-title').textContent.trim()).toEqual(
sastIssues[0].message,
);
expect(wrapper.vm.$el.querySelector('.modal-body').textContent).toContain(
mockFindings[0].solution,
);
done();
});
}, 0);
});
expect(wrapper.vm.$el.querySelector('.modal-body').textContent).toContain(
sastIssues[0].solution,
it('has the success icon for fixed vulnerabilities', done => {
setTimeout(() => {
const icon = wrapper.vm.$el.querySelector(
'.js-sast-container~.js-plain-element .ic-status_success_borderless',
);
expect(icon).not.toBeNull();
done();
});
}, 0);
});
it('has the success icon for fixed vulnerabilities', done => {
setTimeout(() => {
const icon = wrapper.vm.$el.querySelector(
'.js-sast-container~.js-plain-element .ic-status_success_borderless',
);
expect(icon).not.toBeNull();
done();
}, 0);
}, 0);
});
});
});
......@@ -284,348 +256,143 @@ describe('Grouped security reports app', () => {
});
});
describe('with the reports API enabled', () => {
describe('container scanning reports', () => {
const sastContainerEndpoint = 'sast_container.json';
const props = {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
};
const provide = {
glFeatures: {
containerScanningMergeRequestReportApi: true,
},
};
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.container_scanning_comparison_path = sastContainerEndpoint;
mock.onGet(sastContainerEndpoint).reply(200, {
added: [dockerReport.vulnerabilities[0], dockerReport.vulnerabilities[1]],
fixed: [dockerReport.vulnerabilities[2]],
});
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
});
describe('container scanning reports', () => {
beforeEach(done => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.container_scanning_comparison_path = CONTAINER_SCANNING_DIFF_ENDPOINT;
describe('with reports disabled', () => {
beforeEach(() => {
createWrapper(
{
...props,
enabledReports: {
containerScanning: false,
},
},
provide,
);
});
mock.onGet(CONTAINER_SCANNING_DIFF_ENDPOINT).reply(200, containerScanningDiffSuccessMock);
it('should not render the widget', () => {
expect(wrapper.vm.$el.querySelector('.js-sast-container')).toBeNull();
});
createWrapper({
...props,
enabledReports: {
containerScanning: true,
},
});
describe('with reports enabled', () => {
beforeEach(done => {
createWrapper(
{
...props,
enabledReports: {
containerScanning: true,
},
},
provide,
);
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
it('should set setSastContainerDiffEndpoint', () => {
expect(wrapper.vm.sastContainer.paths.diffEndpoint).toEqual(sastContainerEndpoint);
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning detected 2 new, and 1 fixed vulnerabilities',
);
});
});
waitForMutation(wrapper.vm.$store, types.RECEIVE_SAST_CONTAINER_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
describe('dependency scanning reports', () => {
const dependencyScanningEndpoint = 'dependency_scanning.json';
const props = {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
};
const provide = {
glFeatures: {
dependencyScanningMergeRequestReportApi: true,
},
};
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.dependency_scanning_comparison_path = dependencyScanningEndpoint;
it('should set setSastContainerDiffEndpoint', () => {
expect(wrapper.vm.sastContainer.paths.diffEndpoint).toEqual(CONTAINER_SCANNING_DIFF_ENDPOINT);
});
mock.onGet(dependencyScanningEndpoint).reply(200, {
added: [dockerReport.vulnerabilities[0], dockerReport.vulnerabilities[1]],
fixed: [dockerReport.vulnerabilities[2]],
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Container scanning detected 2 new, and 1 fixed vulnerabilities',
);
});
});
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
});
describe('dependency scanning reports', () => {
beforeEach(done => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.dependency_scanning_comparison_path = DEPENDENCY_SCANNING_DIFF_ENDPOINT;
describe('with reports disabled', () => {
beforeEach(() => {
createWrapper(
{
...props,
enabledReports: {
dependencyScanning: false,
},
},
provide,
);
});
mock.onGet(DEPENDENCY_SCANNING_DIFF_ENDPOINT).reply(200, dependencyScanningDiffSuccessMock);
it('should not render the widget', () => {
expect(wrapper.vm.$el.querySelector('.js-dependency-scanning-widget')).toBeNull();
});
createWrapper({
...props,
enabledReports: {
dependencyScanning: true,
},
});
describe('with reports enabled', () => {
beforeEach(done => {
createWrapper(
{
...props,
enabledReports: {
dependencyScanning: true,
},
},
provide,
);
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
it('should set setDependencyScanningDiffEndpoint', () => {
expect(wrapper.vm.dependencyScanning.paths.diffEndpoint).toEqual(
dependencyScanningEndpoint,
);
});
waitForMutation(wrapper.vm.$store, types.RECEIVE_DEPENDENCY_SCANNING_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Dependency scanning detected 2 new, and 1 fixed vulnerabilities',
);
});
});
it('should set setDependencyScanningDiffEndpoint', () => {
expect(wrapper.vm.dependencyScanning.paths.diffEndpoint).toEqual(
DEPENDENCY_SCANNING_DIFF_ENDPOINT,
);
});
describe('dast reports', () => {
const dastEndpoint = 'dast.json';
const props = {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
};
const provide = {
glFeatures: {
dastMergeRequestReportApi: true,
},
};
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Dependency scanning detected 2 new, and 1 fixed vulnerabilities',
);
});
});
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.dast_comparison_path = dastEndpoint;
describe('dast reports', () => {
beforeEach(done => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.dast_comparison_path = DAST_DIFF_ENDPOINT;
mock.onGet(dastEndpoint).reply(200, {
added: [dockerReport.vulnerabilities[0]],
fixed: [dockerReport.vulnerabilities[1], dockerReport.vulnerabilities[2]],
base_report_out_of_date: true,
});
mock
.onGet(DAST_DIFF_ENDPOINT)
.reply(200, { ...dastDiffSuccessMock, base_report_out_of_date: true });
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
createWrapper({
...props,
enabledReports: {
dast: true,
},
});
describe('with reports disabled', () => {
beforeEach(() => {
createWrapper(
{
...props,
enabledReports: {
dast: false,
},
},
provide,
);
});
it('should not render the widget', () => {
expect(wrapper.vm.$el.querySelector('.js-dast-widget')).toBeNull();
});
});
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
describe('with reports enabled', () => {
beforeEach(done => {
createWrapper(
{
...props,
enabledReports: {
dast: true,
},
},
provide,
);
it('should set setDastDiffEndpoint', () => {
expect(wrapper.vm.dast.paths.diffEndpoint).toEqual(DAST_DIFF_ENDPOINT);
});
waitForMutation(wrapper.vm.$store, types.RECEIVE_DAST_DIFF_SUCCESS)
.then(done)
.catch(done.fail);
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'DAST detected 1 new, and 2 fixed vulnerabilities',
);
});
it('should set setDastDiffEndpoint', () => {
expect(wrapper.vm.dast.paths.diffEndpoint).toEqual(dastEndpoint);
});
it('should display out of date message', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Security report is out of date. Retry the pipeline for the target branch',
);
});
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'DAST detected 1 new, and 2 fixed vulnerabilities',
);
});
describe('sast reports', () => {
beforeEach(done => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.sast_comparison_path = SAST_DIFF_ENDPOINT;
gl.mrWidgetData.diverged_commits_count = 100;
it('should display out of date message', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Security report is out of date. Retry the pipeline for the target branch',
);
});
});
});
mock
.onGet(SAST_DIFF_ENDPOINT)
.reply(200, { ...sastDiffSuccessMock, base_report_out_of_date: true });
describe('sast reports', () => {
const sastEndpoint = 'sast.json';
const props = {
headBlobPath: 'path',
baseBlobPath: 'path',
sastHelpPath: 'path',
sastContainerHelpPath: 'path',
dastHelpPath: 'path',
dependencyScanningHelpPath: 'path',
vulnerabilityFeedbackPath: 'vulnerability_feedback_path.json',
vulnerabilityFeedbackHelpPath: 'path',
pipelineId: 123,
canCreateIssue: true,
canCreateMergeRequest: true,
canDismissVulnerability: true,
createWrapper({
...props,
targetBranch: 'master',
};
const provide = {
glFeatures: {
sastMergeRequestReportApi: true,
enabledReports: {
sast: true,
},
};
beforeEach(() => {
gl.mrWidgetData = gl.mrWidgetData || {};
gl.mrWidgetData.sast_comparison_path = sastEndpoint;
gl.mrWidgetData.diverged_commits_count = 100;
mock.onGet(sastEndpoint).reply(200, {
added: [dockerReport.vulnerabilities[0]],
fixed: [dockerReport.vulnerabilities[1], dockerReport.vulnerabilities[2]],
existing: [dockerReport.vulnerabilities[2]],
base_report_out_of_date: true,
});
mock.onGet('vulnerability_feedback_path.json').reply(200, []);
});
describe('with reports disabled', () => {
beforeEach(() => {
createWrapper(
{
...props,
enabledReports: {
sast: false,
},
},
provide,
);
});
it('should not render the widget', () => {
expect(wrapper.vm.$el.querySelector('.js-sast-widget')).toBeNull();
});
});
describe('with reports enabled', () => {
beforeEach(done => {
createWrapper(
{
...props,
enabledReports: {
sast: true,
},
},
provide,
);
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_DIFF_SUCCESS}`)
.then(done)
.catch(done.fail);
});
waitForMutation(wrapper.vm.$store, `sast/${sastTypes.RECEIVE_DIFF_SUCCESS}`)
.then(done)
.catch(done.fail);
});
it('should set setSastDiffEndpoint', () => {
expect(wrapper.vm.sast.paths.diffEndpoint).toEqual(sastEndpoint);
});
it('should set setSastDiffEndpoint', () => {
expect(wrapper.vm.sast.paths.diffEndpoint).toEqual(SAST_DIFF_ENDPOINT);
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'SAST detected 1 new, and 2 fixed vulnerabilities',
);
});
it('should display the correct numbers of vulnerabilities', () => {
expect(wrapper.vm.$el.textContent).toContain(
'SAST detected 1 new, and 2 fixed vulnerabilities',
);
});
it('should display out of date message for Outdated MR ', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Security report is out of date. Please incorporate latest changes from master',
);
});
});
it('should display out of date message for Outdated MR ', () => {
expect(wrapper.vm.$el.textContent).toContain(
'Security report is out of date. Please incorporate latest changes from master',
);
});
});
});
......@@ -4,37 +4,22 @@ import * as mockData from '../../../frontend/vue_shared/security_reports/mock_da
// https://gitlab.com/gitlab-org/gitlab/merge_requests/10466#note_156218753
export const {
allIssuesParsed,
baseIssues,
containerScanningFeedbacks,
dast,
dastBase,
dastFeedbacks,
dependencyScanningFeedbacks,
dependencyScanningIssues,
dependencyScanningIssuesBase,
dependencyScanningIssuesMajor2,
dependencyScanningIssuesOld,
dockerBaseReport,
dockerNewIssues,
dockerOnlyHeadParsed,
dockerReport,
dockerReportParsed,
oldSastIssues,
parsedDast,
parsedDastNewIssues,
parsedDependencyScanningBaseStore,
parsedDependencyScanningIssuesHead,
parsedDependencyScanningIssuesStore,
parsedSastBaseStore,
parsedSastContainerBaseStore,
parsedSastIssuesHead,
parsedSastIssuesStore,
sastBaseAllIssues,
sastFeedbacks,
sastHeadAllIssues,
sastIssues,
sastIssuesBase,
sastIssuesMajor2,
sastParsedIssues,
mockFindings,
sastDiffSuccessMock,
dastDiffSuccessMock,
containerScanningDiffSuccessMock,
dependencyScanningDiffSuccessMock,
} = mockData;
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