Commit 9dee2023 authored by Zamir Martins's avatar Zamir Martins Committed by Jose Ivan Vargas

Add severity_levels in UI dialogs related to project approval rules

parent 86abff01
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { GlFormGroup, GlFormInput, GlDropdown, GlTruncate, GlDropdownItem } from '@gitlab/ui'; import { GlFormGroup, GlFormInput, GlDropdown, GlTruncate, GlDropdownItem } from '@gitlab/ui';
import { groupBy, isEqual, isNumber, omit } from 'lodash'; import { groupBy, isEqual, isNumber, omit } from 'lodash';
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { REPORT_TYPES } from 'ee/security_dashboard/store/constants'; import { REPORT_TYPES, SEVERITY_LEVELS } from 'ee/security_dashboard/store/constants';
import ProtectedBranchesSelector from 'ee/vue_shared/components/branches_selector/protected_branches_selector.vue'; import ProtectedBranchesSelector from 'ee/vue_shared/components/branches_selector/protected_branches_selector.vue';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import { import {
...@@ -71,6 +71,7 @@ export default { ...@@ -71,6 +71,7 @@ export default {
containsHiddenGroups: false, containsHiddenGroups: false,
serverValidationErrors: [], serverValidationErrors: [],
scanners: [], scanners: [],
severityLevels: [],
...this.getInitialData(), ...this.getInitialData(),
}; };
}, },
...@@ -98,11 +99,11 @@ export default { ...@@ -98,11 +99,11 @@ export default {
invalidName() { invalidName() {
if (this.isMultiSubmission) { if (this.isMultiSubmission) {
if (this.serverValidationErrors.includes('name has already been taken')) { if (this.serverValidationErrors.includes('name has already been taken')) {
return this.$options.APPROVAL_DIALOG_I18N.validations.ruleNameTaken; return APPROVAL_DIALOG_I18N.validations.ruleNameTaken;
} }
if (!this.name) { if (!this.name) {
return this.$options.APPROVAL_DIALOG_I18N.validations.ruleNameMissing; return APPROVAL_DIALOG_I18N.validations.ruleNameMissing;
} }
} }
...@@ -110,15 +111,15 @@ export default { ...@@ -110,15 +111,15 @@ export default {
}, },
invalidApprovalsRequired() { invalidApprovalsRequired() {
if (!isNumber(this.approvalsRequired)) { if (!isNumber(this.approvalsRequired)) {
return this.$options.APPROVAL_DIALOG_I18N.validations.approvalsRequiredNotNumber; return APPROVAL_DIALOG_I18N.validations.approvalsRequiredNotNumber;
} }
if (this.approvalsRequired < 0) { if (this.approvalsRequired < 0) {
return this.$options.APPROVAL_DIALOG_I18N.validations.approvalsRequiredNegativeNumber; return APPROVAL_DIALOG_I18N.validations.approvalsRequiredNegativeNumber;
} }
if (this.approvalsRequired < this.minApprovalsRequired) { if (this.approvalsRequired < this.minApprovalsRequired) {
return sprintf(this.$options.APPROVAL_DIALOG_I18N.validations.approvalsRequiredMinimum, { return sprintf(APPROVAL_DIALOG_I18N.validations.approvalsRequiredMinimum, {
number: this.minApprovalsRequired, number: this.minApprovalsRequired,
}); });
} }
...@@ -127,7 +128,7 @@ export default { ...@@ -127,7 +128,7 @@ export default {
}, },
invalidApprovers() { invalidApprovers() {
if (this.isMultiSubmission && this.approvers.length <= 0) { if (this.isMultiSubmission && this.approvers.length <= 0) {
return this.$options.APPROVAL_DIALOG_I18N.validations.approversRequired; return APPROVAL_DIALOG_I18N.validations.approversRequired;
} }
return ''; return '';
...@@ -137,25 +138,33 @@ export default { ...@@ -137,25 +138,33 @@ export default {
!this.isMrEdit && !this.isMrEdit &&
!this.branches.every((branch) => isEqual(branch, ANY_BRANCH) || isNumber(branch?.id)) !this.branches.every((branch) => isEqual(branch, ANY_BRANCH) || isNumber(branch?.id))
) { ) {
return this.$options.APPROVAL_DIALOG_I18N.validations.branchesRequired; return APPROVAL_DIALOG_I18N.validations.branchesRequired;
} }
return ''; return '';
}, },
invalidScanners() { invalidScanners() {
if (this.scanners.length <= 0) { if (this.scanners.length <= 0) {
return this.$options.APPROVAL_DIALOG_I18N.validations.scannersRequired; return APPROVAL_DIALOG_I18N.validations.scannersRequired;
} }
return ''; return '';
}, },
invalidVulnerabilitiesAllowedError() { invalidVulnerabilitiesAllowedError() {
if (!isNumber(this.vulnerabilitiesAllowed)) { if (!isNumber(this.vulnerabilitiesAllowed)) {
return this.$options.APPROVAL_DIALOG_I18N.validations.approvalsRequiredNotNumber; return APPROVAL_DIALOG_I18N.validations.approvalsRequiredNotNumber;
} }
if (this.vulnerabilitiesAllowed < 0) { if (this.vulnerabilitiesAllowed < 0) {
return this.$options.APPROVAL_DIALOG_I18N.validations.vulnerabilitiesAllowedMinimum; return APPROVAL_DIALOG_I18N.validations.vulnerabilitiesAllowedMinimum;
} }
return '';
},
invalidSeverityLevels() {
if (this.severityLevels.length === 0) {
return APPROVAL_DIALOG_I18N.validations.severityLevelsRequired;
}
return ''; return '';
}, },
isValid() { isValid() {
...@@ -165,7 +174,8 @@ export default { ...@@ -165,7 +174,8 @@ export default {
this.isValidApprovalsRequired && this.isValidApprovalsRequired &&
this.isValidApprovers && this.isValidApprovers &&
this.areValidScanners && this.areValidScanners &&
this.isValidVulnerabilitiesAllowed this.isValidVulnerabilitiesAllowed &&
this.areValidSeverityLevels
); );
}, },
isValidName() { isValidName() {
...@@ -190,6 +200,9 @@ export default { ...@@ -190,6 +200,9 @@ export default {
!this.invalidVulnerabilitiesAllowedError !this.invalidVulnerabilitiesAllowedError
); );
}, },
areValidSeverityLevels() {
return !this.showValidation || !this.isVulnerabilityCheck || !this.invalidSeverityLevels;
},
isMultiSubmission() { isMultiSubmission() {
return this.settings.allowMultiRule && !this.isFallbackSubmission; return this.settings.allowMultiRule && !this.isFallbackSubmission;
}, },
...@@ -228,6 +241,7 @@ export default { ...@@ -228,6 +241,7 @@ export default {
removeHiddenGroups: this.removeHiddenGroups, removeHiddenGroups: this.removeHiddenGroups,
protectedBranchIds: this.branches.map((x) => x.id), protectedBranchIds: this.branches.map((x) => x.id),
scanners: this.scanners, scanners: this.scanners,
severityLevels: this.severityLevels,
}; };
}, },
isEditing() { isEditing() {
...@@ -242,15 +256,33 @@ export default { ...@@ -242,15 +256,33 @@ export default {
scannersText() { scannersText() {
switch (this.scanners.length) { switch (this.scanners.length) {
case Object.values(this.$options.REPORT_TYPES).length: case Object.values(this.$options.REPORT_TYPES).length:
return this.$options.APPROVAL_DIALOG_I18N.form.allScannersSelectedLabel; return APPROVAL_DIALOG_I18N.form.allScannersSelectedLabel;
case 0: case 0:
return this.$options.APPROVAL_DIALOG_I18N.form.scannersSelectLabel; return APPROVAL_DIALOG_I18N.form.scannersSelectLabel;
case 1: case 1:
return this.$options.REPORT_TYPES[this.scanners[0]]; return this.$options.REPORT_TYPES[this.scanners[0]];
default: default:
return sprintf(this.$options.APPROVAL_DIALOG_I18N.form.multipleSelectedScannersLabel, { return sprintf(APPROVAL_DIALOG_I18N.form.multipleSelectedLabel, {
scanner: this.$options.REPORT_TYPES[this.scanners[0]], firstLabel: this.$options.REPORT_TYPES[this.scanners[0]],
additionalScanners: this.scanners.length - 1, numberOfAdditionalLabels: this.scanners.length - 1,
});
}
},
areAllSeverityLevelsSelected() {
return this.severityLevels.length === Object.values(this.$options.SEVERITY_LEVELS).length;
},
severityLevelsText() {
switch (this.severityLevels.length) {
case Object.keys(this.$options.SEVERITY_LEVELS).length:
return APPROVAL_DIALOG_I18N.form.allSeverityLevelsSelectedLabel;
case 0:
return APPROVAL_DIALOG_I18N.form.severityLevelsSelectLabel;
case 1:
return this.$options.SEVERITY_LEVELS[this.severityLevels[0]];
default:
return sprintf(APPROVAL_DIALOG_I18N.form.multipleSelectedLabel, {
firstLabel: this.$options.SEVERITY_LEVELS[this.severityLevels[0]],
numberOfAdditionalLabels: this.severityLevels.length - 1,
}); });
} }
}, },
...@@ -371,6 +403,7 @@ export default { ...@@ -371,6 +403,7 @@ export default {
branches, branches,
scanners: this.initRule.scanners || [], scanners: this.initRule.scanners || [],
vulnerabilitiesAllowed: this.initRule.vulnerabilitiesAllowed || 0, vulnerabilitiesAllowed: this.initRule.vulnerabilitiesAllowed || 0,
severityLevels: this.initRule.severityLevels || [],
}; };
}, },
setAllSelectedScanners() { setAllSelectedScanners() {
...@@ -387,9 +420,26 @@ export default { ...@@ -387,9 +420,26 @@ export default {
this.scanners.splice(pos, 1); this.scanners.splice(pos, 1);
} }
}, },
setAllSelectedSeverityLevels() {
this.severityLevels = this.areAllSeverityLevelsSelected
? []
: Object.keys(this.$options.SEVERITY_LEVELS);
},
isSeveritySelected(severity) {
return this.severityLevels.includes(severity);
},
setSeverity(severity) {
const pos = this.severityLevels.indexOf(severity);
if (pos === -1) {
this.severityLevels.push(severity);
} else {
this.severityLevels.splice(pos, 1);
}
},
}, },
APPROVAL_DIALOG_I18N, APPROVAL_DIALOG_I18N,
REPORT_TYPES: omit(REPORT_TYPES, EXCLUDED_REPORT_TYPE), REPORT_TYPES: omit(REPORT_TYPES, EXCLUDED_REPORT_TYPE),
SEVERITY_LEVELS,
}; };
</script> </script>
...@@ -441,7 +491,7 @@ export default { ...@@ -441,7 +491,7 @@ export default {
:is-checked="areAllScannersSelected" :is-checked="areAllScannersSelected"
@click.native.capture.stop="setAllSelectedScanners" @click.native.capture.stop="setAllSelectedScanners"
> >
<gl-truncate :text="$options.APPROVAL_DIALOG_I18N.form.selectAllScannersLabel" /> <gl-truncate :text="$options.APPROVAL_DIALOG_I18N.form.selectAllLabel" />
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item <gl-dropdown-item
v-for="(value, key) in $options.REPORT_TYPES" v-for="(value, key) in $options.REPORT_TYPES"
...@@ -454,6 +504,34 @@ export default { ...@@ -454,6 +504,34 @@ export default {
</gl-dropdown-item> </gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
</gl-form-group> </gl-form-group>
<gl-form-group
v-if="isVulnerabilityCheck"
:label="$options.APPROVAL_DIALOG_I18N.form.severityLevelsLabel"
:description="$options.APPROVAL_DIALOG_I18N.form.severityLevelsDescription"
:state="areValidSeverityLevels"
:invalid-feedback="invalidSeverityLevels"
data-testid="severity-levels-group"
>
<gl-dropdown :text="severityLevelsText">
<gl-dropdown-item
key="all"
is-check-item
:is-checked="areAllSeverityLevelsSelected"
@click.native.capture.stop="setAllSelectedSeverityLevels"
>
<gl-truncate :text="$options.APPROVAL_DIALOG_I18N.form.selectAllLabel" />
</gl-dropdown-item>
<gl-dropdown-item
v-for="(value, key) in $options.SEVERITY_LEVELS"
:key="key"
is-check-item
:is-checked="isSeveritySelected(key)"
@click.native.capture.stop="setSeverity(key)"
>
<gl-truncate :text="value" />
</gl-dropdown-item>
</gl-dropdown>
</gl-form-group>
<gl-form-group <gl-form-group
:label="$options.APPROVAL_DIALOG_I18N.form.approvalsRequiredLabel" :label="$options.APPROVAL_DIALOG_I18N.form.approvalsRequiredLabel"
:state="isValidApprovalsRequired" :state="isValidApprovalsRequired"
......
...@@ -90,13 +90,19 @@ export const APPROVAL_DIALOG_I18N = { ...@@ -90,13 +90,19 @@ export const APPROVAL_DIALOG_I18N = {
scannersDescription: s__( scannersDescription: s__(
'ApprovalRule|Apply this approval rule to consider only the selected security scanners.', 'ApprovalRule|Apply this approval rule to consider only the selected security scanners.',
), ),
selectAllScannersLabel: s__('ApprovalRule|Select All'), selectAllLabel: s__('ApprovalRule|Select All'),
allScannersSelectedLabel: s__('ApprovalRule|All scanners'), allScannersSelectedLabel: s__('ApprovalRule|All scanners'),
multipleSelectedScannersLabel: s__('ApprovalRule|%{scanner} +%{additionalScanners} more'), multipleSelectedLabel: s__('ApprovalRule|%{firstLabel} +%{numberOfAdditionalLabels} more'),
vulnerabilitiesAllowedLabel: s__('ApprovalRule|Vulnerabilities allowed'), vulnerabilitiesAllowedLabel: s__('ApprovalRule|Vulnerabilities allowed'),
vulnerabilitiesAllowedDescription: s__( vulnerabilitiesAllowedDescription: s__(
'ApprovalRule|Number of vulnerabilities allowed before approval rule is triggered.', 'ApprovalRule|Number of vulnerabilities allowed before approval rule is triggered.',
), ),
severityLevelsLabel: s__('ApprovalRule|Severity levels'),
severityLevelsDescription: s__(
'ApprovalRule|Apply this approval rule to consider only the selected severity levels.',
),
severityLevelsSelectLabel: s__('ApprovalRule|Select severity levels'),
allSeverityLevelsSelectedLabel: s__('ApprovalRule|All severity levels'),
}, },
validations: { validations: {
approvalsRequiredNegativeNumber: __('Please enter a non-negative number'), approvalsRequiredNegativeNumber: __('Please enter a non-negative number'),
...@@ -112,5 +118,6 @@ export const APPROVAL_DIALOG_I18N = { ...@@ -112,5 +118,6 @@ export const APPROVAL_DIALOG_I18N = {
vulnerabilitiesAllowedMinimum: s__( vulnerabilitiesAllowedMinimum: s__(
'ApprovalRule|Please enter a number equal or greater than zero', 'ApprovalRule|Please enter a number equal or greater than zero',
), ),
severityLevelsRequired: s__('ApprovalRule|Please select at least one severity level'),
}, },
}; };
...@@ -33,6 +33,7 @@ export const mapApprovalRuleRequest = (req) => ({ ...@@ -33,6 +33,7 @@ export const mapApprovalRuleRequest = (req) => ({
protected_branch_ids: req.protectedBranchIds, protected_branch_ids: req.protectedBranchIds,
scanners: req.scanners, scanners: req.scanners,
vulnerabilities_allowed: req.vulnerabilitiesAllowed, vulnerabilities_allowed: req.vulnerabilitiesAllowed,
severity_levels: req.severityLevels,
}); });
export const mapApprovalFallbackRuleRequest = (req) => ({ export const mapApprovalFallbackRuleRequest = (req) => ({
...@@ -54,6 +55,7 @@ export const mapApprovalRuleResponse = (res) => ({ ...@@ -54,6 +55,7 @@ export const mapApprovalRuleResponse = (res) => ({
overridden: res.overridden, overridden: res.overridden,
scanners: res.scanners, scanners: res.scanners,
vulnerabilitiesAllowed: res.vulnerabilities_allowed, vulnerabilitiesAllowed: res.vulnerabilities_allowed,
severityLevels: res.severity_levels,
}); });
export const mapApprovalSettingsResponse = (res) => ({ export const mapApprovalSettingsResponse = (res) => ({
......
...@@ -41,6 +41,7 @@ const TEST_RULE_VULNERABILITY_CHECK = { ...@@ -41,6 +41,7 @@ const TEST_RULE_VULNERABILITY_CHECK = {
name: VULNERABILITY_CHECK_NAME, name: VULNERABILITY_CHECK_NAME,
scanners: ['sast', 'dast'], scanners: ['sast', 'dast'],
vulnerabilitiesAllowed: 0, vulnerabilitiesAllowed: 0,
severityLevels: ['high'],
}; };
const TEST_APPROVERS = [{ id: 7, type: TYPE_USER }]; const TEST_APPROVERS = [{ id: 7, type: TYPE_USER }];
const TEST_APPROVALS_REQUIRED = 3; const TEST_APPROVALS_REQUIRED = 3;
...@@ -99,6 +100,7 @@ describe('EE Approvals RuleForm', () => { ...@@ -99,6 +100,7 @@ describe('EE Approvals RuleForm', () => {
const findBranchesValidation = () => wrapper.findByTestId('branches-group'); const findBranchesValidation = () => wrapper.findByTestId('branches-group');
const findScannersGroup = () => wrapper.findByTestId('scanners-group'); const findScannersGroup = () => wrapper.findByTestId('scanners-group');
const findVulnerabilityFormGroup = () => wrapper.findByTestId('vulnerability-amount-group'); const findVulnerabilityFormGroup = () => wrapper.findByTestId('vulnerability-amount-group');
const findSeverityLevelsGroup = () => wrapper.findByTestId('severity-levels-group');
const inputsAreValid = (inputs) => inputs.every((x) => x.props('state')); const inputsAreValid = (inputs) => inputs.every((x) => x.props('state'));
...@@ -203,6 +205,7 @@ describe('EE Approvals RuleForm', () => { ...@@ -203,6 +205,7 @@ describe('EE Approvals RuleForm', () => {
groupRecords, groupRecords,
removeHiddenGroups: false, removeHiddenGroups: false,
scanners: [], scanners: [],
severityLevels: [],
protectedBranchIds: branches.map((x) => x.id), protectedBranchIds: branches.map((x) => x.id),
}; };
...@@ -282,6 +285,7 @@ describe('EE Approvals RuleForm', () => { ...@@ -282,6 +285,7 @@ describe('EE Approvals RuleForm', () => {
groupRecords, groupRecords,
removeHiddenGroups: false, removeHiddenGroups: false,
scanners: [], scanners: [],
severityLevels: [],
protectedBranchIds: branches.map((x) => x.id), protectedBranchIds: branches.map((x) => x.id),
}; };
...@@ -362,6 +366,7 @@ describe('EE Approvals RuleForm', () => { ...@@ -362,6 +366,7 @@ describe('EE Approvals RuleForm', () => {
groupRecords, groupRecords,
removeHiddenGroups: false, removeHiddenGroups: false,
scanners: [], scanners: [],
severityLevels: [],
protectedBranchIds: [], protectedBranchIds: [],
}; };
...@@ -539,11 +544,11 @@ describe('EE Approvals RuleForm', () => { ...@@ -539,11 +544,11 @@ describe('EE Approvals RuleForm', () => {
describe('with approval suggestions', () => { describe('with approval suggestions', () => {
describe.each` describe.each`
defaultRuleName | expectedDisabledAttribute | expectedDisplayedScanners | expectedDisplayVulnerabilityAllowed defaultRuleName | expectedDisabledAttribute | expectedDisplayedScanners | expectedDisplayVulnerabilityAllowed | expectedDisplayedSeverityLevels
${VULNERABILITY_CHECK_NAME} | ${true} | ${true} | ${true} ${VULNERABILITY_CHECK_NAME} | ${true} | ${true} | ${true} | ${true}
${'License-Check'} | ${true} | ${false} | ${false} ${'License-Check'} | ${true} | ${false} | ${false} | ${false}
${'Coverage-Check'} | ${true} | ${false} | ${false} ${'Coverage-Check'} | ${true} | ${false} | ${false} | ${false}
${'Foo Bar Baz'} | ${false} | ${false} | ${false} ${'Foo Bar Baz'} | ${false} | ${false} | ${false} | ${false}
`( `(
'with defaultRuleName set to $defaultRuleName', 'with defaultRuleName set to $defaultRuleName',
({ ({
...@@ -551,6 +556,7 @@ describe('EE Approvals RuleForm', () => { ...@@ -551,6 +556,7 @@ describe('EE Approvals RuleForm', () => {
expectedDisabledAttribute, expectedDisabledAttribute,
expectedDisplayedScanners, expectedDisplayedScanners,
expectedDisplayVulnerabilityAllowed, expectedDisplayVulnerabilityAllowed,
expectedDisplayedSeverityLevels,
}) => { }) => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
...@@ -575,6 +581,11 @@ describe('EE Approvals RuleForm', () => { ...@@ -575,6 +581,11 @@ describe('EE Approvals RuleForm', () => {
} the number of vulnerabilities form group`, () => { } the number of vulnerabilities form group`, () => {
expect(findVulnerabilityFormGroup().exists()).toBe(expectedDisplayVulnerabilityAllowed); expect(findVulnerabilityFormGroup().exists()).toBe(expectedDisplayVulnerabilityAllowed);
}); });
it(`it ${
expectedDisplayedSeverityLevels ? 'shows' : 'does not show'
} severity levels dropdown`, () => {
expect(findSeverityLevelsGroup().exists()).toBe(expectedDisplayedSeverityLevels);
});
}, },
); );
}); });
...@@ -672,7 +683,41 @@ describe('EE Approvals RuleForm', () => { ...@@ -672,7 +683,41 @@ describe('EE Approvals RuleForm', () => {
it('contains the supported report types and select all option', () => { it('contains the supported report types and select all option', () => {
const supportedReportsPlusAll = const supportedReportsPlusAll =
Object.keys(REPORT_TYPES).length - [EXCLUDED_REPORT_TYPE].length + 1; Object.keys(REPORT_TYPES).length - [EXCLUDED_REPORT_TYPE].length + 1;
expect(wrapper.findAllComponents(GlTruncate)).toHaveLength(supportedReportsPlusAll); expect(findScannersGroup().findAllComponents(GlTruncate)).toHaveLength(
supportedReportsPlusAll,
);
});
});
describe('and without any severity levels selected', () => {
beforeEach(() => {
createComponent({
initRule: {
...TEST_RULE_VULNERABILITY_CHECK,
severityLevels: [],
},
});
findForm().trigger('submit');
});
it('does not dispatch the action on submit', () => {
expect(actions.postRule).not.toHaveBeenCalled();
});
});
describe('with one severity level selected', () => {
beforeEach(() => {
createComponent({
initRule: TEST_RULE_VULNERABILITY_CHECK,
});
findForm().trigger('submit');
});
it('dispatches the action on submit', () => {
expect(actions.postRule).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ severityLevels: ['high'] }),
);
}); });
}); });
}); });
......
...@@ -4166,7 +4166,7 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun ...@@ -4166,7 +4166,7 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "ApprovalRule|%{scanner} +%{additionalScanners} more" msgid "ApprovalRule|%{firstLabel} +%{numberOfAdditionalLabels} more"
msgstr "" msgstr ""
msgid "ApprovalRule|Add approvers" msgid "ApprovalRule|Add approvers"
...@@ -4175,9 +4175,15 @@ msgstr "" ...@@ -4175,9 +4175,15 @@ msgstr ""
msgid "ApprovalRule|All scanners" msgid "ApprovalRule|All scanners"
msgstr "" msgstr ""
msgid "ApprovalRule|All severity levels"
msgstr ""
msgid "ApprovalRule|Apply this approval rule to consider only the selected security scanners." msgid "ApprovalRule|Apply this approval rule to consider only the selected security scanners."
msgstr "" msgstr ""
msgid "ApprovalRule|Apply this approval rule to consider only the selected severity levels."
msgstr ""
msgid "ApprovalRule|Approval rules" msgid "ApprovalRule|Approval rules"
msgstr "" msgstr ""
...@@ -4205,6 +4211,9 @@ msgstr "" ...@@ -4205,6 +4211,9 @@ msgstr ""
msgid "ApprovalRule|Please select at least one security scanner" msgid "ApprovalRule|Please select at least one security scanner"
msgstr "" msgstr ""
msgid "ApprovalRule|Please select at least one severity level"
msgstr ""
msgid "ApprovalRule|Rule name" msgid "ApprovalRule|Rule name"
msgstr "" msgstr ""
...@@ -4217,6 +4226,12 @@ msgstr "" ...@@ -4217,6 +4226,12 @@ msgstr ""
msgid "ApprovalRule|Select scanners" msgid "ApprovalRule|Select scanners"
msgstr "" msgstr ""
msgid "ApprovalRule|Select severity levels"
msgstr ""
msgid "ApprovalRule|Severity levels"
msgstr ""
msgid "ApprovalRule|Target branch" msgid "ApprovalRule|Target branch"
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