diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 819bf87165e12..687bf91655e2a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -47,6 +47,9 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', risk_score: 50, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts index 767c01f02b187..4adf71d258e0a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/legacy_rules_notification_rule_type.test.ts @@ -76,6 +76,9 @@ const reported = { from: 'now-6m', id: 'rule-id', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], investigation_fields: undefined, language: 'kuery', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts index 452f59df8dcf9..349f54b1e3b3c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/internal_rule_to_api_response.ts @@ -17,6 +17,7 @@ import { } from '../../../normalization/rule_actions'; import { typeSpecificCamelToSnake } from './type_specific_camel_to_snake'; import { commonParamsCamelToSnake } from './common_params_camel_to_snake'; +import { normalizeRuleParams } from './normalize_rule_params'; export const internalRuleToAPIResponse = ( rule: SanitizedRule | ResolvedSanitizedRule @@ -31,6 +32,7 @@ export const internalRuleToAPIResponse = ( const alertActions = rule.actions.map(transformAlertToRuleAction); const throttle = transformFromAlertThrottle(rule); const actions = transformToActionFrequency(alertActions, throttle); + const normalizedRuleParams = normalizeRuleParams(rule.params); return { // saved object properties @@ -49,7 +51,7 @@ export const internalRuleToAPIResponse = ( enabled: rule.enabled, revision: rule.revision, // Security solution shared rule params - ...commonParamsCamelToSnake(rule.params), + ...commonParamsCamelToSnake(normalizedRuleParams), // Type specific security solution rule params ...typeSpecificCamelToSnake(rule.params), // Actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.ts new file mode 100644 index 0000000000000..b8b5db137583b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { normalizeRuleSource } from './normalize_rule_params'; +import type { BaseRuleParams } from '../../../../rule_schema'; + +describe('normalizeRuleSource', () => { + it('should return rule_source of type `internal` when immutable is false and ruleSource is undefined', () => { + const result = normalizeRuleSource({ + immutable: false, + ruleSource: undefined, + }); + expect(result).toEqual({ + type: 'internal', + }); + }); + + it('should return rule_source of type `external` and `isCustomized: false` when immutable is true and ruleSource is undefined', () => { + const result = normalizeRuleSource({ + immutable: true, + ruleSource: undefined, + }); + expect(result).toEqual({ + type: 'external', + isCustomized: false, + }); + }); + + it('should return existing value when ruleSource is present', () => { + const externalRuleSource: BaseRuleParams['ruleSource'] = { + type: 'external', + isCustomized: true, + }; + const externalResult = normalizeRuleSource({ immutable: true, ruleSource: externalRuleSource }); + expect(externalResult).toEqual({ + type: externalRuleSource.type, + isCustomized: externalRuleSource.isCustomized, + }); + + const internalRuleSource: BaseRuleParams['ruleSource'] = { + type: 'internal', + }; + const internalResult = normalizeRuleSource({ + immutable: false, + ruleSource: internalRuleSource, + }); + expect(internalResult).toEqual({ + type: internalRuleSource.type, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.ts new file mode 100644 index 0000000000000..eddd8b0434ba0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/converters/normalize_rule_params.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { BaseRuleParams, RuleSourceCamelCased } from '../../../../rule_schema'; + +interface NormalizeRuleSourceParams { + immutable: BaseRuleParams['immutable']; + ruleSource: BaseRuleParams['ruleSource']; +} + +/* + * Since there's no mechanism to migrate all rules at the same time, + * we cannot guarantee that the ruleSource params is present in all rules. + * This function will normalize the ruleSource param, creating it if does + * not exist in ES, based on the immutable param. + */ +export const normalizeRuleSource = ({ + immutable, + ruleSource, +}: NormalizeRuleSourceParams): RuleSourceCamelCased => { + if (!ruleSource) { + const normalizedRuleSource: RuleSourceCamelCased = immutable + ? { + type: 'external', + isCustomized: false, + } + : { + type: 'internal', + }; + + return normalizedRuleSource; + } + return ruleSource; +}; + +export const normalizeRuleParams = (params: BaseRuleParams) => { + return { + ...params, + // Fields to normalize + ruleSource: normalizeRuleSource({ + immutable: params.immutable, + ruleSource: params.ruleSource, + }), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 0ba0afbce715a..382df4bfa5ffc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -100,6 +100,9 @@ describe('getExportAll', () => { from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', rule_id: 'rule-1', @@ -280,6 +283,9 @@ describe('getExportAll', () => { from: 'now-6m', id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', immutable: false, + rule_source: { + type: 'internal', + }, index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', rule_id: 'rule-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index f11e31691d25b..c9a5a93a4f1c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -8,85 +8,15 @@ import { transformValidateBulkError } from './validate'; import type { BulkError } from '../../routes/utils'; import { getRuleMock } from '../../routes/__mocks__/request_responses'; -import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; -import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../../rule_schema/mocks'; -import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; - -export const ruleOutput = (): RuleResponse => ({ - actions: [], - author: ['Elastic'], - building_block_type: 'default', - created_at: '2019-12-13T16:40:33.400Z', - updated_at: '2019-12-13T16:40:33.400Z', - created_by: 'elastic', - description: 'Detecting root and admin users', - enabled: true, - false_positives: [], - from: 'now-6m', - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - immutable: false, - interval: '5m', - rule_id: 'rule-1', - language: 'kuery', - license: 'Elastic License', - output_index: '.siem-signals', - max_signals: 10000, - risk_score: 50, - risk_score_mapping: [], - name: 'Detect Root/Admin Users', - query: 'user.name: root or user.name: admin', - references: ['http://example.com', 'https://example.com'], - severity: 'high', - severity_mapping: [], - updated_by: 'elastic', - tags: [], - to: 'now', - type: 'query', - throttle: undefined, - threat: getThreatMock(), - version: 1, - revision: 0, - filters: [ - { - query: { - match_phrase: { - 'host.name': 'some-host', - }, - }, - }, - ], - exceptions_list: getListArrayMock(), - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - meta: { - someMeta: 'someField', - }, - note: '# Investigative notes', - timeline_title: 'some-timeline-title', - timeline_id: 'some-timeline-id', - related_integrations: [], - required_fields: [], - response_actions: undefined, - setup: '', - outcome: undefined, - alias_target_id: undefined, - alias_purpose: undefined, - rule_name_override: undefined, - timestamp_override: undefined, - timestamp_override_fallback_disabled: undefined, - namespace: undefined, - data_view_id: undefined, - saved_id: undefined, - alert_suppression: undefined, - investigation_fields: undefined, -}); +import { getOutputRuleAlertForRest } from '../../routes/__mocks__/utils'; describe('validate', () => { describe('transformValidateBulkError', () => { test('it should do a validation correctly of a rule id', () => { const ruleAlert = getRuleMock(getQueryRuleParams()); const validatedOrError = transformValidateBulkError('rule-1', ruleAlert); - expect(validatedOrError).toEqual(ruleOutput()); + expect(validatedOrError).toEqual(getOutputRuleAlertForRest()); }); test('it should do an in-validation correctly of a rule id', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 3a4fa1dadd778..8099d7a00049f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -32,6 +32,9 @@ export const getBaseRuleParams = (): BaseRuleParams => { description: 'Detecting root and admin users', falsePositives: [], immutable: false, + ruleSource: { + type: 'internal', + }, from: 'now-6m', to: 'now', severity: 'high', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index ffb5f6ee45170..4aaa0189eefc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -162,6 +162,9 @@ describe('buildAlert', () => { }, ], immutable: false, + rule_source: { + type: 'internal', + }, type: 'query', language: 'kuery', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], @@ -357,6 +360,9 @@ describe('buildAlert', () => { }, ], immutable: false, + rule_source: { + type: 'internal', + }, type: 'query', language: 'kuery', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],