diff options
Diffstat (limited to 'app/assets/javascripts/security_configuration/components')
5 files changed, 114 insertions, 36 deletions
diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue index c48c9067250..ba0120a0a70 100644 --- a/app/assets/javascripts/security_configuration/components/app.vue +++ b/app/assets/javascripts/security_configuration/components/app.vue @@ -30,6 +30,7 @@ export const i18n = { securityTrainingDescription: s__( 'SecurityConfiguration|Enable security training to help your developers learn how to fix vulnerabilities. Developers can view security training from selected educational providers, relevant to the detected vulnerability.', ), + securityTrainingDoc: s__('SecurityConfiguration|Learn more about vulnerability training'), }; export default { @@ -50,7 +51,7 @@ export default { TrainingProviderList, }, mixins: [glFeatureFlagsMixin()], - inject: ['projectFullPath'], + inject: ['projectFullPath', 'vulnerabilityTrainingDocsPath'], props: { augmentedSecurityFeatures: { type: Array, @@ -143,7 +144,6 @@ export default { <local-storage-sync v-model="autoDevopsEnabledAlertDismissedProjects" :storage-key="$options.autoDevopsEnabledAlertStorageKey" - as-json /> <user-callout-dismisser @@ -262,6 +262,11 @@ export default { <p> {{ $options.i18n.securityTrainingDescription }} </p> + <p> + <gl-link :href="vulnerabilityTrainingDocsPath">{{ + $options.i18n.securityTrainingDoc + }}</gl-link> + </p> </template> <template #features> <training-provider-list /> diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index 39a2939f52a..6db28ef0fad 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -50,18 +50,24 @@ export const SAST_IAC_CONFIG_HELP_PATH = helpPagePath( export const DAST_NAME = __('Dynamic Application Security Testing (DAST)'); export const DAST_SHORT_NAME = s__('ciReport|DAST'); -export const DAST_DESCRIPTION = __('Analyze a review version of your web application.'); +export const DAST_DESCRIPTION = s__( + 'ciReport|Analyze a deployed version of your web application for known vulnerabilities by examining it from the outside in. DAST works by simulating external attacks on your application while it is running.', +); export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index'); export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/dast/index', { anchor: 'enable-dast', }); +export const DAST_BADGE_TEXT = __('Available on-demand'); +export const DAST_BADGE_TOOLTIP = __( + 'On-demand scans run outside of the DevOps cycle and find vulnerabilities in your projects', +); -export const DAST_PROFILES_NAME = __('DAST Scans'); +export const DAST_PROFILES_NAME = __('DAST profiles'); 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'); +export const DAST_PROFILES_CONFIG_TEXT = s__('SecurityConfiguration|Manage profiles'); export const SECRET_DETECTION_NAME = __('Secret Detection'); export const SECRET_DETECTION_DESCRIPTION = __( @@ -171,18 +177,23 @@ export const securityFeatures = [ type: REPORT_TYPE_SAST_IAC, }, { - name: DAST_NAME, - shortName: DAST_SHORT_NAME, - description: DAST_DESCRIPTION, - helpPath: DAST_HELP_PATH, - configurationHelpPath: DAST_CONFIG_HELP_PATH, - type: REPORT_TYPE_DAST, + badge: { + text: DAST_BADGE_TEXT, + tooltipText: DAST_BADGE_TOOLTIP, + variant: 'info', + }, secondary: { type: REPORT_TYPE_DAST_PROFILES, name: DAST_PROFILES_NAME, description: DAST_PROFILES_DESCRIPTION, configurationText: DAST_PROFILES_CONFIG_TEXT, }, + name: DAST_NAME, + shortName: DAST_SHORT_NAME, + description: DAST_DESCRIPTION, + helpPath: DAST_HELP_PATH, + configurationHelpPath: DAST_CONFIG_HELP_PATH, + type: REPORT_TYPE_DAST, }, { name: DEPENDENCY_SCANNING_NAME, diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue index cd5ad86e1a8..309e5f21445 100644 --- a/app/assets/javascripts/security_configuration/components/feature_card.vue +++ b/app/assets/javascripts/security_configuration/components/feature_card.vue @@ -1,7 +1,9 @@ <script> import { GlButton, GlCard, GlIcon, GlLink } from '@gitlab/ui'; import { __, s__, sprintf } from '~/locale'; +import { REPORT_TYPE_SAST_IAC } from '~/vue_shared/security_reports/constants'; import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue'; +import FeatureCardBadge from './feature_card_badge.vue'; export default { components: { @@ -9,6 +11,7 @@ export default { GlCard, GlIcon, GlLink, + FeatureCardBadge, ManageViaMr, }, props: { @@ -36,11 +39,14 @@ export default { text: this.$options.i18n.enableFeature, }; - button.category = 'secondary'; + button.category = this.feature.category || 'secondary'; button.text = sprintf(button.text, { feature: this.shortName }); return button; }, + manageViaMrButtonCategory() { + return this.feature.category || 'secondary'; + }, showManageViaMr() { return ManageViaMr.canRender(this.feature); }, @@ -48,19 +54,32 @@ export default { return { 'gl-bg-gray-10': !this.available }; }, statusClasses() { - const { enabled } = this; + const { enabled, hasBadge } = this; return { 'gl-ml-auto': true, 'gl-flex-shrink-0': true, 'gl-text-gray-500': !enabled, 'gl-text-green-500': enabled, + 'gl-w-full': hasBadge, + 'gl-justify-content-space-between': hasBadge, + 'gl-display-flex': hasBadge, + 'gl-mb-4': hasBadge, }; }, hasSecondary() { const { name, description, configurationText } = this.feature.secondary ?? {}; return Boolean(name && description && configurationText); }, + // This condition is a temporary hack to not display any wrong information + // until this BE Bug is fixed: https://gitlab.com/gitlab-org/gitlab/-/issues/350307. + // More Information: https://gitlab.com/gitlab-org/gitlab/-/issues/350307#note_825447417 + isNotSastIACTemporaryHack() { + return this.feature.type !== REPORT_TYPE_SAST_IAC; + }, + hasBadge() { + return Boolean(this.available && this.feature.badge?.text); + }, }, methods: { onError(message) { @@ -81,21 +100,31 @@ export default { <template> <gl-card :class="cardClasses"> - <div class="gl-display-flex gl-align-items-baseline"> + <div + class="gl-display-flex gl-align-items-baseline" + :class="{ 'gl-flex-direction-column-reverse': hasBadge }" + > <h3 class="gl-font-lg gl-m-0 gl-mr-3">{{ feature.name }}</h3> <div + v-if="isNotSastIACTemporaryHack" :class="statusClasses" data-testid="feature-status" :data-qa-selector="`${feature.type}_status`" > + <feature-card-badge + v-if="hasBadge" + :badge="feature.badge" + :badge-href="feature.badge.badgeHref" + /> + <template v-if="enabled"> <gl-icon name="check-circle-filled" /> <span class="gl-text-green-700">{{ $options.i18n.enabled }}</span> </template> <template v-else-if="available"> - {{ $options.i18n.notEnabled }} + <span>{{ $options.i18n.notEnabled }}</span> </template> <template v-else> @@ -109,7 +138,7 @@ export default { <gl-link :href="feature.helpPath">{{ $options.i18n.learnMore }}</gl-link> </p> - <template v-if="available"> + <template v-if="available && isNotSastIACTemporaryHack"> <gl-button v-if="feature.configurationPath" :href="feature.configurationPath" @@ -125,7 +154,7 @@ export default { v-else-if="showManageViaMr" :feature="feature" variant="confirm" - category="secondary" + :category="manageViaMrButtonCategory" class="gl-mt-5" :data-qa-selector="`${feature.type}_mr_button`" @error="onError" diff --git a/app/assets/javascripts/security_configuration/components/feature_card_badge.vue b/app/assets/javascripts/security_configuration/components/feature_card_badge.vue new file mode 100644 index 00000000000..0907e33c8e2 --- /dev/null +++ b/app/assets/javascripts/security_configuration/components/feature_card_badge.vue @@ -0,0 +1,40 @@ +<script> +import { GlBadge, GlTooltip } from '@gitlab/ui'; + +export default { + components: { + GlBadge, + GlTooltip, + }, + props: { + badge: { + type: Object, + required: true, + }, + badgeHref: { + type: String, + required: false, + default: '', + }, + }, +}; +</script> + +<template> + <span> + <gl-tooltip + v-if="badge.tooltipText" + placement="top" + boundary="window" + title="Tooltip title" + :target="() => $refs.badge" + > + {{ badge.tooltipText }} + </gl-tooltip> + <span ref="badge"> + <gl-badge size="sm" :href="badgeHref" :variant="badge.variant"> + {{ badge.text }} + </gl-badge> + </span> + </span> +</template> diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue index bb540303cfd..ef50d085ae8 100644 --- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue +++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue @@ -3,6 +3,7 @@ import { GlAlert, GlTooltipDirective, GlCard, + GlFormRadio, GlToggle, GlLink, GlSkeletonLoader, @@ -44,6 +45,7 @@ export default { components: { GlAlert, GlCard, + GlFormRadio, GlToggle, GlLink, GlSkeletonLoader, @@ -79,6 +81,9 @@ export default { }; }, computed: { + primaryProviderId() { + return this.securityTrainingProviders.find(({ isPrimary }) => isPrimary)?.id; + }, enabledProviders() { return this.securityTrainingProviders.filter(({ isEnabled }) => isEnabled); }, @@ -256,31 +261,19 @@ export default { {{ __('Learn more.') }} </gl-link> </p> - <!-- Note: The following `div` and it's content will be replaced by 'GlFormRadio' once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1720#note_857342988 is resolved --> - <div - class="gl-form-radio custom-control custom-radio" - data-testid="primary-provider-radio" + <gl-form-radio + :checked="primaryProviderId" + :disabled="!provider.isEnabled" + :value="provider.id" + @change="setPrimaryProvider(provider)" > - <input - :id="`security-training-provider-${provider.id}`" - type="radio" - :checked="provider.isPrimary" - class="custom-control-input" - :disabled="!provider.isEnabled" - @change="setPrimaryProvider(provider)" - /> - <label - class="custom-control-label" - :for="`security-training-provider-${provider.id}`" - > - {{ $options.i18n.primaryTraining }} - </label> + {{ $options.i18n.primaryTraining }} <gl-icon v-gl-tooltip="$options.i18n.primaryTrainingDescription" name="information-o" class="gl-ml-2 gl-cursor-help" /> - </div> + </gl-form-radio> </div> </div> </gl-card> |