Configure Secret Detection via an MR

This enables the ManageViaMr component for configuring Secret Detection.
It automates the creation of an MR via the GraphQL API.
parent 33dc6e4b
...@@ -6,3 +6,4 @@ filenames: ...@@ -6,3 +6,4 @@ filenames:
- ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/dast_failed_site_validations.query.graphql - ee/app/assets/javascripts/security_configuration/dast_profiles/graphql/dast_failed_site_validations.query.graphql
- app/assets/javascripts/repository/queries/blob_info.query.graphql - app/assets/javascripts/repository/queries/blob_info.query.graphql
- ee/app/assets/javascripts/security_configuration/graphql/configure_dependency_scanning.mutation.graphql - ee/app/assets/javascripts/security_configuration/graphql/configure_dependency_scanning.mutation.graphql
- ee/app/assets/javascripts/security_configuration/graphql/configure_secret_detection.mutation.graphql
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { REPORT_TYPE_DEPENDENCY_SCANNING } from '~/vue_shared/security_reports/constants'; import {
REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_SECRET_DETECTION,
} from '~/vue_shared/security_reports/constants';
import configureDependencyScanningMutation from '../graphql/configure_dependency_scanning.mutation.graphql'; import configureDependencyScanningMutation from '../graphql/configure_dependency_scanning.mutation.graphql';
import configureSecretDetectionMutation from '../graphql/configure_secret_detection.mutation.graphql';
export const SMALL = 'SMALL'; export const SMALL = 'SMALL';
export const MEDIUM = 'MEDIUM'; export const MEDIUM = 'MEDIUM';
...@@ -23,4 +27,8 @@ export const featureToMutationMap = { ...@@ -23,4 +27,8 @@ export const featureToMutationMap = {
type: 'configureDependencyScanning', type: 'configureDependencyScanning',
mutation: configureDependencyScanningMutation, mutation: configureDependencyScanningMutation,
}, },
[REPORT_TYPE_SECRET_DETECTION]: {
type: 'configureSecretDetection',
mutation: configureSecretDetectionMutation,
},
}; };
...@@ -4,6 +4,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; ...@@ -4,6 +4,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { import {
REPORT_TYPE_DAST_PROFILES, REPORT_TYPE_DAST_PROFILES,
REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_SECRET_DETECTION,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
import ManageDastProfiles from './manage_dast_profiles.vue'; import ManageDastProfiles from './manage_dast_profiles.vue';
import ManageGeneric from './manage_generic.vue'; import ManageGeneric from './manage_generic.vue';
...@@ -12,6 +13,7 @@ import ManageViaMr from './manage_via_mr.vue'; ...@@ -12,6 +13,7 @@ import ManageViaMr from './manage_via_mr.vue';
const scannerComponentMap = { const scannerComponentMap = {
[REPORT_TYPE_DAST_PROFILES]: ManageDastProfiles, [REPORT_TYPE_DAST_PROFILES]: ManageDastProfiles,
[REPORT_TYPE_DEPENDENCY_SCANNING]: ManageViaMr, [REPORT_TYPE_DEPENDENCY_SCANNING]: ManageViaMr,
[REPORT_TYPE_SECRET_DETECTION]: ManageViaMr,
}; };
export default { export default {
...@@ -23,6 +25,9 @@ export default { ...@@ -23,6 +25,9 @@ export default {
if (!this.glFeatures.secDependencyScanningUiEnable) { if (!this.glFeatures.secDependencyScanningUiEnable) {
delete scannerComponentMapCopy[REPORT_TYPE_DEPENDENCY_SCANNING]; delete scannerComponentMapCopy[REPORT_TYPE_DEPENDENCY_SCANNING];
} }
if (!this.glFeatures.secSecretDetectionUiEnable) {
delete scannerComponentMapCopy[REPORT_TYPE_SECRET_DETECTION];
}
return scannerComponentMapCopy; return scannerComponentMapCopy;
}, },
manageComponent() { manageComponent() {
......
mutation configureSecretDetection($fullPath: ID!) {
configureSecretDetection(fullPath: $fullPath) {
successPath
errors
}
}
...@@ -15,6 +15,7 @@ module EE ...@@ -15,6 +15,7 @@ module EE
before_action only: [:show] do before_action only: [:show] do
push_frontend_feature_flag(:security_auto_fix, project, default_enabled: false) push_frontend_feature_flag(:security_auto_fix, project, default_enabled: false)
push_frontend_feature_flag(:sec_dependency_scanning_ui_enable, project, default_enabled: :yaml) push_frontend_feature_flag(:sec_dependency_scanning_ui_enable, project, default_enabled: :yaml)
push_frontend_feature_flag(:sec_secret_detection_ui_enable, project, default_enabled: :yaml)
end end
before_action only: [:auto_fix] do before_action only: [:auto_fix] do
......
---
name: sec_secret_detection_ui_enable
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57869
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326049
milestone: '13.11'
type: development
group: group::static analysis
default_enabled: false
const buildConfigureDependencyScanningMock = ({ export const buildConfigureSecurityFeatureMockFactory = (mutationType) => ({
successPath = 'testSuccessPath', successPath = 'testSuccessPath',
errors = [], errors = [],
} = {}) => ({ } = {}) => ({
data: { data: {
configureDependencyScanning: { [mutationType]: {
successPath, successPath,
errors, errors,
__typename: 'ConfigureDependencyScanningPayload', __typename: `${mutationType}Payload`,
}, },
}, },
}); });
export const configureDependencyScanningSuccess = buildConfigureDependencyScanningMock();
export const configureDependencyScanningNoSuccessPath = buildConfigureDependencyScanningMock({
successPath: '',
});
export const configureDependencyScanningError = buildConfigureDependencyScanningMock({
errors: ['foo'],
});
...@@ -6,6 +6,7 @@ import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue' ...@@ -6,6 +6,7 @@ import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue'
import { import {
REPORT_TYPE_DAST_PROFILES, REPORT_TYPE_DAST_PROFILES,
REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_SECRET_DETECTION,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
import { generateFeatures } from './helpers'; import { generateFeatures } from './helpers';
...@@ -21,6 +22,7 @@ describe('ManageFeature component', () => { ...@@ -21,6 +22,7 @@ describe('ManageFeature component', () => {
provide: { provide: {
glFeatures: { glFeatures: {
secDependencyScanningUiEnable: true, secDependencyScanningUiEnable: true,
secSecretDetectionUiEnable: true,
}, },
}, },
...options, ...options,
...@@ -53,6 +55,7 @@ describe('ManageFeature component', () => { ...@@ -53,6 +55,7 @@ describe('ManageFeature component', () => {
type | expectedComponent type | expectedComponent
${REPORT_TYPE_DAST_PROFILES} | ${ManageDastProfiles} ${REPORT_TYPE_DAST_PROFILES} | ${ManageDastProfiles}
${REPORT_TYPE_DEPENDENCY_SCANNING} | ${ManageViaMr} ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${ManageViaMr}
${REPORT_TYPE_SECRET_DETECTION} | ${ManageViaMr}
${'foo'} | ${ManageGeneric} ${'foo'} | ${ManageGeneric}
`('given a $type feature', ({ type, expectedComponent }) => { `('given a $type feature', ({ type, expectedComponent }) => {
let feature; let feature;
...@@ -77,6 +80,7 @@ describe('ManageFeature component', () => { ...@@ -77,6 +80,7 @@ describe('ManageFeature component', () => {
it.each` it.each`
type | featureFlag type | featureFlag
${REPORT_TYPE_DEPENDENCY_SCANNING} | ${'secDependencyScanningUiEnable'} ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${'secDependencyScanningUiEnable'}
${REPORT_TYPE_SECRET_DETECTION} | ${'secSecretDetectionUiEnable'}
`('renders generic component for $type if $featureFlag is disabled', ({ type, featureFlag }) => { `('renders generic component for $type if $featureFlag is disabled', ({ type, featureFlag }) => {
const [feature] = generateFeatures(1, { type }); const [feature] = generateFeatures(1, { type });
createComponent({ createComponent({
......
...@@ -4,16 +4,16 @@ import Vue from 'vue'; ...@@ -4,16 +4,16 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue'; import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue';
import configureDependencyScanningMutation from 'ee/security_configuration/graphql/configure_dependency_scanning.mutation.graphql'; import configureDependencyScanningMutation from 'ee/security_configuration/graphql/configure_dependency_scanning.mutation.graphql';
import configureSecretDetectionMutation from 'ee/security_configuration/graphql/configure_secret_detection.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { REPORT_TYPE_DEPENDENCY_SCANNING } from '~/vue_shared/security_reports/constants';
import { import {
configureDependencyScanningSuccess, REPORT_TYPE_DEPENDENCY_SCANNING,
configureDependencyScanningNoSuccessPath, REPORT_TYPE_SECRET_DETECTION,
configureDependencyScanningError, } from '~/vue_shared/security_reports/constants';
} from './apollo_mocks'; import { buildConfigureSecurityFeatureMockFactory } from './apollo_mocks';
jest.mock('~/lib/utils/url_utility'); jest.mock('~/lib/utils/url_utility');
...@@ -24,13 +24,27 @@ describe('ManageViaMr component', () => { ...@@ -24,13 +24,27 @@ describe('ManageViaMr component', () => {
const findButton = () => wrapper.findComponent(GlButton); const findButton = () => wrapper.findComponent(GlButton);
const successHandler = async () => configureDependencyScanningSuccess; describe.each`
const noSuccessPathHandler = async () => configureDependencyScanningNoSuccessPath; featureName | featureType | mutation | mutationType
const errorHandler = async () => configureDependencyScanningError; ${'Dependency Scanning'} | ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${configureDependencyScanningMutation} | ${'configureDependencyScanning'}
${'Secret Detection'} | ${REPORT_TYPE_SECRET_DETECTION} | ${configureSecretDetectionMutation} | ${'configureSecretDetection'}
`('$featureType', ({ featureName, featureType, mutation, mutationType }) => {
const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(
mutationType,
);
const successHandler = async () => buildConfigureSecurityFeatureMock();
const noSuccessPathHandler = async () =>
buildConfigureSecurityFeatureMock({
successPath: '',
});
const errorHandler = async () =>
buildConfigureSecurityFeatureMock({
errors: ['foo'],
});
const pendingHandler = () => new Promise(() => {}); const pendingHandler = () => new Promise(() => {});
function createMockApolloProvider(handler) { function createMockApolloProvider(handler) {
const requestHandlers = [[configureDependencyScanningMutation, handler]]; const requestHandlers = [[mutation, handler]];
return createMockApollo(requestHandlers); return createMockApollo(requestHandlers);
} }
...@@ -41,9 +55,9 @@ describe('ManageViaMr component', () => { ...@@ -41,9 +55,9 @@ describe('ManageViaMr component', () => {
apolloProvider: mockApollo, apolloProvider: mockApollo,
propsData: { propsData: {
feature: { feature: {
name: 'Dependency Scanning', name: featureName,
configured: isFeatureConfigured, configured: isFeatureConfigured,
type: REPORT_TYPE_DEPENDENCY_SCANNING, type: featureType,
}, },
}, },
}), }),
...@@ -112,7 +126,7 @@ describe('ManageViaMr component', () => { ...@@ -112,7 +126,7 @@ describe('ManageViaMr component', () => {
describe.each` describe.each`
handler | message handler | message
${noSuccessPathHandler} | ${'Dependency Scanning merge request creation mutation failed'} ${noSuccessPathHandler} | ${`${featureName} merge request creation mutation failed`}
${errorHandler} | ${'foo'} ${errorHandler} | ${'foo'}
`('given an error response', ({ handler, message }) => { `('given an error response', ({ handler, message }) => {
beforeEach(() => { beforeEach(() => {
...@@ -127,4 +141,5 @@ describe('ManageViaMr component', () => { ...@@ -127,4 +141,5 @@ describe('ManageViaMr component', () => {
expect(findButton().props('loading')).toBe(false); expect(findButton().props('loading')).toBe(false);
}); });
}); });
});
}); });
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