From 8dfb6ab986224a85d172bc95bf01ed1782fd3f5b Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 11:45:15 -0600 Subject: [PATCH 01/10] Adds the initial saved object references --- .../signals/saved_object_references/README.md | 136 ++++++++++++++++++ .../extract_exceptions_list.ts | 35 +++++ .../extract_references.ts | 55 +++++++ .../signals/saved_object_references/index.ts | 9 ++ .../inject_exceptions_list.ts | 43 ++++++ .../inject_references.ts | 60 ++++++++ .../utils/constants.ts | 11 ++ .../utils/create_exception_reference.ts | 22 +++ .../utils/get_saved_object_name_pattern.ts | 8 ++ ..._object_name_pattern_for_exception_list.ts | 12 ++ .../utils/get_saved_object_reference.ts | 19 +++ ...ed_object_reference_for_exceptions_list.ts | 16 +++ .../saved_object_references/utils/index.ts | 14 ++ .../utils/log_missing_saved_object_error.ts | 22 +++ ...arning_if_different_references_detected.ts | 26 ++++ .../signals/signal_rule_alert_type.ts | 5 + .../lib/detection_engine/signals/types.ts | 4 +- 17 files changed, 495 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md new file mode 100644 index 0000000000000..7d9120a723568 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -0,0 +1,136 @@ +This is where you add code when you have rules which contain saved object references. Saved object references are for +when you have "joins" in the saved objects between one saved object and another one. This can be a 1 to M (1 to many) +relationship for example where you have a rule which contains the "id" of another saved object. + +Examples are the `exceptionsList` on a rule which contains a saved object reference from the rule to another set of +saved objects of the type `exception-list` + +## Useful queries +How to get all your alerts to see if you have `exceptionsList` on it or not in dev tools: + +```json +GET .kibana/_search +{ + "query": { + "term": { + "type": { + "value": "alert" + } + } + } +} +``` + +## Structure on disk +Run a query in dev tools and you should see this code that adds the following savedObject references +to any newly saved rule: + +```json + { + "_index" : ".kibana-hassanabad19_8.0.0_001", + "_id" : "alert:38482620-ef1b-11eb-ad71-7de7959be71c", + "_score" : 6.2607274, + "_source" : { + "alert" : { + "name" : "kql test rule 1", + "tags" : [ + "__internal_rule_id:4ec223b9-77fa-4895-8539-6b3e586a2858", + "__internal_immutable:false" + ], + "alertTypeId" : "siem.signals", + ... + "exceptionsList" : [ + { + "id" : "endpoint_list", + "list_id" : "endpoint_list", + "namespace_type" : "agnostic", + "type" : "endpoint" + }, + { + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "list_id" : "cd152d0d-3590-4a45-a478-eed04da7936b", + "type" : "detection", + "namespace_type" : "single" + } + ], + ... + "references" : [ + { + "name" : "param:exceptionsList_0", + "id" : "endpoint_list", + "type" : "exception-list" + }, + { + "name" : "param:exceptionsList_1", + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "type" : "exception-list" + } + ], + ... +``` + +The structure is that the alerting framework in conjunction with this code will make an array of saved object references which are going to be: +```json +"references" : [ +{ + "name" : "param:exceptionsList_1", + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "type" : "exception-list" +} +``` + +`name` is the pattern of `param:${name}_${index}`. See the functions and constants in `utils.ts` of: + +* EXCEPTIONS_LIST_NAME +* getSavedObjectNamePattern +* getSavedObjectNamePatternForExceptionsList +* getSavedObjectReference +* getSavedObjectReferenceForExceptionsList + +For how it is constructed and retrieved. If you need to add more types, you should copy and create your own versions or use the generic +utilities/helpers if possible. + +`id` is the saved object id and should always be the same value as the `"exceptionsList" : [ "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c" ...`. +If for some reason the saved object id changes or is different, then on the next save/persist the `exceptionsList.id` will update to that within +its saved object. Note though, that the references id replaces _always_ the `exceptionsList.id` at all times through `inject_references.ts`. If +for some reason the `references` id is deleted, then on the next `inject_references` it will prefer to use the last good known reference and log +a warning. + +Within the rule parameters you can still keep the last known good saved object reference id as above it is shown +```json +"exceptionsList" : [ + { + "id" : "endpoint_list", + "list_id" : "endpoint_list", + "namespace_type" : "agnostic", + "type" : "endpoint" + }, + { + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "list_id" : "cd152d0d-3590-4a45-a478-eed04da7936b", + "type" : "detection", + "namespace_type" : "single" + } +], +``` + +## How to add a new saved object id reference to a rule + +See the files of: +* extract_references.ts +* inject_references.ts + +And their top level comments for how to wire up new instances. It's best to create a new file per saved object reference and push only the needed data +per file. + +Good examples and utilities can be found in the folder of `utils` such as: +* EXCEPTIONS_LIST_NAME +* getSavedObjectNamePattern +* getSavedObjectNamePatternForExceptionsList +* getSavedObjectReference +* getSavedObjectReferenceForExceptionsList + +You can follow those patterns but if it doesn't fit your use case it's fine to just create a new file and wire up your new saved object references + +## End to end tests +At this moment there are none. \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts new file mode 100644 index 0000000000000..f732d36d8dd64 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts @@ -0,0 +1,35 @@ +/* + * 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 { Logger, SavedObjectReference } from 'src/core/server'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { getSavedObjectNamePatternForExceptionsList } from './utils'; + +export const extractExceptionsList = ( + logger: Logger, + exceptionsList: RuleParams['exceptionsList'] +): SavedObjectReference[] => { + if (exceptionsList == null) { + logger.debug( + `Exception list does not exist to extract saved object references for. Returning empty saved object reference` + ); + return []; + } else { + const references = exceptionsList.map((exceptionItem, index) => ({ + name: getSavedObjectNamePatternForExceptionsList(index), + id: exceptionItem.id, + type: EXCEPTION_LIST_NAMESPACE, + })); + logger.debug( + `Found exception list to extract exception list saved object references: ${JSON.stringify( + references + )}` + ); + return references; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts new file mode 100644 index 0000000000000..a0dc421879c4b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.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 { Logger } from 'src/core/server'; +import { RuleParamsAndRefs } from '../../../../../../alerting/server'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { extractExceptionsList } from './extract_exceptions_list'; + +/** + * Extracts references and returns the saved object references. + * + * @param logger Kibana injected logger + * @param params The params of the base rule(s). + * @returns The rule parameters and the saved object references to store. + * + * How to add a new extracted references here: + * --- + * Add a new file for extraction named: extract_.ts, example: extract_foo.ts + * Add a function into that file named: extract, example: extractFoo(logger, params.foo) + * Add a new line below and concat together the new extract with existing ones like so: + * + * const exceptionReferences = extractExceptionsList(logger, params.exceptionsList); + * const fooReferences = extractFoo(logger, params.foo); + * const returnReferences = [...exceptionReferences, ...fooReferences]; + * + * Optionally you can remove any parameters you do not want to store within the Saved Object here: + * const paramsWithoutSavedObjectReferences = { removeParam, ...otherParams }; + * + * If you do remove params, then update the types in: security_solution/server/lib/detection_engine/signals/types.ts + * to use an omit for the functions of "isAlertExecutor" and "SignalRuleAlertTypeDefinition" + */ +export const extractReferences = ( + logger: Logger, + params: RuleParams +): RuleParamsAndRefs => { + logger.debug(['Extracting any saved object references from rule_id: ', params.ruleId].join('')); + + const exceptionReferences = extractExceptionsList(logger, params.exceptionsList); + const returnReferences = [...exceptionReferences]; + + // Modify params if you want to remove any elements separately here. For exceptionLists, we do not remove the id and instead + // keep it to both fail safe guard against manually removed saved object references or if there are migration issues and the saved object + // references are removed. Also keeping it we can detect and log out a warning if the reference between it and the saved_object reference + // array have changed between each other indicating the saved_object array is being mutated outside of this functionality + const paramsWithoutSavedObjectReferences = { ...params }; + + return { + references: returnReferences, + params: paramsWithoutSavedObjectReferences, + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts new file mode 100644 index 0000000000000..b855554545837 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/index.ts @@ -0,0 +1,9 @@ +/* + * 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. + */ + +export * from './inject_references'; +export * from './extract_references'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts new file mode 100644 index 0000000000000..4ec08eb3c292a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts @@ -0,0 +1,43 @@ +/* + * 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 { Logger, SavedObjectReference } from 'src/core/server'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { + createExceptionReference, + getSavedObjectReferenceForExceptionsList, + logMissingSavedObjectError, + logWarningIfDifferentReferencesDetected, +} from './utils'; + +export const injectExceptionsReferences = ( + logger: Logger, + exceptionsList: RuleParams['exceptionsList'], + savedObjectReferences: SavedObjectReference[] +): RuleParams['exceptionsList'] => { + logger.debug( + [ + 'Injecting "exceptionsList" saved object references for the alerting rule parameters, saved object references are:', + JSON.stringify(savedObjectReferences), + 'exceptionsList is:', + JSON.stringify(exceptionsList), + ].join('') + ); + return exceptionsList.map((exceptionItem, index) => { + const savedObjectReference = getSavedObjectReferenceForExceptionsList( + index, + savedObjectReferences + ); + if (savedObjectReference != null) { + logWarningIfDifferentReferencesDetected(logger, savedObjectReference.id, exceptionItem.id); + return createExceptionReference(logger, exceptionItem, savedObjectReference); + } else { + logMissingSavedObjectError(logger, exceptionItem); + return exceptionItem; + } + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts new file mode 100644 index 0000000000000..bd4b6201c84ae --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts @@ -0,0 +1,60 @@ +/* + * 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 { Logger, SavedObjectReference } from 'src/core/server'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { injectExceptionsReferences } from './inject_exceptions_list'; + +/** + * Injects references and returns the saved object references. + * @param logger Kibana injected logger + * @param params The params of the base rule(s). + * @returns The rule parameters with the saved object references. + * + * How to add a new injected references here: + * --- + * Add a new file for injection named: inject_.ts, example: inject_foo.ts + * Add a new function into that file named: inject, example: injectFooReferences(logger, params.foo) + * Add a new line below and spread the new parameter together like so: + * + * const foo = injectFooReferences(logger, params.foo, savedObjectReferences); + * const ruleParamsWithSavedObjectReferences: RuleParams = { + * ...params, + * foo, + * exceptionsList, + * }; + */ +export const injectReferences = ( + logger: Logger, + params: RuleParams, + savedObjectReferences: SavedObjectReference[] +): RuleParams => { + logger.debug( + [ + 'Injecting references into the rule params of: ', + JSON.stringify(params), + ', savedObjectReferences: ', + JSON.stringify(savedObjectReferences), + ].join('') + ); + const exceptionsList = injectExceptionsReferences( + logger, + params.exceptionsList, + savedObjectReferences + ); + const ruleParamsWithSavedObjectReferences: RuleParams = { + ...params, + exceptionsList, + }; + logger.debug( + [ + 'The saved object references injected are: ', + JSON.stringify(ruleParamsWithSavedObjectReferences), + ].join('') + ); + return ruleParamsWithSavedObjectReferences; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts new file mode 100644 index 0000000000000..347b4e960c088 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +/** + * The name of the exceptions list we give it when we save the saved object references. + */ +export const EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME = 'exceptionsList'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts new file mode 100644 index 0000000000000..62770d3323d52 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts @@ -0,0 +1,22 @@ +/* + * 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 { Logger, SavedObjectReference } from 'src/core/server'; +import { RuleParams } from '../../../schemas/rule_schemas'; + +export const createExceptionReference = ( + logger: Logger, + exceptionItem: RuleParams['exceptionsList'][0], + savedObjectReference: SavedObjectReference +): RuleParams['exceptionsList'][0] => { + const reference: RuleParams['exceptionsList'][0] = { + ...exceptionItem, + id: savedObjectReference.id, + }; + logger.debug(`Saved object reference found for exception list:, ${JSON.stringify(reference)}`); + return reference; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts new file mode 100644 index 0000000000000..4dc1318f31a79 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ +export const getSavedObjectNamePattern = (name: string, index: number): string => + `${name}_${index}`; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts new file mode 100644 index 0000000000000..ff3ee3e63e67d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts @@ -0,0 +1,12 @@ +/* + * 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 { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './constants'; +import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; + +export const getSavedObjectNamePatternForExceptionsList = (index: number): string => + getSavedObjectNamePattern(EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts new file mode 100644 index 0000000000000..0a3dcaa636c3e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts @@ -0,0 +1,19 @@ +/* + * 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 { SavedObjectReference } from 'src/core/server'; +import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; + +export const getSavedObjectReference = ( + name: string, + index: number, + savedObjectReferences: SavedObjectReference[] +): SavedObjectReference | undefined => { + return savedObjectReferences.find( + (reference) => reference.name === getSavedObjectNamePattern(name, index) + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts new file mode 100644 index 0000000000000..5f0093b9afea5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts @@ -0,0 +1,16 @@ +/* + * 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 { SavedObjectReference } from 'src/core/server'; +import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './constants'; +import { getSavedObjectReference } from './get_saved_object_reference'; + +export const getSavedObjectReferenceForExceptionsList = ( + index: number, + savedObjectReferences: SavedObjectReference[] +): SavedObjectReference | undefined => + getSavedObjectReference(EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index, savedObjectReferences); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts new file mode 100644 index 0000000000000..d780a8f72943a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ +export * from './constants'; +export * from './create_exception_reference'; +export * from './get_saved_object_name_pattern_for_exception_list'; +export * from './get_saved_object_name_pattern'; +export * from './get_saved_object_reference_for_exceptions_list'; +export * from './get_saved_object_reference'; +export * from './log_missing_saved_object_error'; +export * from './log_warning_if_different_references_detected'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts new file mode 100644 index 0000000000000..d75901dba39cc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts @@ -0,0 +1,22 @@ +/* + * 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 { Logger } from 'src/core/server'; +import { RuleParams } from '../../../schemas/rule_schemas'; + +export const logMissingSavedObjectError = ( + logger: Logger, + exceptionItem: RuleParams['exceptionsList'][0] +): void => { + logger.warn( + [ + 'The saved object references were not found for our exception list when we were expecting to find it. Kibana migrations might not have run correctly or someone might have removed it manually.', + 'Returning the last known good exception list id which might not work.', + JSON.stringify(exceptionItem), + ].join() + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts new file mode 100644 index 0000000000000..9738146ddb256 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts @@ -0,0 +1,26 @@ +/* + * 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 { Logger } from 'src/core/server'; + +export const logWarningIfDifferentReferencesDetected = ( + logger: Logger, + savedObjectReferenceId: string, + savedObjectId: string +): void => { + if (savedObjectReferenceId !== savedObjectId) { + logger.warn( + [ + 'The id of the saved object reference: ', + savedObjectReferenceId, + 'is not the same as the saved object id: ', + savedObjectId, + 'Preferring and using the saved object reference id instead of the saved object id', + ].join('') + ); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index d524757b7c144..fa5386c537eba 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -70,6 +70,7 @@ import { wrapHitsFactory } from './wrap_hits_factory'; import { wrapSequencesFactory } from './wrap_sequences_factory'; import { ConfigType } from '../../../config'; import { ExperimentalFeatures } from '../../../../common/experimental_features'; +import { injectReferences, extractReferences } from './saved_object_references'; export const signalRulesAlertType = ({ logger, @@ -93,6 +94,10 @@ export const signalRulesAlertType = ({ name: 'SIEM signal', actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', + useSavedObjectReferences: { + extractReferences: (params) => extractReferences(logger, params), + injectReferences: (params, references) => injectReferences(logger, params, references), + }, validate: { params: { validate: (object: unknown): RuleParams => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index dbc6848335893..55c7428061abd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -184,7 +184,7 @@ export const isAlertExecutor = ( obj: SignalRuleAlertTypeDefinition ): obj is AlertType< RuleParams, - never, // Only use if defining useSavedObjectReferences hook + RuleParams, AlertTypeState, AlertInstanceState, AlertInstanceContext, @@ -195,7 +195,7 @@ export const isAlertExecutor = ( export type SignalRuleAlertTypeDefinition = AlertType< RuleParams, - never, // Only use if defining useSavedObjectReferences hook + RuleParams, AlertTypeState, AlertInstanceState, AlertInstanceContext, From 57c2263dcc7f83b40f4114874e5b0b0f9723b0f8 Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 11:50:54 -0600 Subject: [PATCH 02/10] Removed debugger statements --- .../extract_exceptions_list.ts | 12 +++--------- .../saved_object_references/extract_references.ts | 2 -- .../inject_exceptions_list.ts | 8 -------- .../saved_object_references/inject_references.ts | 14 -------------- .../utils/create_exception_reference.ts | 1 - .../signals/saved_object_references/utils/index.ts | 1 + 6 files changed, 4 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts index f732d36d8dd64..d50db0840b290 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts @@ -15,21 +15,15 @@ export const extractExceptionsList = ( exceptionsList: RuleParams['exceptionsList'] ): SavedObjectReference[] => { if (exceptionsList == null) { - logger.debug( - `Exception list does not exist to extract saved object references for. Returning empty saved object reference` + logger.warn( + 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty saved object reference' ); return []; } else { - const references = exceptionsList.map((exceptionItem, index) => ({ + return exceptionsList.map((exceptionItem, index) => ({ name: getSavedObjectNamePatternForExceptionsList(index), id: exceptionItem.id, type: EXCEPTION_LIST_NAMESPACE, })); - logger.debug( - `Found exception list to extract exception list saved object references: ${JSON.stringify( - references - )}` - ); - return references; } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts index a0dc421879c4b..8f5d2a595b909 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts @@ -37,8 +37,6 @@ export const extractReferences = ( logger: Logger, params: RuleParams ): RuleParamsAndRefs => { - logger.debug(['Extracting any saved object references from rule_id: ', params.ruleId].join('')); - const exceptionReferences = extractExceptionsList(logger, params.exceptionsList); const returnReferences = [...exceptionReferences]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts index 4ec08eb3c292a..a4e460f18de4f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts @@ -19,14 +19,6 @@ export const injectExceptionsReferences = ( exceptionsList: RuleParams['exceptionsList'], savedObjectReferences: SavedObjectReference[] ): RuleParams['exceptionsList'] => { - logger.debug( - [ - 'Injecting "exceptionsList" saved object references for the alerting rule parameters, saved object references are:', - JSON.stringify(savedObjectReferences), - 'exceptionsList is:', - JSON.stringify(exceptionsList), - ].join('') - ); return exceptionsList.map((exceptionItem, index) => { const savedObjectReference = getSavedObjectReferenceForExceptionsList( index, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts index bd4b6201c84ae..b40145bfc5010 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts @@ -33,14 +33,6 @@ export const injectReferences = ( params: RuleParams, savedObjectReferences: SavedObjectReference[] ): RuleParams => { - logger.debug( - [ - 'Injecting references into the rule params of: ', - JSON.stringify(params), - ', savedObjectReferences: ', - JSON.stringify(savedObjectReferences), - ].join('') - ); const exceptionsList = injectExceptionsReferences( logger, params.exceptionsList, @@ -50,11 +42,5 @@ export const injectReferences = ( ...params, exceptionsList, }; - logger.debug( - [ - 'The saved object references injected are: ', - JSON.stringify(ruleParamsWithSavedObjectReferences), - ].join('') - ); return ruleParamsWithSavedObjectReferences; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts index 62770d3323d52..55c6534c7c491 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts @@ -17,6 +17,5 @@ export const createExceptionReference = ( ...exceptionItem, id: savedObjectReference.id, }; - logger.debug(`Saved object reference found for exception list:, ${JSON.stringify(reference)}`); return reference; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts index d780a8f72943a..4299e2083931f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + export * from './constants'; export * from './create_exception_reference'; export * from './get_saved_object_name_pattern_for_exception_list'; From 1473bc0be97956d48efa28bead2f98ec8386910b Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 11:57:39 -0600 Subject: [PATCH 03/10] Fixed markdown comments --- .../signals/saved_object_references/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md index 7d9120a723568..a8cf9be74b93d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -25,7 +25,7 @@ GET .kibana/_search Run a query in dev tools and you should see this code that adds the following savedObject references to any newly saved rule: -```json +```json5 { "_index" : ".kibana-hassanabad19_8.0.0_001", "_id" : "alert:38482620-ef1b-11eb-ad71-7de7959be71c", @@ -38,7 +38,7 @@ to any newly saved rule: "__internal_immutable:false" ], "alertTypeId" : "siem.signals", - ... + "other data... other data": "other data...other data", "exceptionsList" : [ { "id" : "endpoint_list", @@ -53,7 +53,7 @@ to any newly saved rule: "namespace_type" : "single" } ], - ... + "other data... other data": "other data...other data", "references" : [ { "name" : "param:exceptionsList_0", @@ -66,7 +66,7 @@ to any newly saved rule: "type" : "exception-list" } ], - ... + "other data... other data": "other data...other data", ``` The structure is that the alerting framework in conjunction with this code will make an array of saved object references which are going to be: From 1e30db1cfcb15c88b18bbdd55e7906da434b5ffd Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 11:58:19 -0600 Subject: [PATCH 04/10] Removed accidental 5 --- .../detection_engine/signals/saved_object_references/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md index a8cf9be74b93d..ba87893faaa25 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -25,7 +25,7 @@ GET .kibana/_search Run a query in dev tools and you should see this code that adds the following savedObject references to any newly saved rule: -```json5 +```json { "_index" : ".kibana-hassanabad19_8.0.0_001", "_id" : "alert:38482620-ef1b-11eb-ad71-7de7959be71c", From 8f5bf2344afca9195e0d27f99138f593f82957a0 Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 12:00:27 -0600 Subject: [PATCH 05/10] Close brackets of JSON --- .../signals/saved_object_references/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md index ba87893faaa25..db28c67af86d3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -66,7 +66,10 @@ to any newly saved rule: "type" : "exception-list" } ], - "other data... other data": "other data...other data", + "other data... other data": "other data...other data" + } + } + } ``` The structure is that the alerting framework in conjunction with this code will make an array of saved object references which are going to be: From e991975507bbda8530c5b52d882295b1f6e8279a Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 12:01:52 -0600 Subject: [PATCH 06/10] Updated README to be better JSON formatted --- .../signals/saved_object_references/README.md | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md index db28c67af86d3..059005707625f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/README.md @@ -74,11 +74,14 @@ to any newly saved rule: The structure is that the alerting framework in conjunction with this code will make an array of saved object references which are going to be: ```json -"references" : [ { - "name" : "param:exceptionsList_1", - "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", - "type" : "exception-list" + "references" : [ + { + "name" : "param:exceptionsList_1", + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "type" : "exception-list" + } + ] } ``` @@ -101,20 +104,22 @@ a warning. Within the rule parameters you can still keep the last known good saved object reference id as above it is shown ```json -"exceptionsList" : [ - { - "id" : "endpoint_list", - "list_id" : "endpoint_list", - "namespace_type" : "agnostic", - "type" : "endpoint" - }, - { - "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", - "list_id" : "cd152d0d-3590-4a45-a478-eed04da7936b", - "type" : "detection", - "namespace_type" : "single" - } -], +{ + "exceptionsList" : [ + { + "id" : "endpoint_list", + "list_id" : "endpoint_list", + "namespace_type" : "agnostic", + "type" : "endpoint" + }, + { + "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + "list_id" : "cd152d0d-3590-4a45-a478-eed04da7936b", + "type" : "detection", + "namespace_type" : "single" + } + ], +} ``` ## How to add a new saved object id reference to a rule From 3ff1c1d80e713691cedbb51e34728f4b7a4dcd23 Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 12:08:04 -0600 Subject: [PATCH 07/10] Updated comments --- .../server/lib/detection_engine/signals/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 55c7428061abd..79b0430a21453 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -184,7 +184,7 @@ export const isAlertExecutor = ( obj: SignalRuleAlertTypeDefinition ): obj is AlertType< RuleParams, - RuleParams, + RuleParams, // This type is used for useSavedObjectReferences, use an Omit here if you want to remove any values. AlertTypeState, AlertInstanceState, AlertInstanceContext, @@ -195,7 +195,7 @@ export const isAlertExecutor = ( export type SignalRuleAlertTypeDefinition = AlertType< RuleParams, - RuleParams, + RuleParams, // This type is used for useSavedObjectReferences, use an Omit here if you want to remove any values. AlertTypeState, AlertInstanceState, AlertInstanceContext, From dd1ff5008f5e87f34dbbf038edc76e367ee44139 Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 13:54:05 -0600 Subject: [PATCH 08/10] Added comments to functions --- .../extract_exceptions_list.ts | 20 +++++++-- .../extract_references.ts | 24 +++++----- .../inject_exceptions_list.ts | 44 ++++++++++++++----- .../inject_references.ts | 29 ++++++------ .../utils/constants.ts | 12 ++++- .../utils/create_exception_reference.ts | 21 --------- .../utils/get_saved_object_name_pattern.ts | 16 ++++++- ..._object_name_pattern_for_exception_list.ts | 7 ++- .../utils/get_saved_object_reference.ts | 25 ++++++++--- ...ed_object_reference_for_exceptions_list.ts | 25 ++++++++--- .../saved_object_references/utils/index.ts | 1 - .../utils/log_missing_saved_object_error.ts | 16 +++++-- ...arning_if_different_references_detected.ts | 20 ++++++--- .../signals/signal_rule_alert_type.ts | 5 ++- 14 files changed, 178 insertions(+), 87 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts index d50db0840b290..42b0bf5adf701 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts @@ -10,10 +10,22 @@ import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; import { RuleParams } from '../../schemas/rule_schemas'; import { getSavedObjectNamePatternForExceptionsList } from './utils'; -export const extractExceptionsList = ( - logger: Logger, - exceptionsList: RuleParams['exceptionsList'] -): SavedObjectReference[] => { +/** + * This extracts the "exceptionsList" "id" and returns it as a saved object reference. + * NOTE: Due to migrations sometimes not running in the last few releases, I do an additional check for if "exceptionsList" exists or not. Once + * those bugs are fixed, we can remove the "if (exceptionsList == null) {" check, but for the time being it is there to keep things running even + * if exceptionsList has not been migrated. + * @param logger The kibana injected logger + * @param exceptionsList The exceptions list to get the id from and return it as a saved object reference. + * @returns The saved object references from the exceptions list + */ +export const extractExceptionsList = ({ + logger, + exceptionsList, +}: { + logger: Logger; + exceptionsList: RuleParams['exceptionsList']; +}): SavedObjectReference[] => { if (exceptionsList == null) { logger.warn( 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty saved object reference' diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts index 8f5d2a595b909..92e689e225764 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.ts @@ -12,11 +12,6 @@ import { extractExceptionsList } from './extract_exceptions_list'; /** * Extracts references and returns the saved object references. - * - * @param logger Kibana injected logger - * @param params The params of the base rule(s). - * @returns The rule parameters and the saved object references to store. - * * How to add a new extracted references here: * --- * Add a new file for extraction named: extract_.ts, example: extract_foo.ts @@ -32,12 +27,21 @@ import { extractExceptionsList } from './extract_exceptions_list'; * * If you do remove params, then update the types in: security_solution/server/lib/detection_engine/signals/types.ts * to use an omit for the functions of "isAlertExecutor" and "SignalRuleAlertTypeDefinition" + * @param logger Kibana injected logger + * @param params The params of the base rule(s). + * @returns The rule parameters and the saved object references to store. */ -export const extractReferences = ( - logger: Logger, - params: RuleParams -): RuleParamsAndRefs => { - const exceptionReferences = extractExceptionsList(logger, params.exceptionsList); +export const extractReferences = ({ + logger, + params, +}: { + logger: Logger; + params: RuleParams; +}): RuleParamsAndRefs => { + const exceptionReferences = extractExceptionsList({ + logger, + exceptionsList: params.exceptionsList, + }); const returnReferences = [...exceptionReferences]; // Modify params if you want to remove any elements separately here. For exceptionLists, we do not remove the id and instead diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts index a4e460f18de4f..3e01d0918a9f4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts @@ -8,27 +8,47 @@ import { Logger, SavedObjectReference } from 'src/core/server'; import { RuleParams } from '../../schemas/rule_schemas'; import { - createExceptionReference, getSavedObjectReferenceForExceptionsList, logMissingSavedObjectError, logWarningIfDifferentReferencesDetected, } from './utils'; -export const injectExceptionsReferences = ( - logger: Logger, - exceptionsList: RuleParams['exceptionsList'], - savedObjectReferences: SavedObjectReference[] -): RuleParams['exceptionsList'] => { +/** + * This injects any "exceptionsList" "id"'s from saved object reference and returns the "exceptionsList" using the saved object reference. If for + * some reason it is missing on saved object reference, we log an error about it and then take the last known good value from the "exceptionsList" + * + * @param logger The kibana injected logger + * @param exceptionsList The exceptions list to merge the saved object reference from. + * @param savedObjectReferences The saved object references which should contain an "exceptionsList" + * @returns The exceptionsList with the saved object reference replacing any value in the saved object's id. + */ +export const injectExceptionsReferences = ({ + logger, + exceptionsList, + savedObjectReferences, +}: { + logger: Logger; + exceptionsList: RuleParams['exceptionsList']; + savedObjectReferences: SavedObjectReference[]; +}): RuleParams['exceptionsList'] => { return exceptionsList.map((exceptionItem, index) => { - const savedObjectReference = getSavedObjectReferenceForExceptionsList( + const savedObjectReference = getSavedObjectReferenceForExceptionsList({ index, - savedObjectReferences - ); + savedObjectReferences, + }); if (savedObjectReference != null) { - logWarningIfDifferentReferencesDetected(logger, savedObjectReference.id, exceptionItem.id); - return createExceptionReference(logger, exceptionItem, savedObjectReference); + logWarningIfDifferentReferencesDetected({ + logger, + savedObjectReferenceId: savedObjectReference.id, + savedObjectId: exceptionItem.id, + }); + const reference: RuleParams['exceptionsList'][0] = { + ...exceptionItem, + id: savedObjectReference.id, + }; + return reference; } else { - logMissingSavedObjectError(logger, exceptionItem); + logMissingSavedObjectError({ logger, exceptionItem }); return exceptionItem; } }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts index b40145bfc5010..f05bcfe569be7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts @@ -11,10 +11,6 @@ import { injectExceptionsReferences } from './inject_exceptions_list'; /** * Injects references and returns the saved object references. - * @param logger Kibana injected logger - * @param params The params of the base rule(s). - * @returns The rule parameters with the saved object references. - * * How to add a new injected references here: * --- * Add a new file for injection named: inject_.ts, example: inject_foo.ts @@ -27,17 +23,24 @@ import { injectExceptionsReferences } from './inject_exceptions_list'; * foo, * exceptionsList, * }; + * @param logger Kibana injected logger + * @param params The params of the base rule(s). + * @returns The rule parameters with the saved object references. */ -export const injectReferences = ( - logger: Logger, - params: RuleParams, - savedObjectReferences: SavedObjectReference[] -): RuleParams => { - const exceptionsList = injectExceptionsReferences( +export const injectReferences = ({ + logger, + params, + savedObjectReferences, +}: { + logger: Logger; + params: RuleParams; + savedObjectReferences: SavedObjectReference[]; +}): RuleParams => { + const exceptionsList = injectExceptionsReferences({ logger, - params.exceptionsList, - savedObjectReferences - ); + exceptionsList: params.exceptionsList, + savedObjectReferences, + }); const ruleParamsWithSavedObjectReferences: RuleParams = { ...params, exceptionsList, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts index 347b4e960c088..1423dff3b92f1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/constants.ts @@ -6,6 +6,16 @@ */ /** - * The name of the exceptions list we give it when we save the saved object references. + * The name of the exceptions list we give it when we save the saved object references. This name will + * end up in the saved object as in this example: + * { + * "references" : [ + * { + * "name" : "param:exceptionsList_1", + * "id" : "50e3bd70-ef1b-11eb-ad71-7de7959be71c", + * "type" : "exception-list" + * } + * ] + * } */ export const EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME = 'exceptionsList'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts deleted file mode 100644 index 55c6534c7c491..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/create_exception_reference.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { Logger, SavedObjectReference } from 'src/core/server'; -import { RuleParams } from '../../../schemas/rule_schemas'; - -export const createExceptionReference = ( - logger: Logger, - exceptionItem: RuleParams['exceptionsList'][0], - savedObjectReference: SavedObjectReference -): RuleParams['exceptionsList'][0] => { - const reference: RuleParams['exceptionsList'][0] = { - ...exceptionItem, - id: savedObjectReference.id, - }; - return reference; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts index 4dc1318f31a79..7c2bd89d9025e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts @@ -4,5 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export const getSavedObjectNamePattern = (name: string, index: number): string => - `${name}_${index}`; + +/** + * Given a name and index this will return the pattern of "${name_${index}" + * @param name The name to suffix the string + * @param index The index to suffix the string + * @returns The pattern "${name_${index}" + */ +export const getSavedObjectNamePattern = ({ + name, + index, +}: { + name: string; + index: number; +}): string => `${name}_${index}`; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts index ff3ee3e63e67d..01f1df0c69aa6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts @@ -8,5 +8,10 @@ import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './constants'; import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; +/** + * Given an index this will return the pattern of "exceptionsList_${index}" + * @param index The index to suffix the string + * @returns The pattern of "exceptionsList_${index}" + */ export const getSavedObjectNamePatternForExceptionsList = (index: number): string => - getSavedObjectNamePattern(EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index); + getSavedObjectNamePattern({ name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts index 0a3dcaa636c3e..2d50125c2d95c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts @@ -8,12 +8,25 @@ import { SavedObjectReference } from 'src/core/server'; import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; -export const getSavedObjectReference = ( - name: string, - index: number, - savedObjectReferences: SavedObjectReference[] -): SavedObjectReference | undefined => { +/** + * Given a saved object name, and an index, this will return the specific named saved object reference + * even if it is mixed in with other reference objects. This is needed since a references array can contain multiple + * types of saved objects in a single array, we have to use the name to get the value. + * @param name The name of the saved object reference we are getting from the array + * @param index The index position to get for the exceptions list. + * @param savedObjectReferences The saved object references which can contain "exceptionsList" mixed with other saved object types + * @returns The saved object reference if found, otherwise undefined + */ +export const getSavedObjectReference = ({ + name, + index, + savedObjectReferences, +}: { + name: string; + index: number; + savedObjectReferences: SavedObjectReference[]; +}): SavedObjectReference | undefined => { return savedObjectReferences.find( - (reference) => reference.name === getSavedObjectNamePattern(name, index) + (reference) => reference.name === getSavedObjectNamePattern({ name, index }) ); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts index 5f0093b9afea5..60e8132d474dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts @@ -9,8 +9,23 @@ import { SavedObjectReference } from 'src/core/server'; import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './constants'; import { getSavedObjectReference } from './get_saved_object_reference'; -export const getSavedObjectReferenceForExceptionsList = ( - index: number, - savedObjectReferences: SavedObjectReference[] -): SavedObjectReference | undefined => - getSavedObjectReference(EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index, savedObjectReferences); +/** + * Given an index and a saved object reference, this will return the specific "exceptionsList" saved object reference + * even if it is mixed in with other reference objects. This is needed since a references array can contain multiple + * types of saved objects in a single array, we have to use the "exceptionsList" name to get the value. + * @param index The index position to get for the exceptions list. + * @param savedObjectReferences The saved object references which can contain "exceptionsList" mixed with other saved object types + * @returns The saved object reference if found, otherwise undefined + */ +export const getSavedObjectReferenceForExceptionsList = ({ + index, + savedObjectReferences, +}: { + index: number; + savedObjectReferences: SavedObjectReference[]; +}): SavedObjectReference | undefined => + getSavedObjectReference({ + name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, + index, + savedObjectReferences, + }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts index 4299e2083931f..ca88dae364a4b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/index.ts @@ -6,7 +6,6 @@ */ export * from './constants'; -export * from './create_exception_reference'; export * from './get_saved_object_name_pattern_for_exception_list'; export * from './get_saved_object_name_pattern'; export * from './get_saved_object_reference_for_exceptions_list'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts index d75901dba39cc..9f0f33adaa007 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts @@ -8,10 +8,18 @@ import { Logger } from 'src/core/server'; import { RuleParams } from '../../../schemas/rule_schemas'; -export const logMissingSavedObjectError = ( - logger: Logger, - exceptionItem: RuleParams['exceptionsList'][0] -): void => { +/** + * This will log a warning that we are missing an object reference. + * @param logger The kibana injected logger + * @param exceptionItem The exception item to log the warning out as + */ +export const logMissingSavedObjectError = ({ + logger, + exceptionItem, +}: { + logger: Logger; + exceptionItem: RuleParams['exceptionsList'][0]; +}): void => { logger.warn( [ 'The saved object references were not found for our exception list when we were expecting to find it. Kibana migrations might not have run correctly or someone might have removed it manually.', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts index 9738146ddb256..060304121d641 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts @@ -7,11 +7,21 @@ import { Logger } from 'src/core/server'; -export const logWarningIfDifferentReferencesDetected = ( - logger: Logger, - savedObjectReferenceId: string, - savedObjectId: string -): void => { +/** + * This will log a warning that the saved object reference id and the saved object id are not the same if that is true. + * @param logger The kibana injected logger + * @param savedObjectReferenceId The saved object reference id from "references: [{ id: ...}]" + * @param savedObjectId The saved object id from a structure such as exceptions { exceptionsList: { "id": "..." } } + */ +export const logWarningIfDifferentReferencesDetected = ({ + logger, + savedObjectReferenceId, + savedObjectId, +}: { + logger: Logger; + savedObjectReferenceId: string; + savedObjectId: string; +}): void => { if (savedObjectReferenceId !== savedObjectId) { logger.warn( [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index fa5386c537eba..2da5bae755ecc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -95,8 +95,9 @@ export const signalRulesAlertType = ({ actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', useSavedObjectReferences: { - extractReferences: (params) => extractReferences(logger, params), - injectReferences: (params, references) => injectReferences(logger, params, references), + extractReferences: (params) => extractReferences({ logger, params }), + injectReferences: (params, savedObjectReferences) => + injectReferences({ logger, params, savedObjectReferences }), }, validate: { params: { From 8f7b317a006e7b0b469f7374b35fb57ddc31200c Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Wed, 28 Jul 2021 16:55:02 -0600 Subject: [PATCH 09/10] Unit tests added --- .../extract_exceptions_list.test.ts | 74 ++++++++++ .../extract_exceptions_list.ts | 4 +- .../extract_references.test.ts | 82 +++++++++++ .../inject_exceptions_list.test.ts | 139 ++++++++++++++++++ .../inject_exceptions_list.ts | 7 + .../inject_references.test.ts | 102 +++++++++++++ .../get_saved_object_name_pattern.test.ts | 30 ++++ .../utils/get_saved_object_name_pattern.ts | 8 +- ...ct_name_pattern_for_exception_list.test.ts | 37 +++++ ..._object_name_pattern_for_exception_list.ts | 10 +- .../utils/get_saved_object_reference.test.ts | 133 +++++++++++++++++ .../utils/get_saved_object_reference.ts | 24 ++- ...ject_reference_for_exceptions_list.test.ts | 130 ++++++++++++++++ ...ed_object_reference_for_exceptions_list.ts | 23 ++- .../log_missing_saved_object_error.test.ts | 33 +++++ .../utils/log_missing_saved_object_error.ts | 9 +- ...g_if_different_references_detected.test.ts | 38 +++++ ...arning_if_different_references_detected.ts | 8 +- 18 files changed, 867 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts new file mode 100644 index 0000000000000..56a1e875ac5e7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.test.ts @@ -0,0 +1,74 @@ +/* + * 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 { extractExceptionsList } from './extract_exceptions_list'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; +import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './utils'; + +describe('extract_exceptions_list', () => { + type FuncReturn = ReturnType; + let logger = loggingSystemMock.create().get('security_solution'); + const mockExceptionsList = (): RuleParams['exceptionsList'] => [ + { + id: '123', + list_id: '456', + type: 'detection', + namespace_type: 'agnostic', + }, + ]; + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('it returns an empty array given an empty array for exceptionsList', () => { + expect(extractExceptionsList({ logger, exceptionsList: [] })).toEqual([]); + }); + + test('logs expect error message if the exceptionsList is undefined', () => { + extractExceptionsList({ + logger, + exceptionsList: (undefined as unknown) as RuleParams['exceptionsList'], + }); + expect(logger.error).toBeCalledWith( + 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty saved object reference' + ); + }); + + test('It returns exception list transformed into a saved object references', () => { + expect( + extractExceptionsList({ logger, exceptionsList: mockExceptionsList() }) + ).toEqual([ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]); + }); + + test('It returns two exception lists transformed into a saved object references', () => { + const twoInputs: RuleParams['exceptionsList'] = [ + mockExceptionsList()[0], + { ...mockExceptionsList()[0], id: '976' }, + ]; + expect(extractExceptionsList({ logger, exceptionsList: twoInputs })).toEqual([ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + { + id: '976', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_1`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts index 42b0bf5adf701..9b7f8bbcefee1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_exceptions_list.ts @@ -12,7 +12,7 @@ import { getSavedObjectNamePatternForExceptionsList } from './utils'; /** * This extracts the "exceptionsList" "id" and returns it as a saved object reference. - * NOTE: Due to migrations sometimes not running in the last few releases, I do an additional check for if "exceptionsList" exists or not. Once + * NOTE: Due to rolling upgrades with migrations and a few bugs with migrations, I do an additional check for if "exceptionsList" exists or not. Once * those bugs are fixed, we can remove the "if (exceptionsList == null) {" check, but for the time being it is there to keep things running even * if exceptionsList has not been migrated. * @param logger The kibana injected logger @@ -27,7 +27,7 @@ export const extractExceptionsList = ({ exceptionsList: RuleParams['exceptionsList']; }): SavedObjectReference[] => { if (exceptionsList == null) { - logger.warn( + logger.error( 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty saved object reference' ); return []; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts new file mode 100644 index 0000000000000..31288559e9437 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/extract_references.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; +import { extractReferences } from './extract_references'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; +import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './utils'; + +describe('extract_references', () => { + type FuncReturn = ReturnType; + let logger = loggingSystemMock.create().get('security_solution'); + const mockExceptionsList = (): RuleParams['exceptionsList'] => [ + { + id: '123', + list_id: '456', + type: 'detection', + namespace_type: 'agnostic', + }, + ]; + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('It returns params untouched and the references extracted as exception list saved object references', () => { + const params: Partial = { + note: 'some note', + exceptionsList: mockExceptionsList(), + }; + expect( + extractReferences({ + logger, + params: params as RuleParams, + }) + ).toEqual({ + params: params as RuleParams, + references: [ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ], + }); + }); + + test('It returns params untouched and the references an empty array if the exceptionsList is an empty array', () => { + const params: Partial = { + note: 'some note', + exceptionsList: [], + }; + expect( + extractReferences({ + logger, + params: params as RuleParams, + }) + ).toEqual({ + params: params as RuleParams, + references: [], + }); + }); + + test('It returns params untouched and the references an empty array if the exceptionsList is missing for any reason', () => { + const params: Partial = { + note: 'some note', + }; + expect( + extractReferences({ + logger, + params: params as RuleParams, + }) + ).toEqual({ + params: params as RuleParams, + references: [], + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts new file mode 100644 index 0000000000000..fc35088da66fc --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.test.ts @@ -0,0 +1,139 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; +import { SavedObjectReference } from 'src/core/server'; +import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './utils'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; +import { injectExceptionsReferences } from './inject_exceptions_list'; +import { RuleParams } from '../../schemas/rule_schemas'; + +describe('inject_exceptions_list', () => { + type FuncReturn = ReturnType; + let logger = loggingSystemMock.create().get('security_solution'); + const mockExceptionsList = (): RuleParams['exceptionsList'] => [ + { + id: '123', + list_id: '456', + type: 'detection', + namespace_type: 'agnostic', + }, + ]; + const mockSavedObjectReferences = (): SavedObjectReference[] => [ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]; + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('returns empty array given an empty array for both "exceptionsList" and "savedObjectReferences"', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: [], + savedObjectReferences: [], + }) + ).toEqual([]); + }); + + test('logs expect error message if the exceptionsList is undefined', () => { + injectExceptionsReferences({ + logger, + exceptionsList: (undefined as unknown) as RuleParams['exceptionsList'], + savedObjectReferences: mockSavedObjectReferences(), + }); + expect(logger.error).toBeCalledWith( + 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty exception list' + ); + }); + + test('returns empty array given an empty array for "exceptionsList"', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: [], + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual([]); + }); + + test('returns exceptions list array given an empty array for "savedObjectReferences"', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: [], + }) + ).toEqual(mockExceptionsList()); + }); + + test('returns parameters from the saved object if found', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(mockExceptionsList()); + }); + + test('does not log an error if it returns parameters from the saved object when found', () => { + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: mockSavedObjectReferences(), + }); + expect(logger.error).not.toHaveBeenCalled(); + }); + + test('returns parameters from the saved object if found with a different saved object reference id', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], id: '456' }], + }) + ).toEqual([{ ...mockExceptionsList()[0], id: '456' }]); + }); + + test('logs an error if found with a different saved object reference id', () => { + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], id: '456' }], + }); + expect(logger.error).toBeCalledWith( + 'The id of the "saved object reference id": 456 is not the same as the "saved object id": 123. Preferring and using the "saved object reference id" instead of the "saved object id"' + ); + }); + + test('returns exceptionItem if the saved object reference cannot match as a fall back', () => { + expect( + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], name: 'other-name_0' }], + }) + ).toEqual(mockExceptionsList()); + }); + + test('logs an error if the saved object type could not be found', () => { + injectExceptionsReferences({ + logger, + exceptionsList: mockExceptionsList(), + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], name: 'other-name_0' }], + }); + expect(logger.error).toBeCalledWith( + 'The saved object references were not found for our exception list when we were expecting to find it. Kibana migrations might not have run correctly or someone might have removed the saved object references manually. Returning the last known good exception list id which might not work. exceptionItem with its id being returned is: {"id":"123","list_id":"456","type":"detection","namespace_type":"agnostic"}' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts index 3e01d0918a9f4..2e6559fbf18cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_exceptions_list.ts @@ -31,8 +31,15 @@ export const injectExceptionsReferences = ({ exceptionsList: RuleParams['exceptionsList']; savedObjectReferences: SavedObjectReference[]; }): RuleParams['exceptionsList'] => { + if (exceptionsList == null) { + logger.error( + 'Exception list is null when it never should be. This indicates potentially that saved object migrations did not run correctly. Returning empty exception list' + ); + return []; + } return exceptionsList.map((exceptionItem, index) => { const savedObjectReference = getSavedObjectReferenceForExceptionsList({ + logger, index, savedObjectReferences, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts new file mode 100644 index 0000000000000..a80f19ae011d7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.test.ts @@ -0,0 +1,102 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; +import { SavedObjectReference } from 'src/core/server'; +import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './utils'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; +import { injectReferences } from './inject_references'; +import { RuleParams } from '../../schemas/rule_schemas'; + +describe('inject_references', () => { + type FuncReturn = ReturnType; + let logger = loggingSystemMock.create().get('security_solution'); + const mockExceptionsList = (): RuleParams['exceptionsList'] => [ + { + id: '123', + list_id: '456', + type: 'detection', + namespace_type: 'agnostic', + }, + ]; + const mockSavedObjectReferences = (): SavedObjectReference[] => [ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]; + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('returns parameters from a saved object if found', () => { + const params: Partial = { + note: 'some note', + exceptionsList: mockExceptionsList(), + }; + expect( + injectReferences({ + logger, + params: params as RuleParams, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(params as RuleParams); + }); + + test('returns parameters from the saved object if found with a different saved object reference id', () => { + const params: Partial = { + note: 'some note', + exceptionsList: mockExceptionsList(), + }; + + const returnParams: Partial = { + note: 'some note', + exceptionsList: [{ ...mockExceptionsList()[0], id: '456' }], + }; + + expect( + injectReferences({ + logger, + params: params as RuleParams, + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], id: '456' }], + }) + ).toEqual(returnParams as RuleParams); + }); + + test('It returns params untouched and the references an empty array if the exceptionsList is an empty array', () => { + const params: Partial = { + note: 'some note', + exceptionsList: [], + }; + expect( + injectReferences({ + logger, + params: params as RuleParams, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(params as RuleParams); + }); + + test('It returns params with an added exceptionsList if the exceptionsList is missing due to migration bugs', () => { + const params: Partial = { + note: 'some note', + }; + const returnParams: Partial = { + note: 'some note', + exceptionsList: [], + }; + expect( + injectReferences({ + logger, + params: params as RuleParams, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(returnParams as RuleParams); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts new file mode 100644 index 0000000000000..41dc2d9179d83 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.test.ts @@ -0,0 +1,30 @@ +/* + * 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 { getSavedObjectNamePattern } from '.'; + +describe('get_saved_object_name_pattern_for_exception_list', () => { + test('returns expected pattern given a zero', () => { + expect(getSavedObjectNamePattern({ name: 'test', index: 0 })).toEqual('test_0'); + }); + + test('returns expected pattern given a positive number', () => { + expect(getSavedObjectNamePattern({ name: 'test', index: 1 })).toEqual('test_1'); + }); + + test('throws given less than zero', () => { + expect(() => getSavedObjectNamePattern({ name: 'test', index: -1 })).toThrow( + '"index" should alway be >= 0 instead of: -1' + ); + }); + + test('throws given NaN', () => { + expect(() => getSavedObjectNamePattern({ name: 'test', index: NaN })).toThrow( + '"index" should alway be >= 0 instead of: NaN' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts index 7c2bd89d9025e..f4e33cf57fa2b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern.ts @@ -17,4 +17,10 @@ export const getSavedObjectNamePattern = ({ }: { name: string; index: number; -}): string => `${name}_${index}`; +}): string => { + if (!(index >= 0)) { + throw new TypeError(`"index" should alway be >= 0 instead of: ${index}`); + } else { + return `${name}_${index}`; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts new file mode 100644 index 0000000000000..98c575e8835be --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.test.ts @@ -0,0 +1,37 @@ +/* + * 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 { + EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, + getSavedObjectNamePatternForExceptionsList, +} from '.'; + +describe('get_saved_object_name_pattern_for_exception_list', () => { + test('returns expected pattern given a zero', () => { + expect(getSavedObjectNamePatternForExceptionsList(0)).toEqual( + `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0` + ); + }); + + test('returns expected pattern given a positive number', () => { + expect(getSavedObjectNamePatternForExceptionsList(1)).toEqual( + `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_1` + ); + }); + + test('throws given less than zero', () => { + expect(() => getSavedObjectNamePatternForExceptionsList(-1)).toThrow( + '"index" should alway be >= 0 instead of: -1' + ); + }); + + test('throws given NaN', () => { + expect(() => getSavedObjectNamePatternForExceptionsList(NaN)).toThrow( + '"index" should alway be >= 0 instead of: NaN' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts index 01f1df0c69aa6..c505309411689 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_name_pattern_for_exception_list.ts @@ -12,6 +12,12 @@ import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; * Given an index this will return the pattern of "exceptionsList_${index}" * @param index The index to suffix the string * @returns The pattern of "exceptionsList_${index}" + * @throws TypeError if index is less than zero */ -export const getSavedObjectNamePatternForExceptionsList = (index: number): string => - getSavedObjectNamePattern({ name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index }); +export const getSavedObjectNamePatternForExceptionsList = (index: number): string => { + if (!(index >= 0)) { + throw new TypeError(`"index" should alway be >= 0 instead of: ${index}`); + } else { + return getSavedObjectNamePattern({ name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, index }); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts new file mode 100644 index 0000000000000..70f321eed030e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; +import { SavedObjectReference } from 'src/core/server'; +import { getSavedObjectReference } from '.'; + +describe('get_saved_object_reference', () => { + type FuncReturn = ReturnType; + const mockSavedObjectReferences = (): SavedObjectReference[] => [ + { + id: '123', + name: 'test_0', + type: 'some-type', + }, + ]; + let logger = loggingSystemMock.create().get('security_solution'); + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('returns reference found, given index zero', () => { + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 0, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(mockSavedObjectReferences()[0]); + }); + + test('returns reference found, given positive index', () => { + const savedObjectReferences: SavedObjectReference[] = [ + mockSavedObjectReferences()[0], + { + id: '345', + name: 'test_1', + type: 'some-type', + }, + ]; + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 1, + savedObjectReferences, + }) + ).toEqual(savedObjectReferences[1]); + }); + + test('returns undefined, given index larger than the size of object references', () => { + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 100, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(undefined); + }); + + test('returns undefined, when it cannot find the reference', () => { + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 0, + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], name: 'other-name_0' }], + }) + ).toEqual(undefined); + }); + + test('returns found reference, even if the reference is mixed with other references', () => { + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 0, + savedObjectReferences: [ + { ...mockSavedObjectReferences()[0], name: 'other-name_0' }, + mockSavedObjectReferences()[0], + ], + }) + ).toEqual(mockSavedObjectReferences()[0]); + }); + + test('returns found reference, even if the reference is mixed with other references and has an index of 1', () => { + const additionalException: SavedObjectReference = { + ...mockSavedObjectReferences()[0], + name: 'test_1', + }; + expect( + getSavedObjectReference({ + name: 'test', + logger, + index: 1, + savedObjectReferences: [ + { ...mockSavedObjectReferences()[0], name: 'other-name_0' }, + mockSavedObjectReferences()[0], + additionalException, + ], + }) + ).toEqual(additionalException); + }); + + test('throws given less than zero', () => { + expect(() => + getSavedObjectReference({ + name: 'test', + logger, + index: -1, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toThrow('"index" should alway be >= 0 instead of: -1'); + }); + + test('throws given NaN', () => { + expect(() => + getSavedObjectReference({ + name: 'test', + logger, + index: NaN, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toThrow('"index" should alway be >= 0 instead of: NaN'); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts index 2d50125c2d95c..fe3a7393bf377 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference.ts @@ -5,28 +5,44 @@ * 2.0. */ -import { SavedObjectReference } from 'src/core/server'; +import { Logger, SavedObjectReference } from 'src/core/server'; import { getSavedObjectNamePattern } from './get_saved_object_name_pattern'; /** * Given a saved object name, and an index, this will return the specific named saved object reference * even if it is mixed in with other reference objects. This is needed since a references array can contain multiple * types of saved objects in a single array, we have to use the name to get the value. + * @param logger The kibana injected logger * @param name The name of the saved object reference we are getting from the array * @param index The index position to get for the exceptions list. * @param savedObjectReferences The saved object references which can contain "exceptionsList" mixed with other saved object types * @returns The saved object reference if found, otherwise undefined */ export const getSavedObjectReference = ({ + logger, name, index, savedObjectReferences, }: { + logger: Logger; name: string; index: number; savedObjectReferences: SavedObjectReference[]; }): SavedObjectReference | undefined => { - return savedObjectReferences.find( - (reference) => reference.name === getSavedObjectNamePattern({ name, index }) - ); + if (!(index >= 0)) { + throw new TypeError(`"index" should alway be >= 0 instead of: ${index}`); + } else if (index > savedObjectReferences.length) { + logger.error( + [ + 'Cannot get a saved object reference using an index which is larger than the saved object references. Index is:', + index, + ' which is larger than the savedObjectReferences:', + JSON.stringify(savedObjectReferences), + ].join('') + ); + } else { + return savedObjectReferences.find( + (reference) => reference.name === getSavedObjectNamePattern({ name, index }) + ); + } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts new file mode 100644 index 0000000000000..9a16037ed7fd5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; +import { SavedObjectReference } from 'src/core/server'; +import { + EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, + getSavedObjectReferenceForExceptionsList, +} from '.'; +import { EXCEPTION_LIST_NAMESPACE } from '@kbn/securitysolution-list-constants'; + +describe('get_saved_object_reference_for_exceptions_list', () => { + type FuncReturn = ReturnType; + let logger = loggingSystemMock.create().get('security_solution'); + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + const mockSavedObjectReferences = (): SavedObjectReference[] => [ + { + id: '123', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_0`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]; + + test('returns reference found, given index zero', () => { + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 0, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(mockSavedObjectReferences()[0]); + }); + + test('returns reference found, given positive index', () => { + const savedObjectReferences: SavedObjectReference[] = [ + mockSavedObjectReferences()[0], + { + id: '345', + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_1`, + type: EXCEPTION_LIST_NAMESPACE, + }, + ]; + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 1, + savedObjectReferences, + }) + ).toEqual(savedObjectReferences[1]); + }); + + test('returns undefined, given index larger than the size of object references', () => { + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 100, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toEqual(undefined); + }); + + test('returns undefined, when it cannot find the reference', () => { + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 0, + savedObjectReferences: [{ ...mockSavedObjectReferences()[0], name: 'other-name_0' }], + }) + ).toEqual(undefined); + }); + + test('returns found reference, even if the reference is mixed with other references', () => { + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 0, + savedObjectReferences: [ + { ...mockSavedObjectReferences()[0], name: 'other-name_0' }, + mockSavedObjectReferences()[0], + ], + }) + ).toEqual(mockSavedObjectReferences()[0]); + }); + + test('returns found reference, even if the reference is mixed with other references and has an index of 1', () => { + const additionalException: SavedObjectReference = { + ...mockSavedObjectReferences()[0], + name: `${EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME}_1`, + }; + expect( + getSavedObjectReferenceForExceptionsList({ + logger, + index: 1, + savedObjectReferences: [ + { ...mockSavedObjectReferences()[0], name: 'other-name_0' }, + mockSavedObjectReferences()[0], + additionalException, + ], + }) + ).toEqual(additionalException); + }); + + test('throws given less than zero', () => { + expect(() => + getSavedObjectReferenceForExceptionsList({ + logger, + index: -1, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toThrow('"index" should alway be >= 0 instead of: -1'); + }); + + test('throws given NaN', () => { + expect(() => + getSavedObjectReferenceForExceptionsList({ + logger, + index: NaN, + savedObjectReferences: mockSavedObjectReferences(), + }) + ).toThrow('"index" should alway be >= 0 instead of: NaN'); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts index 60e8132d474dd..d1534cc2a06bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/get_saved_object_reference_for_exceptions_list.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObjectReference } from 'src/core/server'; +import { Logger, SavedObjectReference } from 'src/core/server'; import { EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME } from './constants'; import { getSavedObjectReference } from './get_saved_object_reference'; @@ -13,19 +13,28 @@ import { getSavedObjectReference } from './get_saved_object_reference'; * Given an index and a saved object reference, this will return the specific "exceptionsList" saved object reference * even if it is mixed in with other reference objects. This is needed since a references array can contain multiple * types of saved objects in a single array, we have to use the "exceptionsList" name to get the value. + * @param logger The kibana injected logger * @param index The index position to get for the exceptions list. * @param savedObjectReferences The saved object references which can contain "exceptionsList" mixed with other saved object types * @returns The saved object reference if found, otherwise undefined */ export const getSavedObjectReferenceForExceptionsList = ({ + logger, index, savedObjectReferences, }: { + logger: Logger; index: number; savedObjectReferences: SavedObjectReference[]; -}): SavedObjectReference | undefined => - getSavedObjectReference({ - name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, - index, - savedObjectReferences, - }); +}): SavedObjectReference | undefined => { + if (!(index >= 0)) { + throw new TypeError(`"index" should alway be >= 0 instead of: ${index}`); + } else { + return getSavedObjectReference({ + logger, + name: EXCEPTIONS_SAVED_OBJECT_REFERENCE_NAME, + index, + savedObjectReferences, + }); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts new file mode 100644 index 0000000000000..d0158be285667 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.test.ts @@ -0,0 +1,33 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; + +import { logMissingSavedObjectError } from '.'; + +describe('log_missing_saved_object_error', () => { + let logger = loggingSystemMock.create().get('security_solution'); + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('logs expect error message', () => { + logMissingSavedObjectError({ + logger, + exceptionItem: { + id: '123', + list_id: '456', + type: 'detection', + namespace_type: 'agnostic', + }, + }); + expect(logger.error).toBeCalledWith( + 'The saved object references were not found for our exception list when we were expecting to find it. Kibana migrations might not have run correctly or someone might have removed the saved object references manually. Returning the last known good exception list id which might not work. exceptionItem with its id being returned is: {"id":"123","list_id":"456","type":"detection","namespace_type":"agnostic"}' + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts index 9f0f33adaa007..8d448c3cd10c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_missing_saved_object_error.ts @@ -20,11 +20,12 @@ export const logMissingSavedObjectError = ({ logger: Logger; exceptionItem: RuleParams['exceptionsList'][0]; }): void => { - logger.warn( + logger.error( [ - 'The saved object references were not found for our exception list when we were expecting to find it. Kibana migrations might not have run correctly or someone might have removed it manually.', - 'Returning the last known good exception list id which might not work.', + 'The saved object references were not found for our exception list when we were expecting to find it. ', + 'Kibana migrations might not have run correctly or someone might have removed the saved object references manually. ', + 'Returning the last known good exception list id which might not work. exceptionItem with its id being returned is: ', JSON.stringify(exceptionItem), - ].join() + ].join('') ); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.test.ts new file mode 100644 index 0000000000000..a27faa6356c2b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.test.ts @@ -0,0 +1,38 @@ +/* + * 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 { loggingSystemMock } from 'src/core/server/mocks'; + +import { logWarningIfDifferentReferencesDetected } from '.'; + +describe('log_warning_if_different_references_detected', () => { + let logger = loggingSystemMock.create().get('security_solution'); + + beforeEach(() => { + logger = loggingSystemMock.create().get('security_solution'); + }); + + test('logs expect error message if the two ids are different', () => { + logWarningIfDifferentReferencesDetected({ + logger, + savedObjectReferenceId: '123', + savedObjectId: '456', + }); + expect(logger.error).toBeCalledWith( + 'The id of the "saved object reference id": 123 is not the same as the "saved object id": 456. Preferring and using the "saved object reference id" instead of the "saved object id"' + ); + }); + + test('logs nothing if the two ids are the same', () => { + logWarningIfDifferentReferencesDetected({ + logger, + savedObjectReferenceId: '123', + savedObjectId: '123', + }); + expect(logger.error).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts index 060304121d641..9f80ba6d8ce83 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/utils/log_warning_if_different_references_detected.ts @@ -23,13 +23,13 @@ export const logWarningIfDifferentReferencesDetected = ({ savedObjectId: string; }): void => { if (savedObjectReferenceId !== savedObjectId) { - logger.warn( + logger.error( [ - 'The id of the saved object reference: ', + 'The id of the "saved object reference id": ', savedObjectReferenceId, - 'is not the same as the saved object id: ', + ' is not the same as the "saved object id": ', savedObjectId, - 'Preferring and using the saved object reference id instead of the saved object id', + '. Preferring and using the "saved object reference id" instead of the "saved object id"', ].join('') ); } From bb1a2d527c2d5dea7f665b59e58a488c834ae377 Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Tue, 10 Aug 2021 15:40:29 -0600 Subject: [PATCH 10/10] Fix one PR item about JSDocs --- .../signals/saved_object_references/inject_references.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts index f05bcfe569be7..dae5e3037b737 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/saved_object_references/inject_references.ts @@ -25,6 +25,7 @@ import { injectExceptionsReferences } from './inject_exceptions_list'; * }; * @param logger Kibana injected logger * @param params The params of the base rule(s). + * @param savedObjectReferences The saved object references to merge with the rule params * @returns The rule parameters with the saved object references. */ export const injectReferences = ({