Commit caa48d1a authored by Alexander Turinske's avatar Alexander Turinske

Update policy summary to be more readable

- update rules wording and consolidate into single sentence
- update tests

Changelog: changed

EE: true
parent 5ee31276
......@@ -11,3 +11,5 @@ export const NOT_ENABLED_LABEL = s__('SecurityOrchestration|Not enabled');
export const TYPE_TITLE = s__('SecurityOrchestration|Policy Type');
export const STATUS_TITLE = s__('SecurityOrchestration|Status');
export const SUMMARY_TITLE = s__('SecurityOrchestration|Summary');
<script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { s__ } from '~/locale';
import {
fromYaml,
humanizeActions,
humanizeRules,
} from '../policy_editor/scan_execution_policy/lib';
import { SUMMARY_TITLE } from './constants';
import PolicyDrawerLayout from './policy_drawer_layout.vue';
import PolicyInfoRow from './policy_info_row.vue';
export default {
i18n: {
action: s__('SecurityOrchestration|Action'),
rule: s__('SecurityOrchestration|Rule'),
summary: SUMMARY_TITLE,
scanExecution: s__('SecurityOrchestration|Scan execution'),
},
components: {
PolicyDrawerLayout,
PolicyInfoRow,
},
directives: {
SafeHtml,
},
props: {
policy: {
type: Object,
......@@ -50,12 +54,13 @@ export default {
:type="$options.i18n.scanExecution"
>
<template v-if="parsedYaml" #summary>
<policy-info-row data-testid="policy-rules" :label="$options.i18n.rule">
<p v-for="rule in humanizedRules" :key="rule">{{ rule }}</p>
</policy-info-row>
<policy-info-row data-testid="policy-actions" :label="$options.i18n.action">
<p v-for="action in humanizedActions" :key="action">{{ action }}</p>
<policy-info-row data-testid="policy-summary" :label="$options.i18n.summary">
<p v-safe-html="humanizedActions"></p>
<ul>
<li v-for="(rule, idx) in humanizedRules" :key="idx">
{{ rule }}
</li>
</ul>
</policy-info-row>
</template>
</policy-drawer-layout>
......
<script>
import { s__ } from '~/locale';
import { fromYaml, humanizeRules, humanizeAction } from '../policy_editor/scan_result_policy/lib';
import { SUMMARY_TITLE } from './constants';
import PolicyDrawerLayout from './policy_drawer_layout.vue';
import PolicyInfoRow from './policy_info_row.vue';
export default {
i18n: {
action: s__('SecurityOrchestration|Action'),
rule: s__('SecurityOrchestration|Rule'),
summary: SUMMARY_TITLE,
scanResult: s__('SecurityOrchestration|Scan result'),
},
components: {
......@@ -51,7 +51,7 @@ export default {
:type="$options.i18n.scanResult"
>
<template v-if="parsedYaml" #summary>
<policy-info-row data-testid="policy-rules" :label="$options.i18n.rule">
<policy-info-row data-testid="policy-summary" :label="$options.i18n.summary">
<p>{{ humanizedAction }}</p>
<ul>
<li v-for="(rule, idx) in humanizedRules" :key="idx">
......
......@@ -35,4 +35,8 @@ export const GRAPHQL_ERROR_MESSAGE = s__(
'SecurityOrchestration|There was a problem creating the new security policy',
);
export const NO_ACTION_MESSAGE = s__(
'SecurityOrchestration|No actions defined - policy will not run.',
);
export const NO_RULE_MESSAGE = s__('SecurityOrchestration|No rules defined - policy will not run.');
import cronstrue from 'cronstrue/i18n';
import { convertToTitleCase, humanize } from '~/lib/utils/text_utility';
import { getPreferredLocales, sprintf, s__, n__ } from '~/locale';
import { NO_RULE_MESSAGE } from '../../constants';
const getActionText = (scanType) =>
sprintf(s__('SecurityOrchestration|Executes a %{scanType} scan'), {
scanType: convertToTitleCase(humanize(scanType)),
});
import { NO_ACTION_MESSAGE, NO_RULE_MESSAGE } from '../../constants';
/**
* Create a human-readable list of strings, adding the necessary punctuation and conjunctions
......@@ -67,10 +62,29 @@ const HUMANIZE_RULES_METHODS = {
/**
* Create a human-readable version of the actions
* @param {Array} actions [{"scan":"dast","scanner_profile":"Scanner Profile","site_profile":"Site Profile"},{"type":"secret_detection"}]
* @returns {Set}
* @returns {String}
*/
export const humanizeActions = (actions) => {
return new Set(actions.map((action) => getActionText(action.scan)));
export const humanizeActions = (originalActions) => {
const actions = [...new Set(originalActions.map((a) => convertToTitleCase(humanize(a.scan))))];
if (!actions.length) {
return NO_ACTION_MESSAGE;
} else if (actions.length === 1) {
return sprintf(s__('SecurityOrchestration|Runs a <strong>%{actions}</strong> scan'), {
actions: actions.join(','),
});
}
const lastAction = actions.pop();
return sprintf(
s__(
'SecurityOrchestration|Runs <strong>%{actions}</strong> and <strong>%{lastAction}</strong> scans',
),
{
actions: actions.join(', '),
lastAction,
},
);
};
/**
......
import { sprintf, s__, n__ } from '~/locale';
import { convertToTitleCase, humanize } from '~/lib/utils/text_utility';
import { NO_RULE_MESSAGE } from '../../constants';
/**
......@@ -171,7 +172,7 @@ const humanizeRule = (rule) => {
),
{
scanners: humanizeItems({
items: rule.scanners,
items: rule.scanners.map((scanner) => convertToTitleCase(humanize(scanner))),
singular: s__('SecurityOrchestration|scanner finds'),
plural: s__('SecurityOrchestration|scanners find'),
}),
......
......@@ -6,7 +6,7 @@ import { mockScanResultPolicy } from '../../mocks/mock_data';
describe('ScanResultPolicy component', () => {
let wrapper;
const findRules = () => wrapper.findByTestId('policy-rules');
const findSummary = () => wrapper.findByTestId('policy-summary');
const factory = ({ propsData } = {}) => {
wrapper = shallowMountExtended(ScanResultPolicy, {
......@@ -26,8 +26,8 @@ describe('ScanResultPolicy component', () => {
factory({ propsData: { policy: mockScanResultPolicy } });
});
it('does render the policy rules', () => {
expect(findRules().exists()).toBe(true);
it('does render the policy summary', () => {
expect(findSummary().exists()).toBe(true);
});
});
});
......@@ -3,7 +3,10 @@ import {
humanizeRules,
} from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
import { NO_RULE_MESSAGE } from 'ee/threat_monitoring/components/policy_editor/constants';
import {
NO_ACTION_MESSAGE,
NO_RULE_MESSAGE,
} from 'ee/threat_monitoring/components/policy_editor/constants';
jest.mock('~/locale', () => ({
getPreferredLocales: jest.fn().mockReturnValue(['en']),
......@@ -17,6 +20,7 @@ const mockActions = [
{ scan: 'dast', scanner_profile: 'Scanner Profile', site_profile: 'Site Profile' },
{ scan: 'dast', scanner_profile: 'Scanner Profile 01', site_profile: 'Site Profile 01' },
{ scan: 'secret_detection' },
{ scan: 'container_image_scanning' },
];
const mockRules = [
......@@ -33,16 +37,16 @@ const mockRules = [
describe('humanizeActions', () => {
it('returns an empty Array of actions as an empty Set', () => {
expect(humanizeActions([])).toStrictEqual(new Set());
expect(humanizeActions([])).toStrictEqual(NO_ACTION_MESSAGE);
});
it('returns a single action as human-readable string', () => {
expect(humanizeActions([mockActions[0]])).toStrictEqual(new Set(['Executes a Dast scan']));
expect(humanizeActions([mockActions[0]])).toStrictEqual('Runs a <strong>Dast</strong> scan');
});
it('returns multiple actions as human-readable strings', () => {
expect(humanizeActions(mockActions)).toStrictEqual(
new Set(['Executes a Dast scan', 'Executes a Secret Detection scan']),
'Runs <strong>Dast, Secret Detection</strong> and <strong>Container Image Scanning</strong> scans',
);
});
});
......
......@@ -59,6 +59,11 @@ const mockRules = [
},
];
const mockRulesHumanized = [
'The Sast scanner finds a critical vulnerability in an open merge request targeting the main branch.',
'The Dast or Sast scanners find info or critical vulnerabilities in an open merge request targeting the master or main branches.',
];
const mockRulesEmptyBranch = {
type: 'scan_finding',
branches: [],
......@@ -74,21 +79,16 @@ describe('humanizeRules', () => {
});
it('returns a single rule as a human-readable string for user approvers only', () => {
expect(humanizeRules([mockRules[0]])).toStrictEqual([
'The sast scanner finds a critical vulnerability in an open merge request targeting the main branch.',
]);
expect(humanizeRules([mockRules[0]])).toStrictEqual([mockRulesHumanized[0]]);
});
it('returns multiple rules with different number of branches as human-readable strings', () => {
expect(humanizeRules(mockRules)).toStrictEqual([
'The sast scanner finds a critical vulnerability in an open merge request targeting the main branch.',
'The dast or sast scanners find info or critical vulnerabilities in an open merge request targeting the master or main branches.',
]);
expect(humanizeRules(mockRules)).toStrictEqual(mockRulesHumanized);
});
it('returns a single rule as a human-readable string for all branches', () => {
expect(humanizeRules([mockRulesEmptyBranch])).toStrictEqual([
'The sast scanner finds a critical vulnerability in an open merge request targeting all branches.',
'The Sast scanner finds a critical vulnerability in an open merge request targeting all branches.',
]);
});
});
......
......@@ -32482,9 +32482,6 @@ msgstr ""
msgid "SecurityOrchestration|.yaml preview"
msgstr ""
msgid "SecurityOrchestration|Action"
msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
......@@ -32527,9 +32524,6 @@ msgstr ""
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|Executes a %{scanType} scan"
msgstr ""
msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}."
msgstr ""
......@@ -32545,6 +32539,9 @@ msgstr ""
msgid "SecurityOrchestration|New policy"
msgstr ""
msgid "SecurityOrchestration|No actions defined - policy will not run."
msgstr ""
msgid "SecurityOrchestration|No description"
msgstr ""
......@@ -32587,10 +32584,13 @@ msgstr ""
msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:"
msgstr ""
msgid "SecurityOrchestration|Rule"
msgid "SecurityOrchestration|Rules"
msgstr ""
msgid "SecurityOrchestration|Rules"
msgid "SecurityOrchestration|Runs <strong>%{actions}</strong> and <strong>%{lastAction}</strong> scans"
msgstr ""
msgid "SecurityOrchestration|Runs a <strong>%{actions}</strong> scan"
msgstr ""
msgid "SecurityOrchestration|Scan Execution"
......
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