"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.updateAlertStatusAction = exports.sendBulkEventsToTimelineAction = exports.sendAlertToTimelineAction = exports.isThresholdAlert = exports.isNewTermsAlert = exports.isEqlAlertWithGroupId = exports.getThresholdAggregationData = exports.getNewTermsData = exports.determineToAndFrom = exports.buildAlertsKqlFilter = void 0; var _fp = require("lodash/fp"); var _moment = _interopRequireDefault(require("moment")); var _datemath = _interopRequireDefault(require("@kbn/datemath")); var _esQuery = require("@kbn/es-query"); var _i18n = require("@kbn/i18n"); var _ruleDataUtils = require("@kbn/rule-data-utils"); var _rxjs = require("rxjs"); var _field_names = require("../../../../common/field_maps/field_names"); var _timeline = require("../../../../common/types/timeline"); var _timeline2 = require("../../../../common/api/timeline"); var _timeline3 = require("../../../../common/search_strategy/timeline"); var _defaults = require("../../../timelines/store/timeline/defaults"); var _helpers = require("../../../timelines/components/open_timeline/helpers"); var _kuery = require("../../../common/lib/kuery"); var _helpers2 = require("../../../helpers"); var _helpers3 = require("./helpers"); var _api = require("../../../timelines/containers/api"); var _kibana = require("../../../common/lib/kibana"); var _constants = require("../../../../common/constants"); var _alerts = require("../../../common/utils/alerts"); var _default_date_settings = require("../../../common/utils/default_date_settings"); var _update_alerts = require("../../../common/components/toolbar/bulk_actions/update_alerts"); /* * 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. */ /* eslint-disable complexity */ const updateAlertStatusAction = async ({ query, alertIds, selectedStatus, setEventsLoading, setEventsDeleted, onAlertStatusUpdateSuccess, onAlertStatusUpdateFailure }) => { try { var _response$updated, _response$version_con; setEventsLoading({ eventIds: alertIds, isLoading: true }); const response = await (0, _update_alerts.updateAlertStatus)({ query: query && JSON.parse(query), status: selectedStatus, signalIds: alertIds }); // TODO: Only delete those that were successfully updated from updatedRules setEventsDeleted({ eventIds: alertIds, isDeleted: true }); if (response.version_conflicts && alertIds.length === 1) { throw new Error(_i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.updateAlertStatusFailedSingleAlert', { defaultMessage: 'Failed to update alert because it was already being modified.' })); } onAlertStatusUpdateSuccess((_response$updated = response.updated) !== null && _response$updated !== void 0 ? _response$updated : 0, (_response$version_con = response.version_conflicts) !== null && _response$version_con !== void 0 ? _response$version_con : 0, selectedStatus); } catch (error) { onAlertStatusUpdateFailure(selectedStatus, error); } finally { setEventsLoading({ eventIds: alertIds, isLoading: false }); } }; exports.updateAlertStatusAction = updateAlertStatusAction; const determineToAndFrom = ({ ecs }) => { if (Array.isArray(ecs)) { const timestamps = ecs.reduce((acc, item) => { const dateTimestamp = item.timestamp ? new Date(item.timestamp) : new Date(); if (!acc.includes(dateTimestamp.valueOf())) { return [...acc, dateTimestamp.valueOf()]; } return acc; }, []); return { from: new Date(Math.min(...timestamps)).toISOString(), to: new Date(Math.max(...timestamps)).toISOString() }; } const ecsData = ecs; const ruleFrom = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_FROM); const elapsedTimeRule = _moment.default.duration((0, _moment.default)().diff(_datemath.default.parse(ruleFrom != null ? ruleFrom[0] : 'now-1d'))); const alertTimestampEcsValue = (0, _helpers2.getField)(ecsData, _ruleDataUtils.TIMESTAMP); const alertTimestamp = Array.isArray(alertTimestampEcsValue) ? alertTimestampEcsValue[0] : alertTimestampEcsValue; const to = (0, _moment.default)(alertTimestamp !== null && alertTimestamp !== void 0 ? alertTimestamp : new Date()).toISOString(); const from = (0, _moment.default)(to).subtract(elapsedTimeRule).toISOString(); return { to, from }; }; exports.determineToAndFrom = determineToAndFrom; const calculateFromTimeFallback = (thresholdData, originalTime) => { // relative time that the rule's time range starts at (e.g. now-1h) const ruleFromValue = (0, _helpers2.getField)(thresholdData, _ruleDataUtils.ALERT_RULE_FROM); const normalizedRuleFromValue = Array.isArray(ruleFromValue) ? ruleFromValue[0] : ruleFromValue; const ruleFrom = _datemath.default.parse(normalizedRuleFromValue); // get the absolute (moment.duration) interval by subtracting `ruleFrom` from `now` const now = (0, _moment.default)(); const ruleInterval = _moment.default.duration(now.diff(ruleFrom)); // subtract the rule interval from the time the alert was generated... this will // overshoot and potentially contain false positives in the timeline results return originalTime.clone().subtract(ruleInterval); }; const getThresholdAggregationData = ecsData => { const thresholdEcsData = Array.isArray(ecsData) ? ecsData : [ecsData]; return thresholdEcsData.reduce((outerAcc, thresholdData) => { var _getField, _thresholdData$signal, _thresholdData$signal2, _thresholdResult$from; const threshold = (_getField = (0, _helpers2.getField)(thresholdData, `${_ruleDataUtils.ALERT_RULE_PARAMETERS}.threshold`)) !== null && _getField !== void 0 ? _getField : (_thresholdData$signal = thresholdData.signal) === null || _thresholdData$signal === void 0 ? void 0 : (_thresholdData$signal2 = _thresholdData$signal.rule) === null || _thresholdData$signal2 === void 0 ? void 0 : _thresholdData$signal2.threshold; const thresholdResult = (0, _helpers2.getField)(thresholdData, _field_names.ALERT_THRESHOLD_RESULT); // timestamp representing when the alert was generated const originalTimeValue = (0, _helpers2.getField)(thresholdData, _field_names.ALERT_ORIGINAL_TIME); const normalizedOriginalTimeValue = Array.isArray(originalTimeValue) ? originalTimeValue[0] : originalTimeValue; const originalTime = (0, _moment.default)(normalizedOriginalTimeValue); /* * Compute the fallback interval when `threshold_result.from` is not available * (for pre-7.12 backcompat) */ const fromOriginalTime = calculateFromTimeFallback(thresholdData, originalTime); const aggregationFields = Array.isArray(threshold.field) ? threshold.field : [threshold.field]; return { thresholdFrom: (_thresholdResult$from = thresholdResult.from) !== null && _thresholdResult$from !== void 0 ? _thresholdResult$from : fromOriginalTime.toISOString(), thresholdTo: originalTime.toISOString(), dataProviders: [...outerAcc.dataProviders, ...aggregationFields.reduce((acc, aggregationField, i) => { const aggregationValue = thresholdResult.terms.filter(term => term.field === aggregationField)[0].value; const dataProviderValue = Array.isArray(aggregationValue) ? aggregationValue[0] : aggregationValue; if (!dataProviderValue) { return acc; } const aggregationFieldId = aggregationField.replace('.', '-'); const dataProviderPartial = { id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${_timeline.TimelineId.active}-${aggregationFieldId}-${dataProviderValue}`, name: aggregationField, enabled: true, excluded: false, kqlQuery: '', queryMatch: { field: aggregationField, value: dataProviderValue, operator: ':' } }; if (i === 0) { return [...acc, { ...dataProviderPartial, and: [] }]; } else { acc[0].and.push(dataProviderPartial); return acc; } }, [])] }; }, { dataProviders: [], thresholdFrom: '', thresholdTo: '' }); }; exports.getThresholdAggregationData = getThresholdAggregationData; const isEqlAlertWithGroupId = ecsData => { const ruleType = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_TYPE); const groupId = (0, _helpers2.getField)(ecsData, _field_names.ALERT_GROUP_ID); const isEql = ruleType === 'eql' || Array.isArray(ruleType) && ruleType[0] === 'eql'; return isEql && (groupId === null || groupId === void 0 ? void 0 : groupId.length) > 0; }; exports.isEqlAlertWithGroupId = isEqlAlertWithGroupId; const isThresholdAlert = ecsData => { const ruleType = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_TYPE); return ruleType === 'threshold' || Array.isArray(ruleType) && ruleType.length > 0 && ruleType[0] === 'threshold'; }; exports.isThresholdAlert = isThresholdAlert; const isNewTermsAlert = ecsData => { const ruleType = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_TYPE); return ruleType === 'new_terms' || Array.isArray(ruleType) && ruleType.length > 0 && ruleType[0] === 'new_terms'; }; exports.isNewTermsAlert = isNewTermsAlert; const isSuppressedAlert = ecsData => { return (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_SUPPRESSION_DOCS_COUNT) != null; }; const buildAlertsKqlFilter = (key, alertIds, label = 'Alert Ids') => { const singleId = alertIds.length === 1; if (singleId) { return [{ meta: { alias: null, negate: false, disabled: false, type: 'phrase', key, params: { query: alertIds[0] } }, query: { match_phrase: { _id: alertIds[0] } }, $state: { store: _esQuery.FilterStateStore.APP_STATE } }]; } return [{ query: { bool: { filter: { ids: { values: alertIds } } } }, meta: { alias: label, negate: false, disabled: false, type: 'phrases', key, value: alertIds.join(), params: alertIds }, $state: { store: _esQuery.FilterStateStore.APP_STATE } }]; }; exports.buildAlertsKqlFilter = buildAlertsKqlFilter; const buildEventsDataProviderById = (key, eventIds) => { const singleId = eventIds.length === 1; return [{ and: [], id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${_timeline.TimelineId.active}-alert-id-${eventIds.join(',')}`, name: eventIds.join(','), enabled: true, excluded: false, kqlQuery: '', queryMatch: { field: key, // @ts-ignore till https://github.com/elastic/kibana/pull/142436 is merged value: singleId ? eventIds[0] : eventIds, // @ts-ignore till https://github.com/elastic/kibana/pull/142436 is merged operator: singleId ? ':' : 'includes' } }]; }; const buildTimelineDataProviderOrFilter = (alertIds, prefer, label) => { return { filters: prefer === 'KqlFilter' ? buildAlertsKqlFilter('_id', alertIds, label) : [], dataProviders: prefer === 'dataProvider' ? buildEventsDataProviderById('_id', alertIds) : [] }; }; const buildEqlDataProviderOrFilter = (alertIds, ecs) => { if (!(0, _fp.isEmpty)(alertIds) && Array.isArray(ecs) && ecs.length > 1) { return { dataProviders: [], filters: buildAlertsKqlFilter(_field_names.ALERT_GROUP_ID, ecs.reduce((acc, ecsData) => { const alertGroupIdField = (0, _helpers2.getField)(ecsData, _field_names.ALERT_GROUP_ID); const alertGroupId = Array.isArray(alertGroupIdField) ? alertGroupIdField[0] : alertGroupIdField; if (!acc.includes(alertGroupId)) { return [...acc, alertGroupId]; } return acc; }, [])) }; } else if (!Array.isArray(ecs) || ecs.length === 1) { const ecsData = Array.isArray(ecs) ? ecs[0] : ecs; const alertGroupIdField = (0, _helpers2.getField)(ecsData, _field_names.ALERT_GROUP_ID); const queryMatchField = (0, _helpers2.getFieldKey)(ecsData, _field_names.ALERT_GROUP_ID); const alertGroupId = Array.isArray(alertGroupIdField) ? alertGroupIdField[0] : alertGroupIdField; return { dataProviders: [{ and: [], id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${_timeline.TimelineId.active}-alert-id-${alertGroupId}`, name: ecsData._id, enabled: true, excluded: false, kqlQuery: '', queryMatch: { field: queryMatchField, value: alertGroupId, operator: ':' } }], filters: [] }; } return { filters: [], dataProviders: [] }; }; const createThresholdTimeline = async (ecsData, createTimeline, noteContent, templateValues, getExceptionFilter) => { try { var _alertResponse$hits$h, _ref, _filters, _alertDoc$signal, _alertDoc$signal$rule, _ref2, _params$language, _alertDoc$signal2, _alertDoc$signal2$rul, _ref3, _params$query, _alertDoc$signal3, _alertDoc$signal3$rul, _ref4, _getField2, _alertDoc$signal4, _alertDoc$signal4$rul, _templateValues$filte, _templateValues$colum, _templateValues$dataP, _templateValues$query, _templateValues$query2; const alertResponse = await _kibana.KibanaServices.get().http.fetch(_constants.DETECTION_ENGINE_QUERY_SIGNALS_URL, { method: 'POST', body: JSON.stringify((0, _alerts.buildAlertsQuery)([ecsData._id])) }); const formattedAlertData = (_alertResponse$hits$h = alertResponse === null || alertResponse === void 0 ? void 0 : alertResponse.hits.hits.reduce((acc, { _id, _index, _source = {} }) => { return [...acc, { ...(0, _alerts.formatAlertToEcsSignal)(_source), _id, _index, timestamp: _source['@timestamp'] }]; }, [])) !== null && _alertResponse$hits$h !== void 0 ? _alertResponse$hits$h : []; const alertDoc = formattedAlertData[0]; const params = (0, _helpers2.getField)(alertDoc, _ruleDataUtils.ALERT_RULE_PARAMETERS); const ruleAuthor = (0, _helpers2.getField)(alertDoc, _ruleDataUtils.ALERT_RULE_CREATED_BY); const filters = (_ref = (_filters = params.filters) !== null && _filters !== void 0 ? _filters : (_alertDoc$signal = alertDoc.signal) === null || _alertDoc$signal === void 0 ? void 0 : (_alertDoc$signal$rule = _alertDoc$signal.rule) === null || _alertDoc$signal$rule === void 0 ? void 0 : _alertDoc$signal$rule.filters) !== null && _ref !== void 0 ? _ref : []; // https://github.com/elastic/kibana/issues/126574 - if the provided filter has no `meta` field // we expect an empty object to be inserted before calling `createTimeline` const augmentedFilters = filters.map(filter => { return filter.meta != null ? filter : { ...filter, meta: {} }; }); const language = (_ref2 = (_params$language = params.language) !== null && _params$language !== void 0 ? _params$language : (_alertDoc$signal2 = alertDoc.signal) === null || _alertDoc$signal2 === void 0 ? void 0 : (_alertDoc$signal2$rul = _alertDoc$signal2.rule) === null || _alertDoc$signal2$rul === void 0 ? void 0 : _alertDoc$signal2$rul.language) !== null && _ref2 !== void 0 ? _ref2 : 'kuery'; const query = (_ref3 = (_params$query = params.query) !== null && _params$query !== void 0 ? _params$query : (_alertDoc$signal3 = alertDoc.signal) === null || _alertDoc$signal3 === void 0 ? void 0 : (_alertDoc$signal3$rul = _alertDoc$signal3.rule) === null || _alertDoc$signal3$rul === void 0 ? void 0 : _alertDoc$signal3$rul.query) !== null && _ref3 !== void 0 ? _ref3 : ''; const indexNames = (_ref4 = (_getField2 = (0, _helpers2.getField)(alertDoc, _field_names.ALERT_RULE_INDICES)) !== null && _getField2 !== void 0 ? _getField2 : (_alertDoc$signal4 = alertDoc.signal) === null || _alertDoc$signal4 === void 0 ? void 0 : (_alertDoc$signal4$rul = _alertDoc$signal4.rule) === null || _alertDoc$signal4$rul === void 0 ? void 0 : _alertDoc$signal4$rul.index) !== null && _ref4 !== void 0 ? _ref4 : []; const { thresholdFrom, thresholdTo, dataProviders } = getThresholdAggregationData(alertDoc); const exceptionsFilter = await getExceptionFilter(ecsData); const allFilters = ((_templateValues$filte = templateValues.filters) !== null && _templateValues$filte !== void 0 ? _templateValues$filte : augmentedFilters).concat(!exceptionsFilter ? [] : [exceptionsFilter]); return createTimeline({ from: thresholdFrom, notes: null, timeline: { ..._defaults.timelineDefaults, columns: (_templateValues$colum = templateValues.columns) !== null && _templateValues$colum !== void 0 ? _templateValues$colum : _defaults.timelineDefaults.columns, description: `_id: ${alertDoc._id}`, filters: allFilters, dataProviders: (_templateValues$dataP = templateValues.dataProviders) !== null && _templateValues$dataP !== void 0 ? _templateValues$dataP : dataProviders, id: _timeline.TimelineId.active, indexNames, dateRange: { start: thresholdFrom, end: thresholdTo }, eventType: 'all', kqlQuery: { filterQuery: { kuery: { kind: language, expression: (_templateValues$query = templateValues.query) !== null && _templateValues$query !== void 0 ? _templateValues$query : query }, serializedQuery: (_templateValues$query2 = templateValues.query) !== null && _templateValues$query2 !== void 0 ? _templateValues$query2 : query } } }, to: thresholdTo, ruleNote: noteContent, ruleAuthor }); } catch (error) { const { toasts } = _kibana.KibanaServices.get().notifications; toasts.addError(error, { toastMessage: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createThresholdTimelineFailure', { defaultMessage: 'Failed to create timeline for document _id: {id}', values: { id: ecsData._id } }), title: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createThresholdTimelineFailureTitle', { defaultMessage: 'Failed to create threshold alert timeline' }) }); const from = _default_date_settings.DEFAULT_FROM_MOMENT.toISOString(); const to = _default_date_settings.DEFAULT_TO_MOMENT.toISOString(); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all' }, to }); } }; const getNewTermsData = ecsData => { var _getField3; const normalizedEcsData = Array.isArray(ecsData) ? ecsData[0] : ecsData; const originalTimeValue = (0, _helpers2.getField)(normalizedEcsData, _field_names.ALERT_ORIGINAL_TIME); const newTermsFields = (_getField3 = (0, _helpers2.getField)(normalizedEcsData, `${_ruleDataUtils.ALERT_RULE_PARAMETERS}.new_terms_fields`)) !== null && _getField3 !== void 0 ? _getField3 : []; const dataProviderPartials = newTermsFields.map((newTermsField, index) => { const newTermsFieldId = newTermsField.replace('.', '-'); const newTermsValue = (0, _helpers2.getField)(normalizedEcsData, _field_names.ALERT_NEW_TERMS)[index]; return { id: `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${_timeline.TimelineId.active}-${newTermsFieldId}-${newTermsValue}`, name: newTermsField, enabled: true, excluded: false, kqlQuery: '', queryMatch: { field: newTermsField, value: newTermsValue, operator: ':' }, and: [] }; }); const dataProviders = dataProviderPartials.length ? [{ ...dataProviderPartials[0], and: dataProviderPartials.slice(1) }] : []; return { from: originalTimeValue, to: (0, _moment.default)().toISOString(), dataProviders }; }; exports.getNewTermsData = getNewTermsData; const createNewTermsTimeline = async (ecsData, createTimeline, noteContent, templateValues, getExceptionFilter) => { try { var _alertResponse$hits$h2, _ref5, _filters2, _alertDoc$signal5, _alertDoc$signal5$rul, _ref6, _params$language2, _alertDoc$signal6, _alertDoc$signal6$rul, _ref7, _params$query2, _alertDoc$signal7, _alertDoc$signal7$rul, _ref8, _getField4, _alertDoc$signal8, _alertDoc$signal8$rul, _templateValues$filte2, _templateValues$colum2, _templateValues$dataP2, _templateValues$query3, _templateValues$query4; const alertResponse = await _kibana.KibanaServices.get().http.fetch(_constants.DETECTION_ENGINE_QUERY_SIGNALS_URL, { method: 'POST', body: JSON.stringify((0, _alerts.buildAlertsQuery)([ecsData._id])) }); const formattedAlertData = (_alertResponse$hits$h2 = alertResponse === null || alertResponse === void 0 ? void 0 : alertResponse.hits.hits.reduce((acc, { _id, _index, _source = {} }) => { return [...acc, { ...(0, _alerts.formatAlertToEcsSignal)(_source), _id, _index, timestamp: _source['@timestamp'] }]; }, [])) !== null && _alertResponse$hits$h2 !== void 0 ? _alertResponse$hits$h2 : []; const alertDoc = formattedAlertData[0]; const params = (0, _helpers2.getField)(alertDoc, _ruleDataUtils.ALERT_RULE_PARAMETERS); const filters = (_ref5 = (_filters2 = params.filters) !== null && _filters2 !== void 0 ? _filters2 : (_alertDoc$signal5 = alertDoc.signal) === null || _alertDoc$signal5 === void 0 ? void 0 : (_alertDoc$signal5$rul = _alertDoc$signal5.rule) === null || _alertDoc$signal5$rul === void 0 ? void 0 : _alertDoc$signal5$rul.filters) !== null && _ref5 !== void 0 ? _ref5 : []; // https://github.com/elastic/kibana/issues/126574 - if the provided filter has no `meta` field // we expect an empty object to be inserted before calling `createTimeline` const augmentedFilters = filters.map(filter => { return filter.meta != null ? filter : { ...filter, meta: {} }; }); const language = (_ref6 = (_params$language2 = params.language) !== null && _params$language2 !== void 0 ? _params$language2 : (_alertDoc$signal6 = alertDoc.signal) === null || _alertDoc$signal6 === void 0 ? void 0 : (_alertDoc$signal6$rul = _alertDoc$signal6.rule) === null || _alertDoc$signal6$rul === void 0 ? void 0 : _alertDoc$signal6$rul.language) !== null && _ref6 !== void 0 ? _ref6 : 'kuery'; const query = (_ref7 = (_params$query2 = params.query) !== null && _params$query2 !== void 0 ? _params$query2 : (_alertDoc$signal7 = alertDoc.signal) === null || _alertDoc$signal7 === void 0 ? void 0 : (_alertDoc$signal7$rul = _alertDoc$signal7.rule) === null || _alertDoc$signal7$rul === void 0 ? void 0 : _alertDoc$signal7$rul.query) !== null && _ref7 !== void 0 ? _ref7 : ''; const indexNames = (_ref8 = (_getField4 = (0, _helpers2.getField)(alertDoc, _field_names.ALERT_RULE_INDICES)) !== null && _getField4 !== void 0 ? _getField4 : (_alertDoc$signal8 = alertDoc.signal) === null || _alertDoc$signal8 === void 0 ? void 0 : (_alertDoc$signal8$rul = _alertDoc$signal8.rule) === null || _alertDoc$signal8$rul === void 0 ? void 0 : _alertDoc$signal8$rul.index) !== null && _ref8 !== void 0 ? _ref8 : []; const { from, to, dataProviders } = getNewTermsData(alertDoc); const filter = await getExceptionFilter(ecsData); const allFilters = ((_templateValues$filte2 = templateValues.filters) !== null && _templateValues$filte2 !== void 0 ? _templateValues$filte2 : augmentedFilters).concat(!filter ? [] : [filter]); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, columns: (_templateValues$colum2 = templateValues.columns) !== null && _templateValues$colum2 !== void 0 ? _templateValues$colum2 : _defaults.timelineDefaults.columns, description: `_id: ${alertDoc._id}`, filters: allFilters, dataProviders: (_templateValues$dataP2 = templateValues.dataProviders) !== null && _templateValues$dataP2 !== void 0 ? _templateValues$dataP2 : dataProviders, id: _timeline.TimelineId.active, indexNames, dateRange: { start: from, end: to }, eventType: 'all', kqlQuery: { filterQuery: { kuery: { kind: language, expression: (_templateValues$query3 = templateValues.query) !== null && _templateValues$query3 !== void 0 ? _templateValues$query3 : query }, serializedQuery: (_templateValues$query4 = templateValues.query) !== null && _templateValues$query4 !== void 0 ? _templateValues$query4 : query } } }, to, ruleNote: noteContent }); } catch (error) { const { toasts } = _kibana.KibanaServices.get().notifications; toasts.addError(error, { toastMessage: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createNewTermsTimelineFailure', { defaultMessage: 'Failed to create timeline for document _id: {id}', values: { id: ecsData._id } }), title: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createNewTermsTimelineFailureTitle', { defaultMessage: 'Failed to create new terms alert timeline' }) }); const from = _default_date_settings.DEFAULT_FROM_MOMENT.toISOString(); const to = _default_date_settings.DEFAULT_TO_MOMENT.toISOString(); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all' }, to }); } }; const getSuppressedAlertData = ecsData => { const normalizedEcsData = Array.isArray(ecsData) ? ecsData[0] : ecsData; const from = (0, _helpers2.getField)(normalizedEcsData, _ruleDataUtils.ALERT_SUPPRESSION_START); const to = (0, _helpers2.getField)(normalizedEcsData, _ruleDataUtils.ALERT_SUPPRESSION_END); const terms = (0, _helpers2.getField)(normalizedEcsData, _ruleDataUtils.ALERT_SUPPRESSION_TERMS); const dataProviderPartials = terms.map(term => { const fieldId = term.field.replace('.', '-'); const id = `send-alert-to-timeline-action-default-draggable-event-details-value-formatted-field-value-${_timeline.TimelineId.active}-${fieldId}-${term.value}`; return term.value == null ? { id, name: fieldId, enabled: true, excluded: true, kqlQuery: '', queryMatch: { field: term.field, value: '', operator: ':*' } } : { id, name: fieldId, enabled: true, excluded: false, kqlQuery: '', queryMatch: { field: term.field, value: term.value, operator: ':' } }; }); const dataProvider = { ...dataProviderPartials[0], and: dataProviderPartials.slice(1) }; return { from, to, dataProviders: [dataProvider] }; }; const createSuppressedTimeline = async (ecsData, createTimeline, noteContent, templateValues, getExceptionFilter) => { try { var _alertResponse$hits$h3, _ref9, _filters3, _alertDoc$signal9, _alertDoc$signal9$rul, _ref10, _params$language3, _alertDoc$signal10, _alertDoc$signal10$ru, _ref11, _params$query3, _alertDoc$signal11, _alertDoc$signal11$ru, _ref12, _getField5, _alertDoc$signal12, _alertDoc$signal12$ru, _templateValues$filte3, _templateValues$colum3, _templateValues$dataP3, _templateValues$query5, _templateValues$query6; const alertResponse = await _kibana.KibanaServices.get().http.fetch(_constants.DETECTION_ENGINE_QUERY_SIGNALS_URL, { method: 'POST', body: JSON.stringify((0, _alerts.buildAlertsQuery)([ecsData._id])) }); const formattedAlertData = (_alertResponse$hits$h3 = alertResponse === null || alertResponse === void 0 ? void 0 : alertResponse.hits.hits.reduce((acc, { _id, _index, _source = {} }) => { return [...acc, { ...(0, _alerts.formatAlertToEcsSignal)(_source), _id, _index, timestamp: _source['@timestamp'] }]; }, [])) !== null && _alertResponse$hits$h3 !== void 0 ? _alertResponse$hits$h3 : []; const alertDoc = formattedAlertData[0]; const params = (0, _helpers2.getField)(alertDoc, _ruleDataUtils.ALERT_RULE_PARAMETERS); const filters = (_ref9 = (_filters3 = params.filters) !== null && _filters3 !== void 0 ? _filters3 : (_alertDoc$signal9 = alertDoc.signal) === null || _alertDoc$signal9 === void 0 ? void 0 : (_alertDoc$signal9$rul = _alertDoc$signal9.rule) === null || _alertDoc$signal9$rul === void 0 ? void 0 : _alertDoc$signal9$rul.filters) !== null && _ref9 !== void 0 ? _ref9 : []; // https://github.com/elastic/kibana/issues/126574 - if the provided filter has no `meta` field // we expect an empty object to be inserted before calling `createTimeline` const augmentedFilters = filters.map(filter => { return filter.meta != null ? filter : { ...filter, meta: {} }; }); const language = (_ref10 = (_params$language3 = params.language) !== null && _params$language3 !== void 0 ? _params$language3 : (_alertDoc$signal10 = alertDoc.signal) === null || _alertDoc$signal10 === void 0 ? void 0 : (_alertDoc$signal10$ru = _alertDoc$signal10.rule) === null || _alertDoc$signal10$ru === void 0 ? void 0 : _alertDoc$signal10$ru.language) !== null && _ref10 !== void 0 ? _ref10 : 'kuery'; const query = (_ref11 = (_params$query3 = params.query) !== null && _params$query3 !== void 0 ? _params$query3 : (_alertDoc$signal11 = alertDoc.signal) === null || _alertDoc$signal11 === void 0 ? void 0 : (_alertDoc$signal11$ru = _alertDoc$signal11.rule) === null || _alertDoc$signal11$ru === void 0 ? void 0 : _alertDoc$signal11$ru.query) !== null && _ref11 !== void 0 ? _ref11 : ''; const indexNames = (_ref12 = (_getField5 = (0, _helpers2.getField)(alertDoc, _field_names.ALERT_RULE_INDICES)) !== null && _getField5 !== void 0 ? _getField5 : (_alertDoc$signal12 = alertDoc.signal) === null || _alertDoc$signal12 === void 0 ? void 0 : (_alertDoc$signal12$ru = _alertDoc$signal12.rule) === null || _alertDoc$signal12$ru === void 0 ? void 0 : _alertDoc$signal12$ru.index) !== null && _ref12 !== void 0 ? _ref12 : []; const { from, to, dataProviders } = getSuppressedAlertData(alertDoc); const exceptionsFilter = await getExceptionFilter(ecsData); const allFilters = ((_templateValues$filte3 = templateValues.filters) !== null && _templateValues$filte3 !== void 0 ? _templateValues$filte3 : augmentedFilters).concat(!exceptionsFilter ? [] : [exceptionsFilter]); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, columns: (_templateValues$colum3 = templateValues.columns) !== null && _templateValues$colum3 !== void 0 ? _templateValues$colum3 : _defaults.timelineDefaults.columns, description: `_id: ${alertDoc._id}`, filters: allFilters, dataProviders: (_templateValues$dataP3 = templateValues.dataProviders) !== null && _templateValues$dataP3 !== void 0 ? _templateValues$dataP3 : dataProviders, id: _timeline.TimelineId.active, indexNames, dateRange: { start: from, end: to }, eventType: 'all', kqlQuery: { filterQuery: { kuery: { kind: language, expression: (_templateValues$query5 = templateValues.query) !== null && _templateValues$query5 !== void 0 ? _templateValues$query5 : query }, serializedQuery: (_templateValues$query6 = templateValues.query) !== null && _templateValues$query6 !== void 0 ? _templateValues$query6 : query } } }, to, ruleNote: noteContent }); } catch (error) { const { toasts } = _kibana.KibanaServices.get().notifications; toasts.addError(error, { toastMessage: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createSuppressedTimelineFailure', { defaultMessage: 'Failed to create timeline for document _id: {id}', values: { id: ecsData._id } }), title: _i18n.i18n.translate('xpack.securitySolution.detectionEngine.alerts.createSuppressedTimelineFailureTitle', { defaultMessage: 'Failed to create suppressed alert timeline' }) }); const from = _default_date_settings.DEFAULT_FROM_MOMENT.toISOString(); const to = _default_date_settings.DEFAULT_TO_MOMENT.toISOString(); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all' }, to }); } }; const sendBulkEventsToTimelineAction = (createTimeline, ecs, prefer = 'dataProvider', label) => { const eventIds = Array.isArray(ecs) ? ecs.map(d => d._id) : []; const { to, from } = determineToAndFrom({ ecs }); const { dataProviders, filters } = buildTimelineDataProviderOrFilter(eventIds, prefer, label || `${ecs.length} event IDs`); createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, dataProviders, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all', filters, kqlQuery: { filterQuery: { kuery: { kind: 'kuery', expression: '' }, serializedQuery: '' } } }, to }); }; exports.sendBulkEventsToTimelineAction = sendBulkEventsToTimelineAction; const sendAlertToTimelineAction = async ({ createTimeline, ecsData: ecs, updateTimelineIsLoading, searchStrategyClient, getExceptionFilter }) => { /* FUTURE DEVELOPER * We are making an assumption here that if you have an array of ecs data they are all coming from the same rule * but we still want to determine the filter for each alerts * * New Update: Wherever we need to add multiple alerts/events to the timeline, new function `sendBulkEventsToTimelineAction` * should be invoked */ const ecsData = Array.isArray(ecs) ? ecs[0] : ecs; const ruleNote = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_NOTE); const ruleAuthor = (0, _helpers2.getField)(ecsData, _ruleDataUtils.ALERT_RULE_CREATED_BY); const noteContent = Array.isArray(ruleNote) && ruleNote.length > 0 ? ruleNote[0] : ''; const ruleTimelineId = (0, _helpers2.getField)(ecsData, _field_names.ALERT_RULE_TIMELINE_ID); const timelineId = !(0, _fp.isEmpty)(ruleTimelineId) ? Array.isArray(ruleTimelineId) ? ruleTimelineId[0] : ruleTimelineId : ''; const { to, from } = determineToAndFrom({ ecs }); // For now we do not want to populate the template timeline if we have alertIds if (!(0, _fp.isEmpty)(timelineId)) { try { var _ecsData$_index, _eventDataResp$data; updateTimelineIsLoading({ id: _timeline.TimelineId.active, isLoading: true }); const [responseTimeline, eventDataResp] = await Promise.all([(0, _api.getTimelineTemplate)(timelineId), (0, _rxjs.lastValueFrom)(searchStrategyClient.search({ defaultIndex: [], indexName: (_ecsData$_index = ecsData._index) !== null && _ecsData$_index !== void 0 ? _ecsData$_index : '', eventId: ecsData._id, factoryQueryType: _timeline3.TimelineEventsQueries.details }, { strategy: 'timelineSearchStrategy' }))]); const resultingTimeline = (0, _fp.getOr)({}, 'data.getOneTimeline', responseTimeline); const eventData = (_eventDataResp$data = eventDataResp.data) !== null && _eventDataResp$data !== void 0 ? _eventDataResp$data : []; if (!(0, _fp.isEmpty)(resultingTimeline)) { var _timelineTemplate$tim, _timeline$kqlQuery$fi, _timeline$kqlQuery, _timeline$kqlQuery$fi2, _timeline$kqlQuery$fi3, _timeline$filters, _timeline$dataProvide; const timelineTemplate = (0, _helpers.omitTypenameInTimeline)(resultingTimeline); const { timeline, notes } = (0, _helpers.formatTimelineResultToModel)(timelineTemplate, true, (_timelineTemplate$tim = timelineTemplate.timelineType) !== null && _timelineTemplate$tim !== void 0 ? _timelineTemplate$tim : _timeline2.TimelineType.default); const query = (0, _helpers3.replaceTemplateFieldFromQuery)((_timeline$kqlQuery$fi = (_timeline$kqlQuery = timeline.kqlQuery) === null || _timeline$kqlQuery === void 0 ? void 0 : (_timeline$kqlQuery$fi2 = _timeline$kqlQuery.filterQuery) === null || _timeline$kqlQuery$fi2 === void 0 ? void 0 : (_timeline$kqlQuery$fi3 = _timeline$kqlQuery$fi2.kuery) === null || _timeline$kqlQuery$fi3 === void 0 ? void 0 : _timeline$kqlQuery$fi3.expression) !== null && _timeline$kqlQuery$fi !== void 0 ? _timeline$kqlQuery$fi : '', eventData, timeline.timelineType); const filters = (0, _helpers3.replaceTemplateFieldFromMatchFilters)((_timeline$filters = timeline.filters) !== null && _timeline$filters !== void 0 ? _timeline$filters : [], eventData); const dataProviders = (0, _helpers3.replaceTemplateFieldFromDataProviders)((_timeline$dataProvide = timeline.dataProviders) !== null && _timeline$dataProvide !== void 0 ? _timeline$dataProvide : [], eventData, timeline.timelineType); // threshold with template if (isThresholdAlert(ecsData)) { return createThresholdTimeline(ecsData, createTimeline, noteContent, { filters, query, dataProviders, columns: timeline.columns }, getExceptionFilter); } else if (isNewTermsAlert(ecsData)) { return createNewTermsTimeline(ecsData, createTimeline, noteContent, { filters, query, dataProviders, columns: timeline.columns }, getExceptionFilter); } else if (isSuppressedAlert(ecsData)) { return createSuppressedTimeline(ecsData, createTimeline, noteContent, { filters, query, dataProviders, columns: timeline.columns }, getExceptionFilter); } else { var _timeline$kqlQuery$fi4, _timeline$kqlQuery2, _timeline$kqlQuery2$f, _timeline$kqlQuery2$f2, _notes$map; return createTimeline({ from, timeline: { ...timeline, title: '', timelineType: _timeline2.TimelineType.default, templateTimelineId: null, status: _timeline2.TimelineStatus.draft, dataProviders, eventType: 'all', filters, dateRange: { start: from, end: to }, kqlQuery: { filterQuery: { kuery: { kind: (_timeline$kqlQuery$fi4 = (_timeline$kqlQuery2 = timeline.kqlQuery) === null || _timeline$kqlQuery2 === void 0 ? void 0 : (_timeline$kqlQuery2$f = _timeline$kqlQuery2.filterQuery) === null || _timeline$kqlQuery2$f === void 0 ? void 0 : (_timeline$kqlQuery2$f2 = _timeline$kqlQuery2$f.kuery) === null || _timeline$kqlQuery2$f2 === void 0 ? void 0 : _timeline$kqlQuery2$f2.kind) !== null && _timeline$kqlQuery$fi4 !== void 0 ? _timeline$kqlQuery$fi4 : 'kuery', expression: query }, serializedQuery: (0, _kuery.convertKueryToElasticSearchQuery)(query) } }, noteIds: (_notes$map = notes === null || notes === void 0 ? void 0 : notes.map(n => n.noteId)) !== null && _notes$map !== void 0 ? _notes$map : [], show: true }, to, ruleNote: noteContent, ruleAuthor, notes: notes !== null && notes !== void 0 ? notes : null }); } } } catch { updateTimelineIsLoading({ id: _timeline.TimelineId.active, isLoading: false }); return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all' }, to }); } } else if (isThresholdAlert(ecsData)) { return createThresholdTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); } else if (isNewTermsAlert(ecsData)) { return createNewTermsTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); } else if (isSuppressedAlert(ecsData)) { return createSuppressedTimeline(ecsData, createTimeline, noteContent, {}, getExceptionFilter); } else { let { dataProviders, filters } = buildTimelineDataProviderOrFilter([ecsData._id], 'dataProvider'); if (isEqlAlertWithGroupId(ecsData)) { const tempEql = buildEqlDataProviderOrFilter([ecsData._id], ecs); dataProviders = tempEql.dataProviders; filters = tempEql.filters; } return createTimeline({ from, notes: null, timeline: { ..._defaults.timelineDefaults, dataProviders, id: _timeline.TimelineId.active, indexNames: [], dateRange: { start: from, end: to }, eventType: 'all', filters, kqlQuery: { filterQuery: { kuery: { kind: 'kuery', expression: '' }, serializedQuery: '' } } }, to, ruleNote: noteContent, ruleAuthor }); } }; exports.sendAlertToTimelineAction = sendAlertToTimelineAction;