From 7e9c479f7de77702622631cff2628a9c8dcbc627 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 19 Nov 2020 08:27:35 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-6-stable-ee --- .../vue_shared/security_reports/constants.js | 3 + .../security_reports/security_reports_app.vue | 26 ++++++-- .../security_reports/store/modules/sast/actions.js | 24 +++++++ .../security_reports/store/modules/sast/index.js | 10 +++ .../store/modules/sast/mutation_types.js | 4 ++ .../store/modules/sast/mutations.js | 31 +++++++++ .../security_reports/store/modules/sast/state.js | 16 +++++ .../store/modules/secret_detection/actions.js | 24 +++++++ .../store/modules/secret_detection/index.js | 10 +++ .../modules/secret_detection/mutation_types.js | 4 ++ .../store/modules/secret_detection/mutations.js | 30 +++++++++ .../store/modules/secret_detection/state.js | 16 +++++ .../vue_shared/security_reports/store/utils.js | 75 ++++++++++++++++++++++ 13 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/vue_shared/security_reports/constants.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/sast/actions.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/sast/index.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutation_types.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutations.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/sast/state.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/actions.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/index.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutation_types.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutations.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/state.js create mode 100644 app/assets/javascripts/vue_shared/security_reports/store/utils.js (limited to 'app/assets/javascripts/vue_shared/security_reports') diff --git a/app/assets/javascripts/vue_shared/security_reports/constants.js b/app/assets/javascripts/vue_shared/security_reports/constants.js new file mode 100644 index 00000000000..2f87c4e7878 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/constants.js @@ -0,0 +1,3 @@ +export const FEEDBACK_TYPE_DISMISSAL = 'dismissal'; +export const FEEDBACK_TYPE_ISSUE = 'issue'; +export const FEEDBACK_TYPE_MERGE_REQUEST = 'merge_request'; diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue index d5696e3c8cf..89253cc7116 100644 --- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue +++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue @@ -3,6 +3,7 @@ import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; import ReportSection from '~/reports/components/report_section.vue'; import { status } from '~/reports/constants'; import { s__ } from '~/locale'; +import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils'; import Flash from '~/flash'; import Api from '~/api'; @@ -52,12 +53,27 @@ export default { }); }, methods: { - checkHasSecurityReports(reportTypes) { - return Api.pipelineJobs(this.projectId, this.pipelineId).then(({ data: jobs }) => - jobs.some(({ artifacts = [] }) => + async checkHasSecurityReports(reportTypes) { + let page = 1; + while (page) { + // eslint-disable-next-line no-await-in-loop + const { data: jobs, headers } = await Api.pipelineJobs(this.projectId, this.pipelineId, { + per_page: 100, + page, + }); + + const hasSecurityReports = jobs.some(({ artifacts = [] }) => artifacts.some(({ file_type }) => reportTypes.includes(file_type)), - ), - ); + ); + + if (hasSecurityReports) { + return true; + } + + page = parseIntPagination(normalizeHeaders(headers)).nextPage; + } + + return false; }, activatePipelinesTab() { if (window.mrTabs) { diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/actions.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/actions.js new file mode 100644 index 00000000000..22a45341c51 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/actions.js @@ -0,0 +1,24 @@ +import * as types from './mutation_types'; +import { fetchDiffData } from '../../utils'; + +export const setDiffEndpoint = ({ commit }, path) => commit(types.SET_DIFF_ENDPOINT, path); + +export const requestDiff = ({ commit }) => commit(types.REQUEST_DIFF); + +export const receiveDiffSuccess = ({ commit }, response) => + commit(types.RECEIVE_DIFF_SUCCESS, response); + +export const receiveDiffError = ({ commit }, response) => + commit(types.RECEIVE_DIFF_ERROR, response); + +export const fetchDiff = ({ state, rootState, dispatch }) => { + dispatch('requestDiff'); + + return fetchDiffData(rootState, state.paths.diffEndpoint, 'sast') + .then(data => { + dispatch('receiveDiffSuccess', data); + }) + .catch(() => { + dispatch('receiveDiffError'); + }); +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/index.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/index.js new file mode 100644 index 00000000000..68c81bb4509 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/index.js @@ -0,0 +1,10 @@ +import state from './state'; +import mutations from './mutations'; +import * as actions from './actions'; + +export default { + namespaced: true, + state, + mutations, + actions, +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutation_types.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutation_types.js new file mode 100644 index 00000000000..aacec0fb679 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutation_types.js @@ -0,0 +1,4 @@ +export const RECEIVE_DIFF_SUCCESS = 'RECEIVE_DIFF_SUCCESS'; +export const RECEIVE_DIFF_ERROR = 'RECEIVE_DIFF_ERROR'; +export const REQUEST_DIFF = 'REQUEST_DIFF'; +export const SET_DIFF_ENDPOINT = 'SET_DIFF_ENDPOINT'; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutations.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutations.js new file mode 100644 index 00000000000..5f6153ca3b1 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/mutations.js @@ -0,0 +1,31 @@ +import Vue from 'vue'; +import * as types from './mutation_types'; +import { parseDiff } from '../../utils'; + +export default { + [types.SET_DIFF_ENDPOINT](state, path) { + Vue.set(state.paths, 'diffEndpoint', path); + }, + + [types.REQUEST_DIFF](state) { + state.isLoading = true; + }, + + [types.RECEIVE_DIFF_SUCCESS](state, { diff, enrichData }) { + const { added, fixed, existing } = parseDiff(diff, enrichData); + const baseReportOutofDate = diff.base_report_out_of_date || false; + const hasBaseReport = Boolean(diff.base_report_created_at); + + state.isLoading = false; + state.newIssues = added; + state.resolvedIssues = fixed; + state.allIssues = existing; + state.baseReportOutofDate = baseReportOutofDate; + state.hasBaseReport = hasBaseReport; + }, + + [types.RECEIVE_DIFF_ERROR](state) { + state.isLoading = false; + state.hasError = true; + }, +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/state.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/state.js new file mode 100644 index 00000000000..e860e3af924 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/sast/state.js @@ -0,0 +1,16 @@ +export default () => ({ + paths: { + head: null, + base: null, + diffEndpoint: null, + }, + + isLoading: false, + hasError: false, + + newIssues: [], + resolvedIssues: [], + allIssues: [], + baseReportOutofDate: false, + hasBaseReport: false, +}); diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/actions.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/actions.js new file mode 100644 index 00000000000..c9da824613d --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/actions.js @@ -0,0 +1,24 @@ +import { fetchDiffData } from '../../utils'; +import * as types from './mutation_types'; + +export const setDiffEndpoint = ({ commit }, path) => commit(types.SET_DIFF_ENDPOINT, path); + +export const requestDiff = ({ commit }) => commit(types.REQUEST_DIFF); + +export const receiveDiffSuccess = ({ commit }, response) => + commit(types.RECEIVE_DIFF_SUCCESS, response); + +export const receiveDiffError = ({ commit }, response) => + commit(types.RECEIVE_DIFF_ERROR, response); + +export const fetchDiff = ({ state, rootState, dispatch }) => { + dispatch('requestDiff'); + + return fetchDiffData(rootState, state.paths.diffEndpoint, 'secret_detection') + .then(data => { + dispatch('receiveDiffSuccess', data); + }) + .catch(() => { + dispatch('receiveDiffError'); + }); +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/index.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/index.js new file mode 100644 index 00000000000..68c81bb4509 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/index.js @@ -0,0 +1,10 @@ +import state from './state'; +import mutations from './mutations'; +import * as actions from './actions'; + +export default { + namespaced: true, + state, + mutations, + actions, +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutation_types.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutation_types.js new file mode 100644 index 00000000000..aacec0fb679 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutation_types.js @@ -0,0 +1,4 @@ +export const RECEIVE_DIFF_SUCCESS = 'RECEIVE_DIFF_SUCCESS'; +export const RECEIVE_DIFF_ERROR = 'RECEIVE_DIFF_ERROR'; +export const REQUEST_DIFF = 'REQUEST_DIFF'; +export const SET_DIFF_ENDPOINT = 'SET_DIFF_ENDPOINT'; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutations.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutations.js new file mode 100644 index 00000000000..ee943b0621c --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/mutations.js @@ -0,0 +1,30 @@ +import { parseDiff } from '~/vue_shared/security_reports/store/utils'; +import * as types from './mutation_types'; + +export default { + [types.SET_DIFF_ENDPOINT](state, path) { + state.paths.diffEndpoint = path; + }, + + [types.REQUEST_DIFF](state) { + state.isLoading = true; + }, + + [types.RECEIVE_DIFF_SUCCESS](state, { diff, enrichData }) { + const { added, fixed, existing } = parseDiff(diff, enrichData); + const baseReportOutofDate = diff.base_report_out_of_date || false; + const hasBaseReport = Boolean(diff.base_report_created_at); + + state.isLoading = false; + state.newIssues = added; + state.resolvedIssues = fixed; + state.allIssues = existing; + state.baseReportOutofDate = baseReportOutofDate; + state.hasBaseReport = hasBaseReport; + }, + + [types.RECEIVE_DIFF_ERROR](state) { + state.isLoading = false; + state.hasError = true; + }, +}; diff --git a/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/state.js b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/state.js new file mode 100644 index 00000000000..e860e3af924 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/modules/secret_detection/state.js @@ -0,0 +1,16 @@ +export default () => ({ + paths: { + head: null, + base: null, + diffEndpoint: null, + }, + + isLoading: false, + hasError: false, + + newIssues: [], + resolvedIssues: [], + allIssues: [], + baseReportOutofDate: false, + hasBaseReport: false, +}); diff --git a/app/assets/javascripts/vue_shared/security_reports/store/utils.js b/app/assets/javascripts/vue_shared/security_reports/store/utils.js new file mode 100644 index 00000000000..6e50efae741 --- /dev/null +++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js @@ -0,0 +1,75 @@ +import pollUntilComplete from '~/lib/utils/poll_until_complete'; +import axios from '~/lib/utils/axios_utils'; +import { + FEEDBACK_TYPE_DISMISSAL, + FEEDBACK_TYPE_ISSUE, + FEEDBACK_TYPE_MERGE_REQUEST, +} from '../constants'; + +export const fetchDiffData = (state, endpoint, category) => { + const requests = [pollUntilComplete(endpoint)]; + + if (state.canReadVulnerabilityFeedback) { + requests.push(axios.get(state.vulnerabilityFeedbackPath, { params: { category } })); + } + + return Promise.all(requests).then(([diffResponse, enrichResponse]) => ({ + diff: diffResponse.data, + enrichData: enrichResponse?.data ?? [], + })); +}; + +/** + * Returns given vulnerability enriched with the corresponding + * feedback (`dismissal` or `issue` type) + * @param {Object} vulnerability + * @param {Array} feedback + */ +export const enrichVulnerabilityWithFeedback = (vulnerability, feedback = []) => + feedback + .filter(fb => fb.project_fingerprint === vulnerability.project_fingerprint) + .reduce((vuln, fb) => { + if (fb.feedback_type === FEEDBACK_TYPE_DISMISSAL) { + return { + ...vuln, + isDismissed: true, + dismissalFeedback: fb, + }; + } + if (fb.feedback_type === FEEDBACK_TYPE_ISSUE && fb.issue_iid) { + return { + ...vuln, + hasIssue: true, + issue_feedback: fb, + }; + } + if (fb.feedback_type === FEEDBACK_TYPE_MERGE_REQUEST && fb.merge_request_iid) { + return { + ...vuln, + hasMergeRequest: true, + merge_request_feedback: fb, + }; + } + return vuln; + }, vulnerability); + +/** + * Generates the added, fixed, and existing vulnerabilities from the API report. + * + * @param {Object} diff The original reports. + * @param {Object} enrichData Feedback data to add to the reports. + * @returns {Object} + */ +export const parseDiff = (diff, enrichData) => { + const enrichVulnerability = vulnerability => ({ + ...enrichVulnerabilityWithFeedback(vulnerability, enrichData), + category: vulnerability.report_type, + title: vulnerability.message || vulnerability.name, + }); + + return { + added: diff.added ? diff.added.map(enrichVulnerability) : [], + fixed: diff.fixed ? diff.fixed.map(enrichVulnerability) : [], + existing: diff.existing ? diff.existing.map(enrichVulnerability) : [], + }; +}; -- cgit v1.2.3