diff options
Diffstat (limited to 'app/assets/javascripts/security_configuration')
9 files changed, 219 insertions, 64 deletions
diff --git a/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue b/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue new file mode 100644 index 00000000000..ce6a1b4888b --- /dev/null +++ b/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue @@ -0,0 +1,41 @@ +<script> +import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +export default { + components: { + GlSprintf, + GlAlert, + GlLink, + }, + inject: ['autoDevopsHelpPagePath', 'autoDevopsPath'], + i18n: { + primaryButtonText: s__('SecurityConfiguration|Enable Auto DevOps'), + body: s__( + 'SecurityConfiguration|Quickly enable all continuous testing and compliance tools by enabling %{linkStart}Auto DevOps%{linkEnd}', + ), + }, + methods: { + dismissMethod() { + this.$emit('dismiss'); + }, + }, +}; +</script> + +<template> + <gl-alert + variant="info" + :primary-button-link="autoDevopsPath" + :primary-button-text="$options.i18n.primaryButtonText" + @dismiss="dismissMethod" + > + <gl-sprintf :message="$options.i18n.body"> + <template #link="{ content }"> + <gl-link :href="autoDevopsHelpPagePath"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </gl-alert> +</template> diff --git a/app/assets/javascripts/security_configuration/components/configuration_table.vue b/app/assets/javascripts/security_configuration/components/configuration_table.vue index 2110af1522b..7f250bf1365 100644 --- a/app/assets/javascripts/security_configuration/components/configuration_table.vue +++ b/app/assets/javascripts/security_configuration/components/configuration_table.vue @@ -8,6 +8,7 @@ import { REPORT_TYPE_DAST_PROFILES, REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_CONTAINER_SCANNING, + REPORT_TYPE_CLUSTER_IMAGE_SCANNING, REPORT_TYPE_COVERAGE_FUZZING, REPORT_TYPE_API_FUZZING, REPORT_TYPE_LICENSE_COMPLIANCE, @@ -46,6 +47,7 @@ export default { [REPORT_TYPE_DAST_PROFILES]: Upgrade, [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade, [REPORT_TYPE_CONTAINER_SCANNING]: Upgrade, + [REPORT_TYPE_CLUSTER_IMAGE_SCANNING]: Upgrade, [REPORT_TYPE_COVERAGE_FUZZING]: Upgrade, [REPORT_TYPE_API_FUZZING]: Upgrade, [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade, diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index 142dade914b..5cb9277040d 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -1,7 +1,6 @@ import { helpPagePath } from '~/helpers/help_page_helper'; import { __, s__ } from '~/locale'; -import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql'; import { REPORT_TYPE_SAST, REPORT_TYPE_DAST, @@ -9,11 +8,15 @@ import { REPORT_TYPE_SECRET_DETECTION, REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_CONTAINER_SCANNING, + REPORT_TYPE_CLUSTER_IMAGE_SCANNING, REPORT_TYPE_COVERAGE_FUZZING, REPORT_TYPE_API_FUZZING, REPORT_TYPE_LICENSE_COMPLIANCE, } from '~/vue_shared/security_reports/constants'; +import configureSastMutation from '../graphql/configure_sast.mutation.graphql'; +import configureSecretDetectionMutation from '../graphql/configure_secret_detection.mutation.graphql'; + /** * Translations & helpPagePaths for Static Security Configuration Page */ @@ -34,8 +37,8 @@ export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/das }); export const DAST_PROFILES_NAME = __('DAST Scans'); -export const DAST_PROFILES_DESCRIPTION = __( - 'Saved scan settings and target site settings which are reusable.', +export const DAST_PROFILES_DESCRIPTION = s__( + 'SecurityConfiguration|Manage profiles for use by DAST scans.', ); export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index'); export const DAST_PROFILES_CONFIG_TEXT = s__('SecurityConfiguration|Manage scans'); @@ -76,6 +79,18 @@ export const CONTAINER_SCANNING_CONFIG_HELP_PATH = helpPagePath( { anchor: 'configuration' }, ); +export const CLUSTER_IMAGE_SCANNING_NAME = s__('ciReport|Cluster Image Scanning'); +export const CLUSTER_IMAGE_SCANNING_DESCRIPTION = __( + 'Check your Kubernetes cluster images for known vulnerabilities.', +); +export const CLUSTER_IMAGE_SCANNING_HELP_PATH = helpPagePath( + 'user/application_security/cluster_image_scanning/index', +); +export const CLUSTER_IMAGE_SCANNING_CONFIG_HELP_PATH = helpPagePath( + 'user/application_security/cluster_image_scanning/index', + { anchor: 'configuration' }, +); + export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing'); export const COVERAGE_FUZZING_DESCRIPTION = __( 'Find bugs in your code with coverage-guided fuzzing.', @@ -132,6 +147,12 @@ export const scanners = [ type: REPORT_TYPE_CONTAINER_SCANNING, }, { + name: CLUSTER_IMAGE_SCANNING_NAME, + description: CLUSTER_IMAGE_SCANNING_DESCRIPTION, + helpPath: CLUSTER_IMAGE_SCANNING_HELP_PATH, + type: REPORT_TYPE_CLUSTER_IMAGE_SCANNING, + }, + { name: SECRET_DETECTION_NAME, description: SECRET_DETECTION_DESCRIPTION, helpPath: SECRET_DETECTION_HELP_PATH, @@ -195,6 +216,10 @@ export const securityFeatures = [ helpPath: DEPENDENCY_SCANNING_HELP_PATH, configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH, type: REPORT_TYPE_DEPENDENCY_SCANNING, + + // This field will eventually come from the backend, the progress is + // tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/331621 + canEnableByMergeRequest: window.gon.features?.secDependencyScanningUiEnable, }, { name: CONTAINER_SCANNING_NAME, @@ -204,12 +229,28 @@ export const securityFeatures = [ type: REPORT_TYPE_CONTAINER_SCANNING, }, { + name: CLUSTER_IMAGE_SCANNING_NAME, + description: CLUSTER_IMAGE_SCANNING_DESCRIPTION, + helpPath: CLUSTER_IMAGE_SCANNING_HELP_PATH, + configurationHelpPath: CLUSTER_IMAGE_SCANNING_CONFIG_HELP_PATH, + type: REPORT_TYPE_CLUSTER_IMAGE_SCANNING, + }, + { name: SECRET_DETECTION_NAME, description: SECRET_DETECTION_DESCRIPTION, helpPath: SECRET_DETECTION_HELP_PATH, configurationHelpPath: SECRET_DETECTION_CONFIG_HELP_PATH, type: REPORT_TYPE_SECRET_DETECTION, + + // This field is currently hardcoded because Secret Detection is always + // available. It will eventually come from the Backend, the progress is + // tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/333113 available: true, + + // This field is currently hardcoded because SAST can always be enabled via MR + // It will eventually come from the Backend, the progress is tracked in + // https://gitlab.com/gitlab-org/gitlab/-/issues/331621 + canEnableByMergeRequest: true, }, { name: API_FUZZING_NAME, @@ -247,4 +288,15 @@ export const featureToMutationMap = { }, }), }, + [REPORT_TYPE_SECRET_DETECTION]: { + mutationId: 'configureSecretDetection', + getMutationPayload: (projectPath) => ({ + mutation: configureSecretDetectionMutation, + variables: { + input: { + projectPath, + }, + }, + }), + }, }; diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue index 518a6ede3de..23cffde1f83 100644 --- a/app/assets/javascripts/security_configuration/components/feature_card.vue +++ b/app/assets/javascripts/security_configuration/components/feature_card.vue @@ -46,8 +46,7 @@ export default { return button; }, showManageViaMr() { - const { available, configured, canEnableByMergeRequest } = this.feature; - return canEnableByMergeRequest && available && !configured; + return ManageViaMr.canRender(this.feature); }, cardClasses() { return { 'gl-bg-gray-10': !this.available }; diff --git a/app/assets/javascripts/security_configuration/components/redesigned_app.vue b/app/assets/javascripts/security_configuration/components/redesigned_app.vue index d8a12f4a792..915da378a4f 100644 --- a/app/assets/javascripts/security_configuration/components/redesigned_app.vue +++ b/app/assets/javascripts/security_configuration/components/redesigned_app.vue @@ -2,18 +2,22 @@ import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui'; import { __, s__ } from '~/locale'; import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; +import AutoDevOpsAlert from './auto_dev_ops_alert.vue'; import FeatureCard from './feature_card.vue'; import SectionLayout from './section_layout.vue'; import UpgradeBanner from './upgrade_banner.vue'; export const i18n = { compliance: s__('SecurityConfiguration|Compliance'), + configurationHistory: s__('SecurityConfiguration|Configuration history'), securityTesting: s__('SecurityConfiguration|Security testing'), - securityTestingDescription: s__( + latestPipelineDescription: s__( `SecurityConfiguration|The status of the tools only applies to the - default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. - Once you've enabled a scan for the default branch, any subsequent feature - branch you create will include the scan.`, + default branch and is based on the %{linkStart}latest pipeline%{linkEnd}.`, + ), + description: s__( + `SecurityConfiguration|Once you've enabled a scan for the default branch, + any subsequent feature branch you create will include the scan.`, ), securityConfiguration: __('Security Configuration'), }; @@ -28,6 +32,7 @@ export default { FeatureCard, SectionLayout, UpgradeBanner, + AutoDevOpsAlert, UserCalloutDismisser, }, props: { @@ -44,6 +49,16 @@ export default { required: false, default: false, }, + autoDevopsEnabled: { + type: Boolean, + required: false, + default: false, + }, + canEnableAutoDevops: { + type: Boolean, + required: false, + default: false, + }, gitlabCiHistoryPath: { type: String, required: false, @@ -64,16 +79,26 @@ export default { canViewCiHistory() { return Boolean(this.gitlabCiPresent && this.gitlabCiHistoryPath); }, + shouldShowDevopsAlert() { + return !this.autoDevopsEnabled && !this.gitlabCiPresent && this.canEnableAutoDevops; + }, }, }; </script> <template> <article> + <user-callout-dismisser + v-if="shouldShowDevopsAlert" + feature-name="security_configuration_devops_alert" + > + <template #default="{ dismiss, shouldShowCallout }"> + <auto-dev-ops-alert v-if="shouldShowCallout" class="gl-mt-3" @dismiss="dismiss" /> + </template> + </user-callout-dismisser> <header> <h1 class="gl-font-size-h1">{{ $options.i18n.securityConfiguration }}</h1> </header> - <user-callout-dismisser v-if="canUpgrade" feature-name="security_configuration_upgrade_banner"> <template #default="{ dismiss, shouldShowCallout }"> <upgrade-banner v-if="shouldShowCallout" @close="dismiss" /> @@ -84,16 +109,19 @@ export default { <gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting"> <section-layout :heading="$options.i18n.securityTesting"> <template #description> - <p - v-if="latestPipelinePath" - data-testid="latest-pipeline-info-security" - class="gl-line-height-20" - > - <gl-sprintf :message="$options.i18n.securityTestingDescription"> - <template #link="{ content }"> - <gl-link :href="latestPipelinePath">{{ content }}</gl-link> - </template> - </gl-sprintf> + <p> + <span data-testid="latest-pipeline-info-security"> + <gl-sprintf + v-if="latestPipelinePath" + :message="$options.i18n.latestPipelineDescription" + > + <template #link="{ content }"> + <gl-link :href="latestPipelinePath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> + + {{ $options.i18n.description }} </p> <p v-if="canViewCiHistory"> <gl-link data-testid="security-view-history-link" :href="gitlabCiHistoryPath">{{ @@ -106,6 +134,7 @@ export default { <feature-card v-for="feature in augmentedSecurityFeatures" :key="feature.type" + data-testid="security-testing-card" :feature="feature" class="gl-mb-6" /> @@ -115,16 +144,19 @@ export default { <gl-tab data-testid="compliance-testing-tab" :title="$options.i18n.compliance"> <section-layout :heading="$options.i18n.compliance"> <template #description> - <p - v-if="latestPipelinePath" - class="gl-line-height-20" - data-testid="latest-pipeline-info-compliance" - > - <gl-sprintf :message="$options.i18n.securityTestingDescription"> - <template #link="{ content }"> - <gl-link :href="latestPipelinePath">{{ content }}</gl-link> - </template> - </gl-sprintf> + <p> + <span data-testid="latest-pipeline-info-compliance"> + <gl-sprintf + v-if="latestPipelinePath" + :message="$options.i18n.latestPipelineDescription" + > + <template #link="{ content }"> + <gl-link :href="latestPipelinePath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> + + {{ $options.i18n.description }} </p> <p v-if="canViewCiHistory"> <gl-link data-testid="compliance-view-history-link" :href="gitlabCiHistoryPath">{{ diff --git a/app/assets/javascripts/security_configuration/components/section_layout.vue b/app/assets/javascripts/security_configuration/components/section_layout.vue index 1e1f83a6d99..e351f9b9d8d 100644 --- a/app/assets/javascripts/security_configuration/components/section_layout.vue +++ b/app/assets/javascripts/security_configuration/components/section_layout.vue @@ -11,12 +11,12 @@ export default { </script> <template> - <div class="row"> - <div class="col-lg-5"> + <div class="row gl-line-height-20"> + <div class="col-lg-4"> <h2 class="gl-font-size-h2 gl-mt-0">{{ heading }}</h2> <slot name="description"></slot> </div> - <div class="col-lg-7"> + <div class="col-lg-8"> <slot name="features"></slot> </div> </div> diff --git a/app/assets/javascripts/security_configuration/graphql/configure_secret_detection.mutation.graphql b/app/assets/javascripts/security_configuration/graphql/configure_secret_detection.mutation.graphql new file mode 100644 index 00000000000..e42a8de64f3 --- /dev/null +++ b/app/assets/javascripts/security_configuration/graphql/configure_secret_detection.mutation.graphql @@ -0,0 +1,6 @@ +mutation configureSecretDetection($input: ConfigureSecretDetectionInput!) { + configureSecretDetection(input: $input) { + successPath + errors + } +} diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js index e1dc6f24737..f05bd79258e 100644 --- a/app/assets/javascripts/security_configuration/index.js +++ b/app/assets/javascripts/security_configuration/index.js @@ -7,11 +7,7 @@ import { securityFeatures, complianceFeatures } from './components/constants'; import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue'; import { augmentFeatures } from './utils'; -export const initStaticSecurityConfiguration = (el) => { - if (!el) { - return null; - } - +export const initRedesignedSecurityConfiguration = (el) => { Vue.use(VueApollo); const apolloProvider = new VueApollo({ @@ -24,35 +20,60 @@ export const initStaticSecurityConfiguration = (el) => { features, latestPipelinePath, gitlabCiHistoryPath, + autoDevopsHelpPagePath, + autoDevopsPath, } = el.dataset; - if (gon.features.securityConfigurationRedesign) { - const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures( - securityFeatures, - complianceFeatures, - features ? JSON.parse(features) : [], - ); + const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures( + securityFeatures, + complianceFeatures, + features ? JSON.parse(features) : [], + ); + + return new Vue({ + el, + apolloProvider, + provide: { + projectPath, + upgradePath, + autoDevopsHelpPagePath, + autoDevopsPath, + }, + render(createElement) { + return createElement(RedesignedSecurityConfigurationApp, { + props: { + augmentedComplianceFeatures, + augmentedSecurityFeatures, + latestPipelinePath, + gitlabCiHistoryPath, + ...parseBooleanDataAttributes(el, [ + 'gitlabCiPresent', + 'autoDevopsEnabled', + 'canEnableAutoDevops', + ]), + }, + }); + }, + }); +}; + +export const initCESecurityConfiguration = (el) => { + if (!el) { + return null; + } - return new Vue({ - el, - apolloProvider, - provide: { - projectPath, - upgradePath, - }, - render(createElement) { - return createElement(RedesignedSecurityConfigurationApp, { - props: { - augmentedComplianceFeatures, - augmentedSecurityFeatures, - latestPipelinePath, - gitlabCiHistoryPath, - ...parseBooleanDataAttributes(el, ['gitlabCiPresent']), - }, - }); - }, - }); + if (gon.features?.securityConfigurationRedesign) { + return initRedesignedSecurityConfiguration(el); } + + Vue.use(VueApollo); + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + const { projectPath, upgradePath } = el.dataset; + return new Vue({ el, apolloProvider, diff --git a/app/assets/javascripts/security_configuration/utils.js b/app/assets/javascripts/security_configuration/utils.js index 071ebff4f21..ec6b93c6193 100644 --- a/app/assets/javascripts/security_configuration/utils.js +++ b/app/assets/javascripts/security_configuration/utils.js @@ -1,6 +1,8 @@ +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => { const featuresByType = features.reduce((acc, feature) => { - acc[feature.type] = feature; + acc[feature.type] = convertObjectPropsToCamelCase(feature, { deep: true }); return acc; }, {}); |