Commit 91066fde authored by Jannik Lehmann's avatar Jannik Lehmann Committed by Savas Vedova

SAST Config introduce disabled analyzer warning

This commit solves https://gitlab.com/gitlab-org/gitlab/-/issues/273628
A warning has been introduced to raise attention
about the disabled analyzers on the SAST-config Page.
parent 19cd6a64
<script> <script>
import { GlAlert, GlButton, GlIcon, GlLink } from '@gitlab/ui'; import { GlAlert, GlButton, GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import DynamicFields from 'ee/security_configuration/components/dynamic_fields.vue'; import DynamicFields from 'ee/security_configuration/components/dynamic_fields.vue';
...@@ -22,6 +22,7 @@ export default { ...@@ -22,6 +22,7 @@ export default {
GlButton, GlButton,
GlIcon, GlIcon,
GlLink, GlLink,
GlSprintf,
}, },
inject: { inject: {
createSastMergeRequestPath: { createSastMergeRequestPath: {
...@@ -55,6 +56,7 @@ export default { ...@@ -55,6 +56,7 @@ export default {
analyzersConfiguration: cloneDeep(this.sastCiConfiguration.analyzers.nodes), analyzersConfiguration: cloneDeep(this.sastCiConfiguration.analyzers.nodes),
hasSubmissionError: false, hasSubmissionError: false,
isSubmitting: false, isSubmitting: false,
showAnalyzersTip: false,
}; };
}, },
computed: { computed: {
...@@ -62,6 +64,9 @@ export default { ...@@ -62,6 +64,9 @@ export default {
return this.analyzersConfiguration.length > 0; return this.analyzersConfiguration.length > 0;
}, },
}, },
beforeMount() {
this.shouldRenderAnalyzersTip();
},
methods: { methods: {
onSubmit() { onSubmit() {
this.isSubmitting = true; this.isSubmitting = true;
...@@ -100,7 +105,20 @@ export default { ...@@ -100,7 +105,20 @@ export default {
analyzers: this.analyzersConfiguration.map(toSastCiConfigurationAnalyzerEntityInput), analyzers: this.analyzersConfiguration.map(toSastCiConfigurationAnalyzerEntityInput),
}; };
}, },
shouldRenderAnalyzersTip() {
this.analyzersConfiguration.some((analyzer) => {
if (analyzer.enabled === false && this.showAnalyzersTip === false) {
this.showAnalyzersTip = true;
return true;
}
return false;
});
},
onAnalyzerChange(name, updatedAnalyzer) { onAnalyzerChange(name, updatedAnalyzer) {
// show AnalyzersTip when Analyzer was unchecked
if (updatedAnalyzer.enabled === false && this.showAnalyzersTip === false) {
this.showAnalyzersTip = true;
}
const index = this.analyzersConfiguration.findIndex((analyzer) => analyzer.name === name); const index = this.analyzersConfiguration.findIndex((analyzer) => analyzer.name === name);
if (index === -1) { if (index === -1) {
return; return;
...@@ -108,6 +126,9 @@ export default { ...@@ -108,6 +126,9 @@ export default {
this.analyzersConfiguration.splice(index, 1, updatedAnalyzer); this.analyzersConfiguration.splice(index, 1, updatedAnalyzer);
}, },
dismissAnalyzersTip() {
this.showAnalyzersTip = false;
},
}, },
i18n: { i18n: {
submissionError: s__( submissionError: s__(
...@@ -122,6 +143,10 @@ export default { ...@@ -122,6 +143,10 @@ export default {
cover all languages across your project, and only run if the language is cover all languages across your project, and only run if the language is
detected in the Merge Request.`, detected in the Merge Request.`,
), ),
analyzersTipHeading: s__('We recommend leaving all SAST analyzers enabled'),
analyzersTipBody: s__(
'Keeping all SAST analyzers enabled future-proofs the project in case new languages are added later on. Determining which analyzers apply is a process that consumes minimal resources and adds minimal time to the pipeline. Leaving all SAST analyzers enabled ensures maximum coverage.',
),
}, },
}; };
</script> </script>
...@@ -157,13 +182,27 @@ export default { ...@@ -157,13 +182,27 @@ export default {
:entity="analyzer" :entity="analyzer"
@input="onAnalyzerChange(analyzer.name, $event)" @input="onAnalyzerChange(analyzer.name, $event)"
/> />
<gl-alert
v-if="showAnalyzersTip"
data-testid="analyzers-section-tip"
:title="$options.i18n.analyzersTipHeading"
variant="tip"
@dismiss="dismissAnalyzersTip"
>
<gl-sprintf :message="$options.i18n.analyzersTipBody" />
</gl-alert>
</expandable-section> </expandable-section>
<hr v-else /> <hr v-else />
<gl-alert v-if="hasSubmissionError" class="gl-mb-5" variant="danger" :dismissible="false">{{ <gl-alert
$options.i18n.submissionError v-if="hasSubmissionError"
}}</gl-alert> data-testid="analyzers-error-alert"
class="gl-mb-5"
variant="danger"
:dismissible="false"
>{{ $options.i18n.submissionError }}</gl-alert
>
<div class="gl-display-flex"> <div class="gl-display-flex">
<gl-button <gl-button
......
---
title: Introduce disabled analyzer warning to SAST Config Page
merge_request: 55172
author:
type: changed
...@@ -28,12 +28,12 @@ export const makeEntities = (count, changes) => ...@@ -28,12 +28,12 @@ export const makeEntities = (count, changes) =>
* generated entities. * generated entities.
* @returns {Object[]} * @returns {Object[]}
*/ */
export const makeAnalyzerEntities = (count, changes) => export const makeAnalyzerEntities = (count, changes, isEnabled = true) =>
[...Array(count).keys()].map((i) => ({ [...Array(count).keys()].map((i) => ({
name: `nameValue${i}`, name: `nameValue${i}`,
label: `label${i}`, label: `label${i}`,
description: `description${i}`, description: `description${i}`,
enabled: true, enabled: isEnabled,
...changes, ...changes,
})); }));
...@@ -43,7 +43,7 @@ export const makeAnalyzerEntities = (count, changes) => ...@@ -43,7 +43,7 @@ export const makeAnalyzerEntities = (count, changes) =>
* @param {number} totalEntities - The total number of entities to create. * @param {number} totalEntities - The total number of entities to create.
* @returns {SastCiConfiguration} * @returns {SastCiConfiguration}
*/ */
export const makeSastCiConfiguration = () => { export const makeSastCiConfiguration = (analyzerEnabled = true) => {
// Call makeEntities just once to ensure unique fields // Call makeEntities just once to ensure unique fields
const entities = makeEntities(3); const entities = makeEntities(3);
...@@ -55,11 +55,15 @@ export const makeSastCiConfiguration = () => { ...@@ -55,11 +55,15 @@ export const makeSastCiConfiguration = () => {
nodes: [entities.shift()], nodes: [entities.shift()],
}, },
analyzers: { analyzers: {
nodes: makeAnalyzerEntities(1, { nodes: makeAnalyzerEntities(
variables: { 1,
nodes: [entities.shift()], {
variables: {
nodes: [entities.shift()],
},
}, },
}), analyzerEnabled,
),
}, },
}; };
}; };
import { GlAlert, GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
...@@ -29,7 +29,10 @@ describe('ConfigurationForm component', () => { ...@@ -29,7 +29,10 @@ describe('ConfigurationForm component', () => {
}; };
const createComponent = ({ mutationResult, ...options } = {}) => { const createComponent = ({ mutationResult, ...options } = {}) => {
sastCiConfiguration = makeSastCiConfiguration(); sastCiConfiguration =
options?.analyzerEnabled === false
? makeSastCiConfiguration(options?.analyzerEnabled)
: makeSastCiConfiguration();
wrapper = shallowMount( wrapper = shallowMount(
ConfigurationForm, ConfigurationForm,
...@@ -65,11 +68,12 @@ describe('ConfigurationForm component', () => { ...@@ -65,11 +68,12 @@ describe('ConfigurationForm component', () => {
const findForm = () => wrapper.find('form'); const findForm = () => wrapper.find('form');
const findSubmitButton = () => wrapper.find({ ref: 'submitButton' }); const findSubmitButton = () => wrapper.find({ ref: 'submitButton' });
const findErrorAlert = () => wrapper.find(GlAlert); const findErrorAlert = () => wrapper.find('[data-testid="analyzers-error-alert"]');
const findCancelButton = () => wrapper.find({ ref: 'cancelButton' }); const findCancelButton = () => wrapper.find({ ref: 'cancelButton' });
const findDynamicFieldsComponents = () => wrapper.findAll(DynamicFields); const findDynamicFieldsComponents = () => wrapper.findAll(DynamicFields);
const findAnalyzerConfigurations = () => wrapper.findAll(AnalyzerConfiguration); const findAnalyzerConfigurations = () => wrapper.findAll(AnalyzerConfiguration);
const findAnalyzersSection = () => wrapper.find('[data-testid="analyzers-section"]'); const findAnalyzersSection = () => wrapper.find('[data-testid="analyzers-section"]');
const findAnalyzersSectionTip = () => wrapper.find('[data-testid="analyzers-section-tip"]');
const expectPayloadForEntities = () => { const expectPayloadForEntities = () => {
const expectedPayload = { const expectedPayload = {
...@@ -188,6 +192,11 @@ describe('ConfigurationForm component', () => { ...@@ -188,6 +192,11 @@ describe('ConfigurationForm component', () => {
}); });
}); });
it('does not render alert-tip', () => {
const analyzersSectionTip = findAnalyzersSectionTip();
expect(analyzersSectionTip.exists()).toBe(false);
});
describe('when an AnalyzerConfiguration emits an input event', () => { describe('when an AnalyzerConfiguration emits an input event', () => {
let analyzer; let analyzer;
let updatedEntity; let updatedEntity;
...@@ -205,6 +214,52 @@ describe('ConfigurationForm component', () => { ...@@ -205,6 +214,52 @@ describe('ConfigurationForm component', () => {
expect(analyzer.props('entity')).toBe(updatedEntity); expect(analyzer.props('entity')).toBe(updatedEntity);
}); });
}); });
describe('when at least 1 analyzer gets disabled', () => {
let analyzer;
let updatedEntity;
beforeEach(() => {
analyzer = findAnalyzerConfigurations().at(0);
// eslint-disable-next-line prefer-destructuring
updatedEntity = sastCiConfiguration.analyzers.nodes[0];
updatedEntity.enabled = false;
analyzer.vm.$emit('input', updatedEntity);
});
it('renders alert-tip', () => {
const analyzersSectionTip = findAnalyzersSectionTip();
expect(analyzersSectionTip.exists()).toBe(true);
expect(analyzersSectionTip.html()).toContain(ConfigurationForm.i18n.analyzersTipHeading);
expect(analyzersSectionTip.html()).toContain(ConfigurationForm.i18n.analyzersTipBody);
});
describe('when alert-tip is dismissed', () => {
beforeEach(() => {
findAnalyzersSectionTip().vm.$emit('dismiss');
return wrapper.vm.$nextTick();
});
it('should not be displayed', () => {
expect(findAnalyzersSectionTip().exists()).toBe(false);
});
});
});
});
describe('on Load with disabled analyzers', () => {
beforeEach(() => {
createComponent({
analyzerEnabled: false,
});
});
it('renders alert-tip', () => {
const analyzersSectionTip = findAnalyzersSectionTip();
expect(analyzersSectionTip.exists()).toBe(true);
expect(analyzersSectionTip.html()).toContain(ConfigurationForm.i18n.analyzersTipHeading);
expect(analyzersSectionTip.html()).toContain(ConfigurationForm.i18n.analyzersTipBody);
});
}); });
describe('when submitting the form', () => { describe('when submitting the form', () => {
......
...@@ -17362,6 +17362,9 @@ msgstr "" ...@@ -17362,6 +17362,9 @@ msgstr ""
msgid "Keep editing" msgid "Keep editing"
msgstr "" msgstr ""
msgid "Keeping all SAST analyzers enabled future-proofs the project in case new languages are added later on. Determining which analyzers apply is a process that consumes minimal resources and adds minimal time to the pipeline. Leaving all SAST analyzers enabled ensures maximum coverage."
msgstr ""
msgid "Kerberos access denied" msgid "Kerberos access denied"
msgstr "" msgstr ""
...@@ -33422,6 +33425,9 @@ msgstr "" ...@@ -33422,6 +33425,9 @@ msgstr ""
msgid "We recommend cloud-based mobile authenticator apps such as Authy, Duo Mobile, and LastPass. They can restore access if you lose your hardware device." msgid "We recommend cloud-based mobile authenticator apps such as Authy, Duo Mobile, and LastPass. They can restore access if you lose your hardware device."
msgstr "" msgstr ""
msgid "We recommend leaving all SAST analyzers enabled"
msgstr ""
msgid "We recommend that you buy more Pipeline minutes to avoid any interruption of service." msgid "We recommend that you buy more Pipeline minutes to avoid any interruption of service."
msgstr "" msgstr ""
......
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