Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 12:45:46 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 12:45:46 +0300
commita7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch)
tree7452bd5c3545c2fa67a28aa013835fb4fa071baf /app/assets/javascripts/security_configuration
parentee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff)
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'app/assets/javascripts/security_configuration')
-rw-r--r--app/assets/javascripts/security_configuration/components/app.vue6
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js34
-rw-r--r--app/assets/javascripts/security_configuration/components/feature_card.vue23
-rw-r--r--app/assets/javascripts/security_configuration/components/training_provider_list.vue106
-rw-r--r--app/assets/javascripts/security_configuration/components/upgrade_banner.vue18
-rw-r--r--app/assets/javascripts/security_configuration/constants.js2
-rw-r--r--app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql7
-rw-r--r--app/assets/javascripts/security_configuration/graphql/security_training_providers.query.graphql16
-rw-r--r--app/assets/javascripts/security_configuration/index.js7
-rw-r--r--app/assets/javascripts/security_configuration/resolver.js56
-rw-r--r--app/assets/javascripts/security_configuration/utils.js13
11 files changed, 141 insertions, 147 deletions
diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue
index d228f77f27d..c48c9067250 100644
--- a/app/assets/javascripts/security_configuration/components/app.vue
+++ b/app/assets/javascripts/security_configuration/components/app.vue
@@ -50,7 +50,7 @@ export default {
TrainingProviderList,
},
mixins: [glFeatureFlagsMixin()],
- inject: ['projectPath'],
+ inject: ['projectFullPath'],
props: {
augmentedSecurityFeatures: {
type: Array,
@@ -107,14 +107,14 @@ export default {
shouldShowAutoDevopsEnabledAlert() {
return (
this.autoDevopsEnabled &&
- !this.autoDevopsEnabledAlertDismissedProjects.includes(this.projectPath)
+ !this.autoDevopsEnabledAlertDismissedProjects.includes(this.projectFullPath)
);
},
},
methods: {
dismissAutoDevopsEnabledAlert() {
const dismissedProjects = new Set(this.autoDevopsEnabledAlertDismissedProjects);
- dismissedProjects.add(this.projectPath);
+ dismissedProjects.add(this.projectFullPath);
this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects);
},
onError(message) {
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index 034dba29196..81d222438e3 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -123,7 +123,7 @@ export const COVERAGE_FUZZING_CONFIG_HELP_PATH = helpPagePath(
export const CORPUS_MANAGEMENT_NAME = __('Corpus Management');
export const CORPUS_MANAGEMENT_DESCRIPTION = s__(
- 'SecurityConfiguration|Manage corpus files used as mutation sources in coverage fuzzing.',
+ 'SecurityConfiguration|Manage corpus files used as seed inputs with coverage-guided fuzzing.',
);
export const CORPUS_MANAGEMENT_CONFIG_TEXT = s__('SecurityConfiguration|Manage corpus');
@@ -159,15 +159,6 @@ export const securityFeatures = [
helpPath: SAST_HELP_PATH,
configurationHelpPath: SAST_CONFIG_HELP_PATH,
type: REPORT_TYPE_SAST,
- // This field is currently hardcoded because SAST is always available.
- // It will eventually come from the Backend, the progress is tracked in
- // https://gitlab.com/gitlab-org/gitlab/-/issues/331622
- 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: SAST_IAC_NAME,
@@ -176,15 +167,6 @@ export const securityFeatures = [
helpPath: SAST_IAC_HELP_PATH,
configurationHelpPath: SAST_IAC_CONFIG_HELP_PATH,
type: REPORT_TYPE_SAST_IAC,
-
- // This field is currently hardcoded because SAST IaC is always available.
- // It will eventually come from the Backend, the progress is tracked in
- // https://gitlab.com/gitlab-org/gitlab/-/issues/331622
- available: true,
-
- // This field will eventually come from the backend, the progress is
- // tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/331621
- canEnableByMergeRequest: true,
},
{
name: DAST_NAME,
@@ -206,10 +188,6 @@ 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: true,
},
{
name: CONTAINER_SCANNING_NAME,
@@ -231,16 +209,6 @@ export const securityFeatures = [
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,
diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue
index 33d72b54f86..1c37d8008de 100644
--- a/app/assets/javascripts/security_configuration/components/feature_card.vue
+++ b/app/assets/javascripts/security_configuration/components/feature_card.vue
@@ -24,9 +24,6 @@ export default {
enabled() {
return this.available && this.feature.configured;
},
- hasStatus() {
- return !this.available || typeof this.feature.configured === 'boolean';
- },
shortName() {
return this.feature.shortName ?? this.feature.name;
},
@@ -93,19 +90,17 @@ export default {
data-testid="feature-status"
:data-qa-selector="`${feature.type}_status`"
>
- <template v-if="hasStatus">
- <template v-if="enabled">
- <gl-icon name="check-circle-filled" />
- <span class="gl-text-green-700">{{ $options.i18n.enabled }}</span>
- </template>
+ <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 }}
- </template>
+ <template v-else-if="available">
+ {{ $options.i18n.notEnabled }}
+ </template>
- <template v-else>
- {{ $options.i18n.availableWith }}
- </template>
+ <template v-else>
+ {{ $options.i18n.availableWith }}
</template>
</div>
</div>
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 ca4596e16b3..539e2bff17c 100644
--- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue
+++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
@@ -1,6 +1,13 @@
<script>
import { GlAlert, GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
+import Tracking from '~/tracking';
import { __ } from '~/locale';
+import {
+ TRACK_TOGGLE_TRAINING_PROVIDER_ACTION,
+ TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
+} from '~/security_configuration/constants';
+import dismissUserCalloutMutation from '~/graphql_shared/mutations/dismiss_user_callout.mutation.graphql';
import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql';
import configureSecurityTrainingProvidersMutation from '../graphql/configure_security_training_providers.mutation.graphql';
@@ -21,10 +28,19 @@ export default {
GlLink,
GlSkeletonLoader,
},
- inject: ['projectPath'],
+ mixins: [Tracking.mixin()],
+ inject: ['projectFullPath'],
apollo: {
securityTrainingProviders: {
query: securityTrainingProvidersQuery,
+ variables() {
+ return {
+ fullPath: this.projectFullPath,
+ };
+ },
+ update({ project }) {
+ return project?.securityTrainingProviders;
+ },
error() {
this.errorMessage = this.$options.i18n.providerQueryErrorMessage;
},
@@ -33,8 +49,9 @@ export default {
data() {
return {
errorMessage: '',
- toggleLoading: false,
+ providerLoadingId: null,
securityTrainingProviders: [],
+ hasTouchedConfiguration: false,
};
},
computed: {
@@ -42,33 +59,59 @@ export default {
return this.$apollo.queries.securityTrainingProviders.loading;
},
},
+ created() {
+ const unwatchConfigChance = this.$watch('hasTouchedConfiguration', () => {
+ this.dismissFeaturePromotionCallout();
+ unwatchConfigChance();
+ });
+ },
methods: {
- toggleProvider(selectedProviderId) {
- const toggledProviders = this.securityTrainingProviders.map((provider) => ({
- ...provider,
- ...(provider.id === selectedProviderId && { isEnabled: !provider.isEnabled }),
- }));
+ async dismissFeaturePromotionCallout() {
+ try {
+ const {
+ data: {
+ userCalloutCreate: { errors },
+ },
+ } = await this.$apollo.mutate({
+ mutation: dismissUserCalloutMutation,
+ variables: {
+ input: {
+ featureName: 'security_training_feature_promotion',
+ },
+ },
+ });
- const enabledProviderIds = toggledProviders
- .filter(({ isEnabled }) => isEnabled)
- .map(({ id }) => id);
+ // handle errors reported from the backend
+ if (errors?.length > 0) {
+ throw new Error(errors[0]);
+ }
+ } catch (e) {
+ Sentry.captureException(e);
+ }
+ },
+ toggleProvider(provider) {
+ const { isEnabled } = provider;
+ const toggledIsEnabled = !isEnabled;
- this.storeEnabledProviders(toggledProviders, enabledProviderIds);
+ this.trackProviderToggle(provider.id, toggledIsEnabled);
+ this.storeProvider({ ...provider, isEnabled: toggledIsEnabled });
},
- async storeEnabledProviders(toggledProviders, enabledProviderIds) {
- this.toggleLoading = true;
+ async storeProvider({ id, isEnabled, isPrimary }) {
+ this.providerLoadingId = id;
try {
const {
data: {
- configureSecurityTrainingProviders: { errors = [] },
+ securityTrainingUpdate: { errors = [] },
},
} = await this.$apollo.mutate({
mutation: configureSecurityTrainingProvidersMutation,
variables: {
input: {
- enabledProviders: enabledProviderIds,
- fullPath: this.projectPath,
+ projectPath: this.projectFullPath,
+ providerId: id,
+ isEnabled,
+ isPrimary,
},
},
});
@@ -77,12 +120,23 @@ export default {
// throwing an error here means we can handle scenarios within the `catch` block below
throw new Error();
}
+
+ this.hasTouchedConfiguration = true;
} catch {
this.errorMessage = this.$options.i18n.configMutationErrorMessage;
} finally {
- this.toggleLoading = false;
+ this.providerLoadingId = null;
}
},
+ trackProviderToggle(providerId, providerIsEnabled) {
+ this.track(TRACK_TOGGLE_TRAINING_PROVIDER_ACTION, {
+ label: TRACK_TOGGLE_TRAINING_PROVIDER_LABEL,
+ property: providerId,
+ extra: {
+ providerIsEnabled,
+ },
+ });
+ },
},
i18n,
};
@@ -104,25 +158,21 @@ export default {
</gl-skeleton-loader>
</div>
<ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
- <li
- v-for="{ id, isEnabled, name, description, url } in securityTrainingProviders"
- :key="id"
- class="gl-mb-6"
- >
+ <li v-for="provider in securityTrainingProviders" :key="provider.id" class="gl-mb-6">
<gl-card>
<div class="gl-display-flex">
<gl-toggle
- :value="isEnabled"
+ :value="provider.isEnabled"
:label="__('Training mode')"
label-position="hidden"
- :is-loading="toggleLoading"
- @change="toggleProvider(id)"
+ :is-loading="providerLoadingId === provider.id"
+ @change="toggleProvider(provider)"
/>
<div class="gl-ml-5">
- <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ name }}</h3>
+ <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ provider.name }}</h3>
<p>
- {{ description }}
- <gl-link :href="url" target="_blank">{{ __('Learn more.') }}</gl-link>
+ {{ provider.description }}
+ <gl-link :href="provider.url" target="_blank">{{ __('Learn more.') }}</gl-link>
</p>
</div>
</div>
diff --git a/app/assets/javascripts/security_configuration/components/upgrade_banner.vue b/app/assets/javascripts/security_configuration/components/upgrade_banner.vue
index 79e6b9d7a23..891d7bf2eb0 100644
--- a/app/assets/javascripts/security_configuration/components/upgrade_banner.vue
+++ b/app/assets/javascripts/security_configuration/components/upgrade_banner.vue
@@ -1,11 +1,16 @@
<script>
import { GlBanner } from '@gitlab/ui';
import { s__ } from '~/locale';
+import Tracking from '~/tracking';
+
+export const SECURITY_UPGRADE_BANNER = 'security_upgrade_banner';
+export const UPGRADE_OR_FREE_TRIAL = 'upgrade_or_free_trial';
export default {
components: {
GlBanner,
},
+ mixins: [Tracking.mixin({ property: SECURITY_UPGRADE_BANNER })],
inject: ['upgradePath'],
i18n: {
title: s__('SecurityConfiguration|Secure your project'),
@@ -22,6 +27,17 @@ export default {
],
buttonText: s__('SecurityConfiguration|Upgrade or start a free trial'),
},
+ mounted() {
+ this.track('render', { label: SECURITY_UPGRADE_BANNER });
+ },
+ methods: {
+ bannerClosed() {
+ this.track('dismiss_banner', { label: SECURITY_UPGRADE_BANNER });
+ },
+ bannerButtonClicked() {
+ this.track('click_button', { label: UPGRADE_OR_FREE_TRIAL });
+ },
+ },
};
</script>
@@ -31,6 +47,8 @@ export default {
:button-text="$options.i18n.buttonText"
:button-link="upgradePath"
variant="introduction"
+ @close="bannerClosed"
+ @primary="bannerButtonClicked"
v-on="$listeners"
>
<p>{{ $options.i18n.bodyStart }}</p>
diff --git a/app/assets/javascripts/security_configuration/constants.js b/app/assets/javascripts/security_configuration/constants.js
new file mode 100644
index 00000000000..dc76436e91d
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/constants.js
@@ -0,0 +1,2 @@
+export const TRACK_TOGGLE_TRAINING_PROVIDER_ACTION = 'toggle_security_training_provider';
+export const TRACK_TOGGLE_TRAINING_PROVIDER_LABEL = 'update_security_training_provider';
diff --git a/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql b/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql
index 660e0fadafb..3528bfaf7b8 100644
--- a/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql
+++ b/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql
@@ -1,9 +1,10 @@
-mutation configureSecurityTrainingProviders($input: configureSecurityTrainingProvidersInput!) {
- configureSecurityTrainingProviders(input: $input) @client {
+mutation updateSecurityTraining($input: SecurityTrainingUpdateInput!) {
+ securityTrainingUpdate(input: $input) {
errors
- securityTrainingProviders {
+ training {
id
isEnabled
+ isPrimary
}
}
}
diff --git a/app/assets/javascripts/security_configuration/graphql/security_training_providers.query.graphql b/app/assets/javascripts/security_configuration/graphql/security_training_providers.query.graphql
index e0c5715ba8e..2baeda318f3 100644
--- a/app/assets/javascripts/security_configuration/graphql/security_training_providers.query.graphql
+++ b/app/assets/javascripts/security_configuration/graphql/security_training_providers.query.graphql
@@ -1,9 +1,13 @@
-query Query {
- securityTrainingProviders @client {
- name
+query getSecurityTrainingProviders($fullPath: ID!) {
+ project(fullPath: $fullPath) {
id
- description
- isEnabled
- url
+ securityTrainingProviders {
+ name
+ id
+ description
+ isPrimary
+ isEnabled
+ url
+ }
}
}
diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js
index 24c0585e077..8416692dd27 100644
--- a/app/assets/javascripts/security_configuration/index.js
+++ b/app/assets/javascripts/security_configuration/index.js
@@ -5,7 +5,6 @@ import { parseBooleanDataAttributes } from '~/lib/utils/dom_utils';
import SecurityConfigurationApp from './components/app.vue';
import { securityFeatures, complianceFeatures } from './components/constants';
import { augmentFeatures } from './utils';
-import tempResolvers from './resolver';
export const initSecurityConfiguration = (el) => {
if (!el) {
@@ -15,11 +14,11 @@ export const initSecurityConfiguration = (el) => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(tempResolvers),
+ defaultClient: createDefaultClient(),
});
const {
- projectPath,
+ projectFullPath,
upgradePath,
features,
latestPipelinePath,
@@ -38,7 +37,7 @@ export const initSecurityConfiguration = (el) => {
el,
apolloProvider,
provide: {
- projectPath,
+ projectFullPath,
upgradePath,
autoDevopsHelpPagePath,
autoDevopsPath,
diff --git a/app/assets/javascripts/security_configuration/resolver.js b/app/assets/javascripts/security_configuration/resolver.js
deleted file mode 100644
index 93175d4a3d1..00000000000
--- a/app/assets/javascripts/security_configuration/resolver.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import produce from 'immer';
-import { __ } from '~/locale';
-import securityTrainingProvidersQuery from './graphql/security_training_providers.query.graphql';
-
-// Note: this is behind a feature flag and only a placeholder
-// until the actual GraphQL fields have been added
-// https://gitlab.com/gitlab-org/gi tlab/-/issues/346480
-export default {
- Query: {
- securityTrainingProviders() {
- return [
- {
- __typename: 'SecurityTrainingProvider',
- id: 101,
- name: __('Kontra'),
- description: __('Interactive developer security education.'),
- url: 'https://application.security/',
- isEnabled: false,
- },
- {
- __typename: 'SecurityTrainingProvider',
- id: 102,
- name: __('SecureCodeWarrior'),
- description: __('Security training with guide and learning pathways.'),
- url: 'https://www.securecodewarrior.com/',
- isEnabled: true,
- },
- ];
- },
- },
-
- Mutation: {
- configureSecurityTrainingProviders: (
- _,
- { input: { enabledProviders, primaryProvider } },
- { cache },
- ) => {
- const sourceData = cache.readQuery({
- query: securityTrainingProvidersQuery,
- });
-
- const data = produce(sourceData.securityTrainingProviders, (draftData) => {
- /* eslint-disable no-param-reassign */
- draftData.forEach((provider) => {
- provider.isPrimary = provider.id === primaryProvider;
- provider.isEnabled =
- provider.id === primaryProvider || enabledProviders.includes(provider.id);
- });
- });
- return {
- __typename: 'configureSecurityTrainingProvidersPayload',
- securityTrainingProviders: data,
- };
- },
- },
-};
diff --git a/app/assets/javascripts/security_configuration/utils.js b/app/assets/javascripts/security_configuration/utils.js
index 47231497b8f..173560f8370 100644
--- a/app/assets/javascripts/security_configuration/utils.js
+++ b/app/assets/javascripts/security_configuration/utils.js
@@ -1,6 +1,19 @@
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants';
+/**
+ * This function takes in 3 arrays of objects, securityFeatures, complianceFeatures and features.
+ * securityFeatures and complianceFeatures are static arrays living in the constants.
+ * features is dynamic and coming from the backend.
+ * This function builds a superset of those arrays.
+ * It looks for matching keys within the dynamic and the static arrays
+ * and will enrich the objects with the available static data.
+ * @param [{}] securityFeatures
+ * @param [{}] complianceFeatures
+ * @param [{}] features
+ * @returns {Object} Object with enriched features from constants divided into Security and Compliance Features
+ */
+
export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => {
const featuresByType = features.reduce((acc, feature) => {
acc[feature.type] = convertObjectPropsToCamelCase(feature, { deep: true });