diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts
index 273ea72a2ffe3..21fa780badd84 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts
@@ -222,8 +222,9 @@ export const risk_score_mapping_value = t.string;
export const risk_score_mapping_item = t.exact(
t.type({
field: risk_score_mapping_field,
- operator,
value: risk_score_mapping_value,
+ operator,
+ risk_score: riskScoreOrUndefined,
})
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx
index 2a6cd3fc5bb7a..0d98a0f2f26ff 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx
@@ -331,6 +331,7 @@ describe('helpers', () => {
const result: ListItems[] = buildSeverityDescription({
value: 'low',
mapping: [{ field: 'host.name', operator: 'equals', value: 'hello', severity: 'high' }],
+ isMappingChecked: true,
});
expect(result[0].title).toEqual('Severity');
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx
index 1110c8c098988..600bc999849d1 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx
@@ -35,6 +35,7 @@ import { SeverityBadge } from '../severity_badge';
import ListTreeIcon from './assets/list_tree_icon.svg';
import { assertUnreachable } from '../../../../common/lib/helpers';
import { AboutStepRiskScore, AboutStepSeverity } from '../../../pages/detection_engine/rules/types';
+import { defaultToEmptyTag } from '../../../../common/components/empty_value';
const NoteDescriptionContainer = styled(EuiFlexItem)`
height: 105px;
@@ -236,35 +237,44 @@ export const buildSeverityDescription = (severity: AboutStepSeverity): ListItems
title: i18nSeverity.DEFAULT_SEVERITY,
description: ,
},
- ...severity.mapping.map((severityItem, index) => {
- return {
- title: index === 0 ? i18nSeverity.SEVERITY_MAPPING : '',
- description: (
-
-
-
- <>{severityItem.field}>
-
-
-
- <>{severityItem.value}>
-
-
-
-
-
-
-
-
- ),
- };
- }),
+ ...(severity.isMappingChecked
+ ? severity.mapping
+ .filter((severityItem) => severityItem.field !== '')
+ .map((severityItem, index) => {
+ return {
+ title: index === 0 ? i18nSeverity.SEVERITY_MAPPING : '',
+ description: (
+
+
+
+ <>{`${severityItem.field}:`}>
+
+
+
+
+ {defaultToEmptyTag(severityItem.value)}
+
+
+
+
+
+
+
+
+
+ ),
+ };
+ })
+ : []),
];
export const buildRiskScoreDescription = (riskScore: AboutStepRiskScore): ListItems[] => [
@@ -272,27 +282,31 @@ export const buildRiskScoreDescription = (riskScore: AboutStepRiskScore): ListIt
title: i18nRiskScore.RISK_SCORE,
description: riskScore.value,
},
- ...riskScore.mapping.map((riskScoreItem, index) => {
- return {
- title: index === 0 ? i18nRiskScore.RISK_SCORE_MAPPING : '',
- description: (
-
-
-
- <>{riskScoreItem.field}>
-
-
-
-
-
- {'signal.rule.risk_score'}
-
- ),
- };
- }),
+ ...(riskScore.isMappingChecked
+ ? riskScore.mapping
+ .filter((riskScoreItem) => riskScoreItem.field !== '')
+ .map((riskScoreItem, index) => {
+ return {
+ title: index === 0 ? i18nRiskScore.RISK_SCORE_MAPPING : '',
+ description: (
+
+
+
+ <>{riskScoreItem.field}>
+
+
+
+
+
+ {'signal.rule.risk_score'}
+
+ ),
+ };
+ })
+ : []),
];
const MyRefUrlLink = styled(EuiLink)`
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
index c9e2cb1a8ca24..35816e82540d1 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx
@@ -14,8 +14,9 @@ import {
EuiIcon,
EuiSpacer,
} from '@elastic/eui';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
+import { noop } from 'lodash/fp';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { CommonUseField } from '../../../../cases/components/create';
@@ -24,6 +25,10 @@ import { FieldComponent } from '../../../../common/components/autocomplete/field
import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns';
+const RiskScoreMappingEuiFormRow = styled(EuiFormRow)`
+ width: 468px;
+`;
+
const NestedContent = styled.div`
margin-left: 24px;
`;
@@ -41,6 +46,7 @@ interface RiskScoreFieldProps {
field: FieldHook;
idAria: string;
indices: IIndexPattern;
+ isDisabled: boolean;
placeholder?: string;
}
@@ -49,40 +55,23 @@ export const RiskScoreField = ({
field,
idAria,
indices,
+ isDisabled,
placeholder,
}: RiskScoreFieldProps) => {
- const [isRiskScoreMappingChecked, setIsRiskScoreMappingChecked] = useState(false);
- const [initialFieldCheck, setInitialFieldCheck] = useState(true);
-
const fieldTypeFilter = useMemo(() => ['number'], []);
- useEffect(() => {
- if (
- !isRiskScoreMappingChecked &&
- initialFieldCheck &&
- (field.value as AboutStepRiskScore).mapping?.length > 0
- ) {
- setIsRiskScoreMappingChecked(true);
- setInitialFieldCheck(false);
- }
- }, [
- field,
- initialFieldCheck,
- isRiskScoreMappingChecked,
- setIsRiskScoreMappingChecked,
- setInitialFieldCheck,
- ]);
-
const handleFieldChange = useCallback(
([newField]: IFieldType[]): void => {
const values = field.value as AboutStepRiskScore;
field.setValue({
value: values.value,
+ isMappingChecked: values.isMappingChecked,
mapping: [
{
field: newField?.name ?? '',
operator: 'equals',
- value: '',
+ value: undefined,
+ riskScore: undefined,
},
],
});
@@ -99,8 +88,13 @@ export const RiskScoreField = ({
}, [field.value, indices]);
const handleRiskScoreMappingChecked = useCallback(() => {
- setIsRiskScoreMappingChecked(!isRiskScoreMappingChecked);
- }, [isRiskScoreMappingChecked, setIsRiskScoreMappingChecked]);
+ const values = field.value as AboutStepRiskScore;
+ field.setValue({
+ value: values.value,
+ mapping: [...values.mapping],
+ isMappingChecked: !values.isMappingChecked,
+ });
+ }, [field]);
const riskScoreLabel = useMemo(() => {
return (
@@ -117,11 +111,16 @@ export const RiskScoreField = ({
const riskScoreMappingLabel = useMemo(() => {
return (
-
+
@@ -133,7 +132,7 @@ export const RiskScoreField = ({
);
- }, [handleRiskScoreMappingChecked, isRiskScoreMappingChecked]);
+ }, [field.value, handleRiskScoreMappingChecked, isDisabled]);
return (
@@ -153,6 +152,7 @@ export const RiskScoreField = ({
componentProps={{
idAria: 'detectionEngineStepAboutRuleRiskScore',
'data-test-subj': 'detectionEngineStepAboutRuleRiskScore',
+ isDisabled,
euiFieldProps: {
max: 100,
min: 0,
@@ -166,11 +166,11 @@ export const RiskScoreField = ({
- {i18n.RISK_SCORE_MAPPING_DETAILS}
) : (
''
@@ -184,7 +184,7 @@ export const RiskScoreField = ({
>
- {isRiskScoreMappingChecked && (
+ {(field.value as AboutStepRiskScore).isMappingChecked && (
@@ -208,11 +208,11 @@ export const RiskScoreField = ({
fieldTypeFilter={fieldTypeFilter}
isLoading={false}
isClearable={false}
- isDisabled={false}
+ isDisabled={isDisabled}
onChange={handleFieldChange}
data-test-subj={dataTestSubj}
aria-label={idAria}
- fieldInputWidth={230}
+ fieldInputWidth={270}
/>
@@ -226,7 +226,7 @@ export const RiskScoreField = ({
)}
-
+
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
index 579c60579b32e..54d505a4d867f 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx
@@ -14,7 +14,8 @@ import {
EuiIcon,
EuiSpacer,
} from '@elastic/eui';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { noop } from 'lodash/fp';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
@@ -27,10 +28,16 @@ import {
} from '../../../../../../../../src/plugins/data/common/index_patterns';
import { FieldComponent } from '../../../../common/components/autocomplete/field';
import { AutocompleteFieldMatchComponent } from '../../../../common/components/autocomplete/field_value_match';
+import {
+ Severity,
+ SeverityMapping,
+ SeverityMappingItem,
+} from '../../../../../common/detection_engine/schemas/common/schemas';
-const SeverityMappingParentContainer = styled(EuiFlexItem)`
- max-width: 471px;
+const SeverityMappingEuiFormRow = styled(EuiFormRow)`
+ width: 468px;
`;
+
const NestedContent = styled.div`
margin-left: 24px;
`;
@@ -48,6 +55,7 @@ interface SeverityFieldProps {
field: FieldHook;
idAria: string;
indices: IIndexPattern;
+ isDisabled: boolean;
options: SeverityOptionItem[];
}
@@ -56,42 +64,20 @@ export const SeverityField = ({
field,
idAria,
indices,
+ isDisabled,
options,
}: SeverityFieldProps) => {
- const [isSeverityMappingChecked, setIsSeverityMappingChecked] = useState(false);
- const [initialFieldCheck, setInitialFieldCheck] = useState(true);
const fieldValueInputWidth = 160;
- useEffect(() => {
- if (
- !isSeverityMappingChecked &&
- initialFieldCheck &&
- (field.value as AboutStepSeverity).mapping?.length > 0
- ) {
- setIsSeverityMappingChecked(true);
- setInitialFieldCheck(false);
- }
- }, [
- field,
- initialFieldCheck,
- isSeverityMappingChecked,
- setIsSeverityMappingChecked,
- setInitialFieldCheck,
- ]);
-
- const handleFieldChange = useCallback(
- (index: number, severity: string, [newField]: IFieldType[]): void => {
+ const handleFieldValueChange = useCallback(
+ (newMappingItems: SeverityMapping, index: number): void => {
const values = field.value as AboutStepSeverity;
field.setValue({
value: values.value,
+ isMappingChecked: values.isMappingChecked,
mapping: [
...values.mapping.slice(0, index),
- {
- ...values.mapping[index],
- field: newField?.name ?? '',
- operator: 'equals',
- severity,
- },
+ ...newMappingItems,
...values.mapping.slice(index + 1),
],
});
@@ -99,40 +85,59 @@ export const SeverityField = ({
[field]
);
+ const handleFieldChange = useCallback(
+ (index: number, severity: Severity, [newField]: IFieldType[]): void => {
+ const values = field.value as AboutStepSeverity;
+ const newMappingItems: SeverityMapping = [
+ {
+ ...values.mapping[index],
+ field: newField?.name ?? '',
+ value: newField != null ? values.mapping[index].value : '',
+ operator: 'equals',
+ severity,
+ },
+ ];
+ handleFieldValueChange(newMappingItems, index);
+ },
+ [field, handleFieldValueChange]
+ );
+
const handleFieldMatchValueChange = useCallback(
- (index: number, severity: string, newMatchValue: string): void => {
+ (index: number, severity: Severity, newMatchValue: string): void => {
const values = field.value as AboutStepSeverity;
- field.setValue({
- value: values.value,
- mapping: [
- ...values.mapping.slice(0, index),
- {
- ...values.mapping[index],
- value: newMatchValue,
- operator: 'equals',
- severity,
- },
- ...values.mapping.slice(index + 1),
- ],
- });
+ const newMappingItems: SeverityMapping = [
+ {
+ ...values.mapping[index],
+ field: values.mapping[index].field,
+ value:
+ values.mapping[index].field != null && values.mapping[index].field !== ''
+ ? newMatchValue
+ : '',
+ operator: 'equals',
+ severity,
+ },
+ ];
+ handleFieldValueChange(newMappingItems, index);
},
- [field]
+ [field, handleFieldValueChange]
);
- const selectedState = useMemo(() => {
- return (
- (field.value as AboutStepSeverity).mapping?.map((mapping) => {
- const [newSelectedField] = indices.fields.filter(
- ({ name }) => mapping.field != null && mapping.field === name
- );
- return { field: newSelectedField, value: mapping.value };
- }) ?? []
- );
- }, [field.value, indices]);
+ const getIFieldTypeFromFieldName = (
+ fieldName: string | undefined,
+ iIndexPattern: IIndexPattern
+ ): IFieldType | undefined => {
+ const [iFieldType] = iIndexPattern.fields.filter(({ name }) => fieldName === name);
+ return iFieldType;
+ };
- const handleSeverityMappingSelected = useCallback(() => {
- setIsSeverityMappingChecked(!isSeverityMappingChecked);
- }, [isSeverityMappingChecked, setIsSeverityMappingChecked]);
+ const handleSeverityMappingChecked = useCallback(() => {
+ const values = field.value as AboutStepSeverity;
+ field.setValue({
+ value: values.value,
+ mapping: [...values.mapping],
+ isMappingChecked: !values.isMappingChecked,
+ });
+ }, [field]);
const severityLabel = useMemo(() => {
return (
@@ -149,12 +154,17 @@ export const SeverityField = ({
const severityMappingLabel = useMemo(() => {
return (
-
+
{i18n.SEVERITY_MAPPING}
@@ -165,7 +175,7 @@ export const SeverityField = ({
);
- }, [handleSeverityMappingSelected, isSeverityMappingChecked]);
+ }, [field.value, handleSeverityMappingChecked, isDisabled]);
return (
@@ -185,6 +195,7 @@ export const SeverityField = ({
componentProps={{
idAria: 'detectionEngineStepAboutRuleSeverity',
'data-test-subj': 'detectionEngineStepAboutRuleSeverity',
+ isDisabled,
euiFieldProps: {
fullWidth: false,
disabled: false,
@@ -195,12 +206,12 @@ export const SeverityField = ({
-
-
+ {i18n.SEVERITY_MAPPING_DETAILS}
) : (
''
@@ -214,7 +225,7 @@ export const SeverityField = ({
>
- {isSeverityMappingChecked && (
+ {(field.value as AboutStepSeverity).isMappingChecked && (
@@ -231,53 +242,72 @@ export const SeverityField = ({
- {options.map((option, index) => (
-
-
-
-
-
+ {(field.value as AboutStepSeverity).mapping.map(
+ (severityMappingItem: SeverityMappingItem, index) => (
+
+
+
+
+
-
-
-
-
-
-
-
- {option.inputDisplay}
-
-
-
- ))}
+
+
+
+
+
+
+
+ {
+ options.find((o) => o.value === severityMappingItem.severity)
+ ?.inputDisplay
+ }
+
+
+
+ )
+ )}
)}
-
-
+
+
);
};
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/default_value.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/default_value.ts
index f5d61553b595b..b9c3e4f84c18e 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/default_value.ts
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/default_value.ts
@@ -5,6 +5,7 @@
*/
import { AboutStepRule } from '../../../pages/detection_engine/rules/types';
+import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules/helpers';
export const threatDefault = [
{
@@ -21,8 +22,8 @@ export const stepAboutDefaultValue: AboutStepRule = {
isAssociatedToEndpointList: false,
isBuildingBlock: false,
isNew: true,
- severity: { value: 'low', mapping: [] },
- riskScore: { value: 50, mapping: [] },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
+ riskScore: { value: 50, mapping: [], isMappingChecked: false },
references: [''],
falsePositives: [''],
license: '',
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
index a86c1b7ce1bea..cb3fd5e5bec32 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx
@@ -16,6 +16,7 @@ import { stepAboutDefaultValue } from './default_value';
// we don't have the types for waitFor just yet, so using "as waitFor" until when we do
import { wait as waitFor } from '@testing-library/react';
import { AboutStepRule } from '../../../pages/detection_engine/rules/types';
+import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules/helpers';
const theme = () => ({ eui: euiDarkVars, darkMode: true });
@@ -176,8 +177,8 @@ describe('StepAboutRuleComponent', () => {
name: 'Test name text',
note: '',
references: [''],
- riskScore: { value: 50, mapping: [] },
- severity: { value: 'low', mapping: [] },
+ riskScore: { value: 50, mapping: [], isMappingChecked: false },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
tags: [],
threat: [
{
@@ -236,8 +237,8 @@ describe('StepAboutRuleComponent', () => {
name: 'Test name text',
note: '',
references: [''],
- riskScore: { value: 80, mapping: [] },
- severity: { value: 'low', mapping: [] },
+ riskScore: { value: 80, mapping: [], isMappingChecked: false },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
tags: [],
threat: [
{
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
index fbd03850eee75..20470d7bb924f 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx
@@ -117,18 +117,16 @@ export const schema: FormSchema = {
},
],
},
- mapping: {
- type: FIELD_TYPES.TEXT,
- },
+ mapping: {},
+ isMappingChecked: {},
},
riskScore: {
value: {
type: FIELD_TYPES.RANGE,
serializer: (input: string) => Number(input),
},
- mapping: {
- type: FIELD_TYPES.TEXT,
- },
+ mapping: {},
+ isMappingChecked: {},
},
references: {
label: i18n.translate(
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
index 14cf476e66563..7a6b61f0dfd89 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
@@ -9,6 +9,7 @@ import { Rule, RuleError } from '../../../../../containers/detection_engine/rule
import { List } from '../../../../../../../common/detection_engine/schemas/types';
import { AboutStepRule, ActionsStepRule, DefineStepRule, ScheduleStepRule } from '../../types';
import { FieldValueQueryBar } from '../../../../../components/rules/query_bar';
+import { fillEmptySeverityMappings } from '../../helpers';
export const mockQueryBar: FieldValueQueryBar = {
query: {
@@ -175,8 +176,8 @@ export const mockAboutStepRule = (isNew = false): AboutStepRule => ({
license: 'Elastic License',
name: 'Query with rule-id',
description: '24/7',
- severity: { value: 'low', mapping: [] },
- riskScore: { value: 21, mapping: [] },
+ riskScore: { value: 21, mapping: [], isMappingChecked: false },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
references: ['www.test.co'],
falsePositives: ['test'],
tags: ['tag1', 'tag2'],
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
index 705013beb750f..8b03f62fc82bd 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
@@ -189,10 +189,14 @@ export const formatAboutStepData = (
false_positives: falsePositives.filter((item) => !isEmpty(item)),
references: references.filter((item) => !isEmpty(item)),
risk_score: riskScore.value,
- risk_score_mapping: riskScore.mapping,
+ risk_score_mapping: riskScore.isMappingChecked
+ ? riskScore.mapping.filter((m) => m.field != null && m.field !== '')
+ : [],
rule_name_override: ruleNameOverride !== '' ? ruleNameOverride : undefined,
severity: severity.value,
- severity_mapping: severity.mapping,
+ severity_mapping: severity.isMappingChecked
+ ? severity.mapping.filter((m) => m.field != null && m.field !== '' && m.value != null)
+ : [],
threat: threat
.filter((singleThreat) => singleThreat.tactic.name !== 'none')
.map((singleThreat) => ({
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
index b40243efcfb46..10a20807d6f87 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx
@@ -17,6 +17,7 @@ import {
getPrePackagedTimelineStatus,
determineDetailsValue,
userHasNoPermissions,
+ fillEmptySeverityMappings,
} from './helpers';
import { mockRuleWithEverything, mockRule } from './all/__mocks__/mock';
import { esFilters } from '../../../../../../../../src/plugins/data/public';
@@ -97,9 +98,9 @@ describe('rule helpers', () => {
name: 'Query with rule-id',
note: '# this is some markdown documentation',
references: ['www.test.co'],
- riskScore: { value: 21, mapping: [] },
+ riskScore: { value: 21, mapping: [], isMappingChecked: false },
ruleNameOverride: 'message',
- severity: { value: 'low', mapping: [] },
+ severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false },
tags: ['tag1', 'tag2'],
threat: [
{
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
index 1f7b495bad920..1fcfcb4adb5bb 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx
@@ -24,6 +24,8 @@ import {
ScheduleStepRule,
ActionsStepRule,
} from './types';
+import { SeverityMapping } from '../../../../../common/detection_engine/schemas/common/schemas';
+import { severityOptions } from '../../../components/rules/step_about_rule/data';
export interface GetStepsData {
aboutRuleData: AboutStepRule;
@@ -150,18 +152,38 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu
references,
severity: {
value: severity,
- mapping: severityMapping,
+ mapping: fillEmptySeverityMappings(severityMapping),
+ isMappingChecked: severityMapping.length > 0,
},
tags,
riskScore: {
value: riskScore,
mapping: riskScoreMapping,
+ isMappingChecked: riskScoreMapping.length > 0,
},
falsePositives,
threat: threat as IMitreEnterpriseAttack[],
};
};
+const severitySortMapping = {
+ low: 0,
+ medium: 1,
+ high: 2,
+ critical: 3,
+};
+
+export const fillEmptySeverityMappings = (mappings: SeverityMapping): SeverityMapping => {
+ const missingMappings: SeverityMapping = severityOptions.flatMap((so) =>
+ mappings.find((mapping) => mapping.severity === so.value) == null
+ ? [{ field: '', value: '', operator: 'equals', severity: so.value }]
+ : []
+ );
+ return [...mappings, ...missingMappings].sort(
+ (a, b) => severitySortMapping[a.severity] - severitySortMapping[b.severity]
+ );
+};
+
export const determineDetailsValue = (
rule: Rule,
detailsView: boolean
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts
index 23715a88efc7b..e36f08703dae5 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts
@@ -88,11 +88,13 @@ export interface AboutStepRuleDetails {
export interface AboutStepSeverity {
value: string;
mapping: SeverityMapping;
+ isMappingChecked: boolean;
}
export interface AboutStepRiskScore {
value: number;
mapping: RiskScoreMapping;
+ isMappingChecked: boolean;
}
export interface DefineStepRule extends StepRuleData {
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts
index 356cf95fc0d24..888642f77af60 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/mappings/build_risk_score_from_mapping.ts
@@ -10,7 +10,6 @@ import {
RiskScoreMappingOrUndefined,
} from '../../../../../common/detection_engine/schemas/common/schemas';
import { SignalSourceHit } from '../types';
-import { RiskScore as RiskScoreIOTS } from '../../../../../common/detection_engine/schemas/types';
interface BuildRiskScoreFromMappingProps {
doc: SignalSourceHit;
@@ -33,8 +32,12 @@ export const buildRiskScoreFromMapping = ({
const mappedField = riskScoreMapping[0].field;
// TODO: Expand by verifying fieldType from index via doc._index
const mappedValue = get(mappedField, doc._source);
- // TODO: This doesn't seem to validate...identified riskScore > 100 😬
- if (RiskScoreIOTS.is(mappedValue)) {
+ if (
+ typeof mappedValue === 'number' &&
+ Number.isSafeInteger(mappedValue) &&
+ mappedValue >= 0 &&
+ mappedValue <= 100
+ ) {
return { riskScore: mappedValue, riskScoreMeta: { riskScoreOverridden: true } };
}
}