diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx index eb6e965975171..8e07910c1c071 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/summary_view.tsx @@ -25,6 +25,9 @@ import { ALERTS_HEADERS_RISK_SCORE, ALERTS_HEADERS_RULE, ALERTS_HEADERS_SEVERITY, + ALERTS_HEADERS_THRESHOLD_COUNT, + ALERTS_HEADERS_THRESHOLD_TERMS, + ALERTS_HEADERS_THRESHOLD_CARDINALITY, } from '../../../detections/components/alerts_table/translations'; import { IP_FIELD_TYPE, @@ -61,6 +64,9 @@ const fields = [ { id: 'user.name' }, { id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, { id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, + { id: 'signal.threshold_result.count', label: ALERTS_HEADERS_THRESHOLD_COUNT }, + { id: 'signal.threshold_result.terms', label: ALERTS_HEADERS_THRESHOLD_TERMS }, + { id: 'signal.threshold_result.cardinality', label: ALERTS_HEADERS_THRESHOLD_CARDINALITY }, ]; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -135,6 +141,45 @@ const getSummary = ({ linkValue: linkValue ?? undefined, }; + if (item.id === 'signal.threshold_result.terms') { + try { + const terms = getOr(null, 'originalValue', field); + const parsedValue = terms.map((term: string) => JSON.parse(term)); + const thresholdTerms = (parsedValue ?? []).map( + (entry: { field: string; value: string }) => { + return { + title: `${entry.field} [threshold]`, + description: { + ...description, + value: entry.value, + }, + }; + } + ); + return [...acc, ...thresholdTerms]; + } catch (err) { + return acc; + } + } + + if (item.id === 'signal.threshold_result.cardinality') { + try { + const parsedValue = JSON.parse(value); + return [ + ...acc, + { + title: ALERTS_HEADERS_THRESHOLD_CARDINALITY, + description: { + ...description, + value: `count(${parsedValue.field}) == ${parsedValue.value}`, + }, + }, + ]; + } catch (err) { + return acc; + } + } + return [ ...acc, { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts index 8ecf06616cec2..1829b3822e6a4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/translations.ts @@ -88,6 +88,27 @@ export const ALERTS_HEADERS_RISK_SCORE = i18n.translate( } ); +export const ALERTS_HEADERS_THRESHOLD_COUNT = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdCount', + { + defaultMessage: 'Threshold Count', + } +); + +export const ALERTS_HEADERS_THRESHOLD_TERMS = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdTerms', + { + defaultMessage: 'Threshold Terms', + } +); + +export const ALERTS_HEADERS_THRESHOLD_CARDINALITY = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.thresholdCardinality', + { + defaultMessage: 'Threshold Cardinality', + } +); + export const ACTION_OPEN_ALERT = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.actions.openAlertTitle', { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts index 3726f66cb0f82..c0fdc4eb0189d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.test.ts @@ -79,6 +79,7 @@ describe('transformThresholdNormalizedResultsToEcs', () => { _id, _index: 'test', _source: { + 'source.ip': '127.0.0.1', '@timestamp': '2020-04-20T21:27:45+0000', threshold_result: { from: new Date('2020-12-17T16:27:00.000Z'), @@ -256,6 +257,8 @@ describe('transformThresholdNormalizedResultsToEcs', () => { _index: 'test', _source: { '@timestamp': '2020-04-20T21:27:45+0000', + 'host.name': 'garden-gnomes', + 'source.ip': '127.0.0.1', threshold_result: { from: new Date('2020-12-17T16:28:00.000Z'), // from threshold signal history terms: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index c226b63a0b9ac..43158d1e89783 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -163,6 +163,15 @@ const getTransformedHits = ( const source = { '@timestamp': timestamp, + ...bucket.terms.reduce((termAcc, term) => { + if (!term.field.startsWith('signal.')) { + return { + ...termAcc, + [term.field]: term.value, + }; + } + return termAcc; + }, {}), threshold_result: { terms: bucket.terms, cardinality: bucket.cardinality,