diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index c854f2e13b994..f3f0c6921a248 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -10,7 +10,7 @@ import { useDispatch } from 'react-redux'; import { EuiContextMenuItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ALERT_RULE_EXCEPTIONS_LIST } from '@kbn/rule-data-utils'; +import { ALERT_RULE_EXCEPTIONS_LIST, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import type { ExceptionListId } from '@kbn/securitysolution-io-ts-list-types'; import { useApi } from '@kbn/securitysolution-list-hooks'; @@ -52,9 +52,27 @@ export const useInvestigateInTimeline = ({ const getExceptionFilter = useCallback( async (ecsData: Ecs): Promise => { - const exceptionsLists = (getField(ecsData, ALERT_RULE_EXCEPTIONS_LIST) ?? []).reduce( - (acc: ExceptionListId[], next: string) => { - const parsedList = JSON.parse(next); + // This pulls exceptions list information from `_source` + // This primarily matters for the old `signal` alerts a user may be viewing + // as new exception lists are pulled from kibana.alert.rule.parameters[0].exception_lists; + // Source was removed in favour of the fields api which passes the exceptions_list via `kibana.alert.rule.parameters` + let exceptionsList = getField(ecsData, ALERT_RULE_EXCEPTIONS_LIST) ?? []; + + if (exceptionsList.length === 0) { + try { + const ruleParameters = getField(ecsData, ALERT_RULE_PARAMETERS) ?? {}; + if (ruleParameters.length > 0) { + const parametersObject = JSON.parse(ruleParameters[0]); + exceptionsList = parametersObject?.exceptions_list ?? []; + } + } catch (error) { + // do nothing, just fail silently as parametersObject is initialized + } + } + const detectionExceptionsLists = exceptionsList.reduce( + (acc: ExceptionListId[], next: string | object) => { + // parsed rule.parameters returns an object else use the default string representation + const parsedList = typeof next === 'string' ? JSON.parse(next) : next; if (parsedList.type === 'detection') { const formattedList = { exception_list_id: parsedList.list_id, @@ -67,14 +85,15 @@ export const useInvestigateInTimeline = ({ [] ); - if (exceptionsLists.length > 0) { + let exceptionFilter; + if (detectionExceptionsLists.length > 0) { await getExceptionFilterFromIds({ - exceptionListIds: exceptionsLists, + exceptionListIds: detectionExceptionsLists, excludeExceptions: true, chunkSize: 20, alias: 'Exceptions', onSuccess: (filter) => { - return filter; + exceptionFilter = filter; }, onError: (err: string[]) => { addError(err, { @@ -86,7 +105,7 @@ export const useInvestigateInTimeline = ({ }, }); } - return undefined; + return exceptionFilter; }, [addError, getExceptionFilterFromIds] ); diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts index 09cd43a5dad34..3aec85af09261 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { ALERT_RULE_CONSUMER, ALERT_RISK_SCORE, ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { + ALERT_RULE_CONSUMER, + ALERT_RISK_SCORE, + ALERT_SEVERITY, + ALERT_RULE_PARAMETERS, +} from '@kbn/rule-data-utils'; import { ENRICHMENT_DESTINATION_PATH } from '../../../../../common/constants'; export const MATCHED_ATOMIC = 'matched.atomic'; @@ -40,6 +45,7 @@ export const CTI_ROW_RENDERER_FIELDS = [ FEED_NAME_REFERENCE, ]; +// TODO: update all of these fields to use the constants from technical field names export const TIMELINE_EVENTS_FIELDS = [ ALERT_RULE_CONSUMER, '@timestamp', @@ -58,6 +64,7 @@ export const TIMELINE_EVENTS_FIELDS = [ 'kibana.alert.rule.version', ALERT_SEVERITY, ALERT_RISK_SCORE, + ALERT_RULE_PARAMETERS, 'kibana.alert.threshold_result', 'kibana.alert.building_block_type', 'kibana.alert.suppression.docs_count', diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts index ff2ee23643190..5117f8dc889ed 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts @@ -414,6 +414,50 @@ describe('formatTimelineData', () => { field: 'kibana.alert.rule.uuid', value: ['15d82f10-0926-11ed-bece-6b0c033d0075'], }, + { + field: 'kibana.alert.rule.parameters.sourceId', + value: ['default'], + }, + { + field: 'kibana.alert.rule.parameters.nodeType', + value: ['host'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.comparator', + value: ['>'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.timeSize', + value: ['1'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.metric', + value: ['cpu'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.threshold', + value: ['10'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.customMetric.aggregation', + value: ['avg'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.customMetric.id', + value: ['alert-custom-metric'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.customMetric.field', + value: [''], + }, + { + field: 'kibana.alert.rule.parameters.criteria.customMetric.type', + value: ['custom'], + }, + { + field: 'kibana.alert.rule.parameters.criteria.timeUnit', + value: ['d'], + }, { field: 'event.action', value: ['active'], @@ -466,50 +510,6 @@ describe('formatTimelineData', () => { field: 'kibana.version', value: ['8.4.0'], }, - { - field: 'kibana.alert.rule.parameters.sourceId', - value: ['default'], - }, - { - field: 'kibana.alert.rule.parameters.nodeType', - value: ['host'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.comparator', - value: ['>'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.timeSize', - value: ['1'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.metric', - value: ['cpu'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.threshold', - value: ['10'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.customMetric.aggregation', - value: ['avg'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.customMetric.id', - value: ['alert-custom-metric'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.customMetric.field', - value: [''], - }, - { - field: 'kibana.alert.rule.parameters.criteria.customMetric.type', - value: ['custom'], - }, - { - field: 'kibana.alert.rule.parameters.criteria.timeUnit', - value: ['d'], - }, ], ecs: { '@timestamp': ['2022-07-21T22:38:57.888Z'], @@ -528,6 +528,9 @@ describe('formatTimelineData', () => { consumer: ['infrastructure'], name: ['test 1212'], uuid: ['15d82f10-0926-11ed-bece-6b0c033d0075'], + parameters: [ + '{"sourceId":"default","nodeType":"host","criteria":[{"comparator":">","timeSize":1,"metric":"cpu","threshold":[10],"customMetric":{"aggregation":"avg","id":"alert-custom-metric","field":"","type":"custom"},"timeUnit":"d"}]}', + ], }, workflow_status: ['open'], },