diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts
index ce73fe1b7c2a5..70e4fb052e172 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts
@@ -13,10 +13,10 @@ import {
ABOUT_SEVERITY,
ABOUT_STEP,
ABOUT_TAGS,
- ABOUT_TIMELINE,
ABOUT_URLS,
DEFINITION_CUSTOM_QUERY,
DEFINITION_INDEX_PATTERNS,
+ DEFINITION_TIMELINE,
DEFINITION_STEP,
RULE_NAME_HEADER,
SCHEDULE_LOOPBACK,
@@ -170,10 +170,6 @@ describe('Signal detection rules', () => {
.eq(ABOUT_RISK)
.invoke('text')
.should('eql', newRule.riskScore);
- cy.get(ABOUT_STEP)
- .eq(ABOUT_TIMELINE)
- .invoke('text')
- .should('eql', 'Default blank timeline');
cy.get(ABOUT_STEP)
.eq(ABOUT_URLS)
.invoke('text')
@@ -202,6 +198,10 @@ describe('Signal detection rules', () => {
.eq(DEFINITION_CUSTOM_QUERY)
.invoke('text')
.should('eql', `${newRule.customQuery} `);
+ cy.get(DEFINITION_STEP)
+ .eq(DEFINITION_TIMELINE)
+ .invoke('text')
+ .should('eql', 'Default blank timeline');
cy.get(SCHEDULE_STEP)
.eq(SCHEDULE_RUNS)
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts
index 6c16735ba5f24..06e535b37708c 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts
@@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export const ABOUT_FALSE_POSITIVES = 4;
+export const ABOUT_FALSE_POSITIVES = 3;
-export const ABOUT_MITRE = 5;
+export const ABOUT_MITRE = 4;
export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]';
@@ -16,14 +16,14 @@ export const ABOUT_SEVERITY = 0;
export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description';
-export const ABOUT_TAGS = 6;
+export const ABOUT_TAGS = 5;
-export const ABOUT_TIMELINE = 2;
-
-export const ABOUT_URLS = 3;
+export const ABOUT_URLS = 2;
export const DEFINITION_CUSTOM_QUERY = 1;
+export const DEFINITION_TIMELINE = 3;
+
export const DEFINITION_INDEX_PATTERNS =
'[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts
index 011a2614c1af9..a6aefefedd5c3 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts
@@ -155,10 +155,6 @@ export const mockAboutStepRule = (isNew = false): AboutStepRule => ({
references: ['www.test.co'],
falsePositives: ['test'],
tags: ['tag1', 'tag2'],
- timeline: {
- id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- title: 'Titled timeline',
- },
threat: [
{
framework: 'mockFramework',
@@ -186,6 +182,10 @@ export const mockDefineStepRule = (isNew = false): DefineStepRule => ({
machineLearningJobId: '',
index: ['filebeat-'],
queryBar: mockQueryBar,
+ timeline: {
+ id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ title: 'Titled timeline',
+ },
});
export const mockScheduleStepRule = (isNew = false, enabled = false): ScheduleStepRule => ({
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap
index 4d416e70a096c..9a534297e5e29 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap
@@ -27,21 +27,6 @@ exports[`description_step StepRuleDescriptionComponent renders correctly against
"description": 21,
"title": "Risk score",
},
- Object {
- "description": "Titled timeline",
- "title": "Timeline template",
- },
- ]
- }
- />
-
-
- ,
"title": "Reference URLs",
},
+ ]
+ }
+ />
+
+
+ {
test('returns expected ListItems array when given valid inputs', () => {
const result: ListItems[] = buildListItems(mockAboutStep, schema, mockFilterManager);
- expect(result.length).toEqual(10);
+ expect(result.length).toEqual(9);
});
});
@@ -431,10 +431,11 @@ describe('description_step', () => {
describe('timeline', () => {
test('returns timeline title if one exists', () => {
+ const mockDefineStep = mockDefineStepRule();
const result: ListItems[] = getDescriptionItem(
'timeline',
'Timeline label',
- mockAboutStep,
+ mockDefineStep,
mockFilterManager
);
@@ -444,7 +445,7 @@ describe('description_step', () => {
test('returns default timeline title if none exists', () => {
const mockStep = {
- ...mockAboutStep,
+ ...mockDefineStepRule(),
timeline: {
id: '12345',
},
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
index 417133f230610..52b0038507b59 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
@@ -5,7 +5,6 @@
*/
import { AboutStepRule } from '../../types';
-import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations';
export const threatDefault = [
{
@@ -24,10 +23,6 @@ export const stepAboutDefaultValue: AboutStepRule = {
references: [''],
falsePositives: [''],
tags: [],
- timeline: {
- id: null,
- title: DEFAULT_TIMELINE_TITLE,
- },
threat: threatDefault,
note: '',
};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
index bfb123f3f3204..58b6ca54f5bbd 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
@@ -37,7 +37,6 @@ import { stepAboutDefaultValue } from './default_value';
import { isUrlInvalid } from './helpers';
import { schema } from './schema';
import * as I18n from './translations';
-import { PickTimeline } from '../pick_timeline';
import { StepContentWrapper } from '../step_content_wrapper';
import { MarkdownEditorForm } from '../../../../../components/markdown_editor/form';
@@ -216,15 +215,6 @@ const StepAboutRuleComponent: FC = ({
buttonContent={AdvancedSettingsAccordionButton}
>
-
{
- if (defaultValues != null) {
- return {
- ...defaultValues,
- isNew: false,
- };
- } else {
- return {
- ...stepDefineDefaultValue,
- index: indicesConfig != null ? indicesConfig : [],
- };
- }
-};
-
const StepDefineRuleComponent: FC = ({
addPadding = false,
defaultValues,
@@ -106,18 +94,16 @@ const StepDefineRuleComponent: FC = ({
}) => {
const mlCapabilities = useContext(MlCapabilitiesContext);
const [openTimelineSearch, setOpenTimelineSearch] = useState(false);
- const [localUseIndicesConfig, setLocalUseIndicesConfig] = useState(false);
+ const [indexModified, setIndexModified] = useState(false);
const [localIsMlRule, setIsMlRule] = useState(false);
const [indicesConfig] = useUiSetting$(DEFAULT_INDEX_KEY);
- const [mylocalIndicesConfig, setMyLocalIndicesConfig] = useState(
- defaultValues != null ? defaultValues.index : indicesConfig ?? []
- );
+ const [myStepData, setMyStepData] = useState({
+ ...stepDefineDefaultValue,
+ index: indicesConfig ?? [],
+ });
const [
{ browserFields, indexPatterns: indexPatternQueryBar, isLoading: indexPatternLoadingQueryBar },
- ] = useFetchIndexPatterns(mylocalIndicesConfig);
- const [myStepData, setMyStepData] = useState(
- getStepDefaultValue(indicesConfig, null)
- );
+ ] = useFetchIndexPatterns(myStepData.index);
const { form } = useForm({
defaultValue: myStepData,
@@ -138,15 +124,13 @@ const StepDefineRuleComponent: FC = ({
}, [form]);
useEffect(() => {
- if (indicesConfig != null && defaultValues != null) {
- const myDefaultValues = getStepDefaultValue(indicesConfig, defaultValues);
- if (!deepEqual(myDefaultValues, myStepData)) {
- setMyStepData(myDefaultValues);
- setLocalUseIndicesConfig(deepEqual(myDefaultValues.index, indicesConfig));
- setFieldValue(form, schema, myDefaultValues);
- }
+ const { isNew, ...values } = myStepData;
+ if (defaultValues != null && !deepEqual(values, defaultValues)) {
+ const newValues = { ...values, ...defaultValues, isNew: false };
+ setMyStepData(newValues);
+ setFieldValue(form, schema, newValues);
}
- }, [defaultValues, indicesConfig]);
+ }, [defaultValues, setMyStepData, setFieldValue]);
useEffect(() => {
if (setForm != null) {
@@ -195,7 +179,7 @@ const StepDefineRuleComponent: FC = ({
path="index"
config={{
...schema.index,
- labelAppend: !localUseIndicesConfig ? (
+ labelAppend: indexModified ? (
{i18n.RESET_DEFAULT_INDEX}
@@ -253,17 +237,22 @@ const StepDefineRuleComponent: FC = ({
/>
>
+
{({ index, ruleType }) => {
if (index != null) {
- if (deepEqual(index, indicesConfig) && !localUseIndicesConfig) {
- setLocalUseIndicesConfig(true);
- }
- if (!deepEqual(index, indicesConfig) && localUseIndicesConfig) {
- setLocalUseIndicesConfig(false);
- }
- if (index != null && !isEmpty(index) && !deepEqual(index, mylocalIndicesConfig)) {
- setMyLocalIndicesConfig(index);
+ if (deepEqual(index, indicesConfig) && indexModified) {
+ setIndexModified(false);
+ } else if (!deepEqual(index, indicesConfig) && !indexModified) {
+ setIndexModified(true);
}
}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
index bcfcd4f4ee09d..271c8fabed3a5 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/schema.tsx
@@ -158,4 +158,19 @@ export const schema: FormSchema = {
},
],
},
+ timeline: {
+ label: i18n.translate(
+ 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateLabel',
+ {
+ defaultMessage: 'Timeline template',
+ }
+ ),
+ helpText: i18n.translate(
+ 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateHelpText',
+ {
+ defaultMessage:
+ 'Select an existing timeline to use as a template when investigating generated signals.',
+ }
+ ),
+ },
};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts
index ea6b02924cb3e..dc0459c54adb0 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts
@@ -19,6 +19,7 @@ import {
formatScheduleStepData,
formatAboutStepData,
formatRule,
+ filterRuleFieldsForType,
} from './helpers';
import {
mockDefineStepRule,
@@ -88,6 +89,8 @@ describe('helpers', () => {
saved_id: 'test123',
index: ['filebeat-'],
type: 'saved_query',
+ timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ timeline_title: 'Titled timeline',
};
expect(result).toEqual(expected);
@@ -109,6 +112,119 @@ describe('helpers', () => {
index: ['filebeat-'],
saved_id: '',
type: 'query',
+ timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ timeline_title: 'Titled timeline',
+ };
+
+ expect(result).toEqual(expected);
+ });
+
+ test('returns formatted object without timeline_id and timeline_title if timeline.id is null', () => {
+ const mockStepData = {
+ ...mockData,
+ };
+ delete mockStepData.timeline.id;
+
+ const result: DefineStepRuleJson = formatDefineStepData(mockStepData);
+
+ const expected = {
+ language: 'kuery',
+ filters: mockQueryBar.filters,
+ query: 'test query',
+ index: ['filebeat-'],
+ saved_id: 'test123',
+ type: 'saved_query',
+ };
+
+ expect(result).toEqual(expected);
+ });
+
+ test('returns formatted object with timeline_id and timeline_title if timeline.id is "', () => {
+ const mockStepData = {
+ ...mockData,
+ timeline: {
+ ...mockData.timeline,
+ id: '',
+ },
+ };
+ const result: DefineStepRuleJson = formatDefineStepData(mockStepData);
+
+ const expected = {
+ language: 'kuery',
+ filters: mockQueryBar.filters,
+ query: 'test query',
+ index: ['filebeat-'],
+ saved_id: 'test123',
+ type: 'saved_query',
+ timeline_id: '',
+ timeline_title: 'Titled timeline',
+ };
+
+ expect(result).toEqual(expected);
+ });
+
+ test('returns formatted object without timeline_id and timeline_title if timeline.title is null', () => {
+ const mockStepData = {
+ ...mockData,
+ timeline: {
+ ...mockData.timeline,
+ id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ },
+ };
+ delete mockStepData.timeline.title;
+ const result: DefineStepRuleJson = formatDefineStepData(mockStepData);
+
+ const expected = {
+ language: 'kuery',
+ filters: mockQueryBar.filters,
+ query: 'test query',
+ index: ['filebeat-'],
+ saved_id: 'test123',
+ type: 'saved_query',
+ };
+
+ expect(result).toEqual(expected);
+ });
+
+ test('returns formatted object with timeline_id and timeline_title if timeline.title is "', () => {
+ const mockStepData = {
+ ...mockData,
+ timeline: {
+ ...mockData.timeline,
+ title: '',
+ },
+ };
+ const result: DefineStepRuleJson = formatDefineStepData(mockStepData);
+
+ const expected = {
+ language: 'kuery',
+ filters: mockQueryBar.filters,
+ query: 'test query',
+ index: ['filebeat-'],
+ saved_id: 'test123',
+ type: 'saved_query',
+ timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ timeline_title: '',
+ };
+
+ expect(result).toEqual(expected);
+ });
+
+ test('returns ML fields if type is machine_learning', () => {
+ const mockStepData: DefineStepRule = {
+ ...mockData,
+ ruleType: 'machine_learning',
+ anomalyThreshold: 44,
+ machineLearningJobId: 'some_jobert_id',
+ };
+ const result: DefineStepRuleJson = formatDefineStepData(mockStepData);
+
+ const expected = {
+ type: 'machine_learning',
+ anomaly_threshold: 44,
+ machine_learning_job_id: 'some_jobert_id',
+ timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ timeline_title: 'Titled timeline',
};
expect(result).toEqual(expected);
@@ -249,8 +365,6 @@ describe('helpers', () => {
],
},
],
- timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- timeline_title: 'Titled timeline',
};
expect(result).toEqual(expected);
@@ -289,8 +403,6 @@ describe('helpers', () => {
],
},
],
- timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- timeline_title: 'Titled timeline',
};
expect(result).toEqual(expected);
@@ -327,160 +439,6 @@ describe('helpers', () => {
],
},
],
- timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- timeline_title: 'Titled timeline',
- };
-
- expect(result).toEqual(expected);
- });
-
- test('returns formatted object without timeline_id and timeline_title if timeline.id is null', () => {
- const mockStepData = {
- ...mockData,
- };
- delete mockStepData.timeline.id;
- const result: AboutStepRuleJson = formatAboutStepData(mockStepData);
- const expected = {
- description: '24/7',
- false_positives: ['test'],
- name: 'Query with rule-id',
- note: '# this is some markdown documentation',
- references: ['www.test.co'],
- risk_score: 21,
- severity: 'low',
- tags: ['tag1', 'tag2'],
- threat: [
- {
- framework: 'MITRE ATT&CK',
- tactic: {
- id: '1234',
- name: 'tactic1',
- reference: 'reference1',
- },
- technique: [
- {
- id: '456',
- name: 'technique1',
- reference: 'technique reference',
- },
- ],
- },
- ],
- };
-
- expect(result).toEqual(expected);
- });
-
- test('returns formatted object with timeline_id and timeline_title if timeline.id is "', () => {
- const mockStepData = {
- ...mockData,
- timeline: {
- ...mockData.timeline,
- id: '',
- },
- };
- const result: AboutStepRuleJson = formatAboutStepData(mockStepData);
- const expected = {
- description: '24/7',
- false_positives: ['test'],
- name: 'Query with rule-id',
- note: '# this is some markdown documentation',
- references: ['www.test.co'],
- risk_score: 21,
- severity: 'low',
- tags: ['tag1', 'tag2'],
- threat: [
- {
- framework: 'MITRE ATT&CK',
- tactic: {
- id: '1234',
- name: 'tactic1',
- reference: 'reference1',
- },
- technique: [
- {
- id: '456',
- name: 'technique1',
- reference: 'technique reference',
- },
- ],
- },
- ],
- timeline_id: '',
- timeline_title: 'Titled timeline',
- };
-
- expect(result).toEqual(expected);
- });
-
- test('returns formatted object without timeline_id and timeline_title if timeline.title is null', () => {
- const mockStepData = {
- ...mockData,
- timeline: {
- ...mockData.timeline,
- id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- },
- };
- delete mockStepData.timeline.title;
- const result: AboutStepRuleJson = formatAboutStepData(mockStepData);
- const expected = {
- description: '24/7',
- false_positives: ['test'],
- name: 'Query with rule-id',
- note: '# this is some markdown documentation',
- references: ['www.test.co'],
- risk_score: 21,
- severity: 'low',
- tags: ['tag1', 'tag2'],
- threat: [
- {
- framework: 'MITRE ATT&CK',
- tactic: {
- id: '1234',
- name: 'tactic1',
- reference: 'reference1',
- },
- technique: [
- {
- id: '456',
- name: 'technique1',
- reference: 'technique reference',
- },
- ],
- },
- ],
- };
-
- expect(result).toEqual(expected);
- });
-
- test('returns formatted object with timeline_id and timeline_title if timeline.title is "', () => {
- const mockStepData = {
- ...mockData,
- timeline: {
- id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- title: '',
- },
- };
- const result: AboutStepRuleJson = formatAboutStepData(mockStepData);
- const expected = {
- description: '24/7',
- false_positives: ['test'],
- name: 'Query with rule-id',
- note: '# this is some markdown documentation',
- references: ['www.test.co'],
- risk_score: 21,
- severity: 'low',
- tags: ['tag1', 'tag2'],
- threat: [
- {
- framework: 'MITRE ATT&CK',
- tactic: { id: '1234', name: 'tactic1', reference: 'reference1' },
- technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }],
- },
- ],
- timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- timeline_title: '',
};
expect(result).toEqual(expected);
@@ -539,8 +497,6 @@ describe('helpers', () => {
technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }],
},
],
- timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- timeline_title: 'Titled timeline',
};
expect(result).toEqual(expected);
@@ -583,4 +539,48 @@ describe('helpers', () => {
expect(result.id).toBeUndefined();
});
});
+
+ describe('filterRuleFieldsForType', () => {
+ let fields: DefineStepRule;
+
+ beforeEach(() => {
+ fields = mockDefineStepRule();
+ });
+
+ it('removes query fields if the type is machine learning', () => {
+ const result = filterRuleFieldsForType(fields, 'machine_learning');
+ expect(result).not.toHaveProperty('index');
+ expect(result).not.toHaveProperty('queryBar');
+ });
+
+ it('leaves ML fields if the type is machine learning', () => {
+ const result = filterRuleFieldsForType(fields, 'machine_learning');
+ expect(result).toHaveProperty('anomalyThreshold');
+ expect(result).toHaveProperty('machineLearningJobId');
+ });
+
+ it('leaves arbitrary fields if the type is machine learning', () => {
+ const result = filterRuleFieldsForType(fields, 'machine_learning');
+ expect(result).toHaveProperty('timeline');
+ expect(result).toHaveProperty('ruleType');
+ });
+
+ it('removes ML fields if the type is not machine learning', () => {
+ const result = filterRuleFieldsForType(fields, 'query');
+ expect(result).not.toHaveProperty('anomalyThreshold');
+ expect(result).not.toHaveProperty('machineLearningJobId');
+ });
+
+ it('leaves query fields if the type is query', () => {
+ const result = filterRuleFieldsForType(fields, 'query');
+ expect(result).toHaveProperty('index');
+ expect(result).toHaveProperty('queryBar');
+ });
+
+ it('leaves arbitrary fields if the type is query', () => {
+ const result = filterRuleFieldsForType(fields, 'query');
+ expect(result).toHaveProperty('timeline');
+ expect(result).toHaveProperty('ruleType');
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
index 1f3379bf681bb..f8900e6a1129e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
@@ -64,27 +64,35 @@ export const filterRuleFieldsForType = (fields: T, type: R
export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => {
const ruleFields = filterRuleFieldsForType(defineStepData, defineStepData.ruleType);
+ const { ruleType, timeline } = ruleFields;
+ const baseFields = {
+ type: ruleType,
+ ...(timeline.id != null &&
+ timeline.title != null && {
+ timeline_id: timeline.id,
+ timeline_title: timeline.title,
+ }),
+ };
- if (isMlFields(ruleFields)) {
- const { anomalyThreshold, machineLearningJobId, isNew, ruleType, ...rest } = ruleFields;
- return {
- ...rest,
- type: ruleType,
- anomaly_threshold: anomalyThreshold,
- machine_learning_job_id: machineLearningJobId,
- };
- } else {
- const { queryBar, isNew, ruleType, ...rest } = ruleFields;
- return {
- ...rest,
- type: ruleType,
- filters: queryBar?.filters,
- language: queryBar?.query?.language,
- query: queryBar?.query?.query as string,
- saved_id: queryBar?.saved_id,
- ...(ruleType === 'query' && queryBar?.saved_id ? { type: 'saved_query' as RuleType } : {}),
- };
- }
+ const typeFields = isMlFields(ruleFields)
+ ? {
+ anomaly_threshold: ruleFields.anomalyThreshold,
+ machine_learning_job_id: ruleFields.machineLearningJobId,
+ }
+ : {
+ index: ruleFields.index,
+ filters: ruleFields.queryBar?.filters,
+ language: ruleFields.queryBar?.query?.language,
+ query: ruleFields.queryBar?.query?.query as string,
+ saved_id: ruleFields.queryBar?.saved_id,
+ ...(ruleType === 'query' &&
+ ruleFields.queryBar?.saved_id && { type: 'saved_query' as RuleType }),
+ };
+
+ return {
+ ...baseFields,
+ ...typeFields,
+ };
};
export const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRuleJson => {
@@ -108,26 +116,11 @@ export const formatScheduleStepData = (scheduleData: ScheduleStepRule): Schedule
};
export const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => {
- const {
- falsePositives,
- references,
- riskScore,
- threat,
- timeline,
- isNew,
- note,
- ...rest
- } = aboutStepData;
+ const { falsePositives, references, riskScore, threat, isNew, note, ...rest } = aboutStepData;
return {
false_positives: falsePositives.filter(item => !isEmpty(item)),
references: references.filter(item => !isEmpty(item)),
risk_score: riskScore,
- ...(timeline.id != null && timeline.title != null
- ? {
- timeline_id: timeline.id,
- timeline_title: timeline.title,
- }
- : {}),
threat: threat
.filter(singleThreat => singleThreat.tactic.name !== 'none')
.map(singleThreat => ({
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
index ee43ae5f1d6e2..3224c605192e6 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx
@@ -65,6 +65,10 @@ describe('rule helpers', () => {
],
saved_id: 'test123',
},
+ timeline: {
+ id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ title: 'Titled timeline',
+ },
};
const aboutRuleStepData = {
description: '24/7',
@@ -93,10 +97,6 @@ describe('rule helpers', () => {
],
},
],
- timeline: {
- id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
- title: 'Titled timeline',
- },
};
const scheduleRuleStepData = { enabled: true, from: '0s', interval: '5m', isNew: false };
const aboutRuleDataDetailsData = {
@@ -112,16 +112,6 @@ describe('rule helpers', () => {
});
describe('getAboutStepsData', () => {
- test('returns timeline id and title of null if they do not exist on rule', () => {
- const mockedRule = mockRuleWithEverything('test-id');
- delete mockedRule.timeline_id;
- delete mockedRule.timeline_title;
- const result: AboutStepRule = getAboutStepsData(mockedRule, false);
-
- expect(result.timeline.id).toBeNull();
- expect(result.timeline.title).toBeNull();
- });
-
test('returns name, description, and note as empty string if detailsView is true', () => {
const result: AboutStepRule = getAboutStepsData(mockRuleWithEverything('test-id'), true);
@@ -195,6 +185,10 @@ describe('rule helpers', () => {
filters: [],
saved_id: "Garrett's IP",
},
+ timeline: {
+ id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ title: 'Untitled timeline',
+ },
};
expect(result).toEqual(expected);
@@ -220,10 +214,24 @@ describe('rule helpers', () => {
filters: [],
saved_id: undefined,
},
+ timeline: {
+ id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
+ title: 'Untitled timeline',
+ },
};
expect(result).toEqual(expected);
});
+
+ test('returns timeline id and title of null if they do not exist on rule', () => {
+ const mockedRule = mockRuleWithEverything('test-id');
+ delete mockedRule.timeline_id;
+ delete mockedRule.timeline_title;
+ const result: DefineStepRule = getDefineStepsData(mockedRule);
+
+ expect(result.timeline.id).toBeNull();
+ expect(result.timeline.title).toBeNull();
+ });
});
describe('getHumanizedDuration', () => {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
index e59ca5e7e14e5..2ace154482a27 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx
@@ -42,20 +42,22 @@ export const getStepsData = ({
return { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData };
};
-export const getDefineStepsData = (rule: Rule): DefineStepRule => {
- return {
- isNew: false,
- ruleType: rule.type,
- anomalyThreshold: rule.anomaly_threshold ?? 50,
- machineLearningJobId: rule.machine_learning_job_id ?? '',
- index: rule.index ?? [],
- queryBar: {
- query: { query: rule.query ?? '', language: rule.language ?? '' },
- filters: (rule.filters ?? []) as Filter[],
- saved_id: rule.saved_id,
- },
- };
-};
+export const getDefineStepsData = (rule: Rule): DefineStepRule => ({
+ isNew: false,
+ ruleType: rule.type,
+ anomalyThreshold: rule.anomaly_threshold ?? 50,
+ machineLearningJobId: rule.machine_learning_job_id ?? '',
+ index: rule.index ?? [],
+ queryBar: {
+ query: { query: rule.query ?? '', language: rule.language ?? '' },
+ filters: (rule.filters ?? []) as Filter[],
+ saved_id: rule.saved_id,
+ },
+ timeline: {
+ id: rule.timeline_id ?? null,
+ title: rule.timeline_title ?? null,
+ },
+});
export const getScheduleStepsData = (rule: Rule): ScheduleStepRule => {
const { enabled, interval, from } = rule;
@@ -94,8 +96,6 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu
risk_score: riskScore,
tags,
threat,
- timeline_id: timelineId,
- timeline_title: timelineTitle,
} = rule;
return {
@@ -109,10 +109,6 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu
riskScore,
falsePositives,
threat: threat as IMitreEnterpriseAttack[],
- timeline: {
- id: timelineId ?? null,
- title: timelineTitle ?? null,
- },
};
};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
index 447b5dc6325ee..d4caa4639f338 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
@@ -57,7 +57,6 @@ export interface AboutStepRule extends StepRuleData {
references: string[];
falsePositives: string[];
tags: string[];
- timeline: FieldValueTimeline;
threat: IMitreEnterpriseAttack[];
note: string;
}
@@ -73,6 +72,7 @@ export interface DefineStepRule extends StepRuleData {
machineLearningJobId: string;
queryBar: FieldValueQueryBar;
ruleType: RuleType;
+ timeline: FieldValueTimeline;
}
export interface ScheduleStepRule extends StepRuleData {
@@ -90,6 +90,8 @@ export interface DefineStepRuleJson {
saved_id?: string;
query?: string;
language?: string;
+ timeline_id?: string;
+ timeline_title?: string;
type: RuleType;
}
@@ -101,8 +103,6 @@ export interface AboutStepRuleJson {
references: string[];
false_positives: string[];
tags: string[];
- timeline_id?: string;
- timeline_title?: string;
threat: IMitreEnterpriseAttack[];
note?: string;
}