Skip to content

Commit 325a4a0

Browse files
committed
[Security Solution][CTI] Investigation time enrichment UI (#103383)
* Add pure fn and consuming hook to fetch event enrichment It's not being invoked yet, but I've added a placeholder where it's going. * Move existing enrichment tests to new spec file This is a rough copy/paste, I'll clean up as I flesh out the new tests. * Move test constants into tests that use them * style: declare FC function as an FC * Extract some inline parsing logic into a helper function And test it! * Solidifying enrichment types on the backend * Declares an enum for our types * Sets type during indicator match rule enrichment * Sets type during investigation-time enrichment * WIP: Enrichment rows are rendered on the alerts summary There are lots of TODOs here, but this implements the following: * Fetching investigation-time enrichments from the backend * Parsing existing enrichments from timeline data * Merging the two enrichment types together, and rendering them in rows as specified Much of the data-fetching is hardcoded, and this broke the existing pattern with SummaryView/SummaryRow so that got a little messy; I may end up just using my own EuiTable but we'll see. Threat Intel tab is currently broken; that's up next. * Updates ThreatDetailsView to accept an array of enrichments The investigation-time enrichments are a little messy because they contain all the non-ECS fields that indicators contain; other than that, this is looking good. Still need to add the new header, and potentially sort the fields. * Sort our details fields This promotes sanity for the user. * Add "view threat intel data" button This simply opens the threat intel tab. * Implement header for threat details sections * Add a basic jest "unit" test around ThreatSummaryView * Fix remaining tests for components we modified This also addresses a bug where we were not properly sorting new enrichments by first_seen; this is covered under the tests that were fixed. * Filter out duplicate investigation-time enrichments Because the enrichment endpoint is dumb and doesn't know about the existing event or its enrichments, we need to merge these together on the client to reduce noise and redundant data. * Add inspect button to investigation enrichments * Massages the response into the format that the inspect component uses * Moves stateful fetching of query and persisting in redux to new, more specialized hook * Moves existing enrichment hook to a more suitable location in containers/ * Fix failing unit tests * indicator match rule now specifies `matched.type` as coming from the rule * Inspecting the enrichment query requires use of the redux store, which was not previously mocked * Fix existing CTI cypress tests This covers the basics of the Alert Summary and Threat Intel tabs; the investigation-time enrichment functionality is up next. * Adds a cypress test exercising investigation time enrichment * Loads more indicators (filebeat data, `threat_indicator2` archive) AFTER the rule has executed * Asserts that those indicators are also found on the alert summary. * Populate event enrichment call with actual alert fields This was previously hardcoded during development. * Add a new field to our suspicious event to trigger enrichment The existing myhash field will generate an alert due to the way the rule is written, but the alert had no other fields that would match the investigation time enrichment. This gives it a source.ip, and updates the indicator to match. * Only fetch enrichments data if there are valid event fields If none of the alert's fields would be relevant to the enrichment query, then we don't make the request at all. * Update enrichments matched.typed in integration tests This field was updated to reflect the source of the match, in this case: indicator match rules. * Ensure draggable fields are unique in a multi-match scenario If a given field matched multiple indicators, then the previous contextId was not unique as it was based on field/value that matched. Adding provider to the mix would fix it, except that we're not guaranteed to have a provider. I've added both provider (if present) and an index value to the key to ensure that it's unique. * Simplify types This field can never be null, as we always set it in our response. * Move helper functioons out of shared location and into consuming component These are unlikely to be used elsewhere. * Clean up data parsing logic using reduce This obviates the need for our filter/guard function and the extra loop that it entails. We have to specify the return value of our reduce fn, however, but that's mostly equivalent to our type guard. * Move our general function into a general location * Extract the concept of "enrichment identifiers" This was already partially codified with 'buildEnrichmentId,' which is used to dedup enrichments; this extends the idea to all fields that could uniquely identify a given indicator. * Use existing constant as the source of our enrichments query This is now used by both the overview card and the enrichment query. * Codify our default enrichment lookback as constants * Remove unnecessary flexbox The generic SummaryView component previously had to deal with multi-valued CTI fields, representing the multiple values coming from the multiple nested objects with that field. However, with the new UI we no longer have that constraint, and so the default columnar style, and the corresponding overriding styles, are no longer necessary. * Filter out partial responses in the event enrichment observable The UI does not currently handle these. We need to test the behavior of long-running queries with this filter, but this should simplify the behavior to complete/error until we handle partial responses. * Display placeholders while event enrichment is loading Displays a loading spinner in the Threat Intel tab title, and some loading lines where the enrichments summary is. * Update our indicator data to be within the last 30 days This fixes our cypress test, but it's going to start failing again in 30 days. However, by that time I'll have implemented the absolute data picker, which will allow for a more comprehensive test in addition to us sidestepping this issue. * Fix type error with our details tabs The name prop on a Tab will be rendered as a node, so both strings and elements are acceptable. This relaxes the types to inherit from the component itself. * Fix failing jest tests The addition of our filtering of the search observable broke this test, since we now need to implement the search observable. Rather than do that, we'll instead mock our local hook as that's more likely to change. # Conflicts: # x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx
1 parent 1a8a226 commit 325a4a0

40 files changed

Lines changed: 2663 additions & 651 deletions

File tree

x-pack/plugins/security_solution/common/cti/constants.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { INDICATOR_DESTINATION_PATH } from '../constants';
99

1010
export const MATCHED_ATOMIC = 'matched.atomic';
1111
export const MATCHED_FIELD = 'matched.field';
12+
export const MATCHED_ID = 'matched.id';
1213
export const MATCHED_TYPE = 'matched.type';
1314
export const INDICATOR_MATCH_SUBFIELDS = [MATCHED_ATOMIC, MATCHED_FIELD, MATCHED_TYPE];
1415

@@ -18,11 +19,12 @@ export const INDICATOR_MATCHED_TYPE = `${INDICATOR_DESTINATION_PATH}.${MATCHED_T
1819

1920
export const EVENT_DATASET = 'event.dataset';
2021
export const EVENT_REFERENCE = 'event.reference';
22+
export const EVENT_URL = 'event.url';
2123
export const PROVIDER = 'provider';
2224
export const FIRSTSEEN = 'first_seen';
2325

2426
export const INDICATOR_DATASET = `${INDICATOR_DESTINATION_PATH}.${EVENT_DATASET}`;
25-
export const INDICATOR_EVENT_URL = `${INDICATOR_DESTINATION_PATH}.event.url`;
27+
export const INDICATOR_EVENT_URL = `${INDICATOR_DESTINATION_PATH}.${EVENT_URL}`;
2628
export const INDICATOR_FIRSTSEEN = `${INDICATOR_DESTINATION_PATH}.${FIRSTSEEN}`;
2729
export const INDICATOR_LASTSEEN = `${INDICATOR_DESTINATION_PATH}.last_seen`;
2830
export const INDICATOR_PROVIDER = `${INDICATOR_DESTINATION_PATH}.${PROVIDER}`;
@@ -37,13 +39,10 @@ export const CTI_ROW_RENDERER_FIELDS = [
3739
INDICATOR_PROVIDER,
3840
];
3941

40-
export const SORTED_THREAT_SUMMARY_FIELDS = [
41-
INDICATOR_MATCHED_FIELD,
42-
INDICATOR_MATCHED_TYPE,
43-
INDICATOR_PROVIDER,
44-
INDICATOR_FIRSTSEEN,
45-
INDICATOR_LASTSEEN,
46-
];
42+
export enum ENRICHMENT_TYPES {
43+
InvestigationTime = 'investigation_time',
44+
IndicatorMatchRule = 'indicator_match_rule',
45+
}
4746

4847
export const EVENT_ENRICHMENT_INDICATOR_FIELD_MAP = {
4948
'file.hash.md5': 'threatintel.indicator.file.hash.md5',
@@ -58,6 +57,9 @@ export const EVENT_ENRICHMENT_INDICATOR_FIELD_MAP = {
5857
'registry.path': 'threatintel.indicator.registry.path',
5958
};
6059

60+
export const DEFAULT_EVENT_ENRICHMENT_FROM = 'now-30d';
61+
export const DEFAULT_EVENT_ENRICHMENT_TO = 'now';
62+
6163
export const CTI_DEFAULT_SOURCES = [
6264
'Abuse URL',
6365
'Abuse Malware',

x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.mock.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { IEsSearchResponse } from 'src/plugins/data/public';
99

1010
import {
11+
CtiEnrichment,
1112
CtiEventEnrichmentRequestOptions,
1213
CtiEventEnrichmentStrategyResponse,
1314
CtiQueries,
@@ -99,11 +100,63 @@ export const buildEventEnrichmentRawResponseMock = (): IEsSearchResponse => ({
99100
},
100101
});
101102

103+
export const buildEventEnrichmentMock = (
104+
overrides: Partial<CtiEnrichment> = {}
105+
): CtiEnrichment => ({
106+
'@timestamp': ['2021-05-28T18:33:52.993Z'],
107+
'agent.ephemeral_id': ['d6b14f65-5bf3-430d-8315-7b5613685979'],
108+
'agent.hostname': ['rylastic.local'],
109+
'agent.id': ['ff93aee5-86a1-4a61-b0e6-0cdc313d01b5'],
110+
'agent.name': ['rylastic.local'],
111+
'agent.type': ['filebeat'],
112+
'agent.version': ['8.0.0'],
113+
'ecs.version': ['1.6.0'],
114+
'event.category': ['threat'],
115+
'event.created': ['2021-05-28T18:33:52.993Z'],
116+
'event.dataset': ['threatintel.abusemalware'],
117+
'event.ingested': ['2021-05-28T18:33:55.086Z'],
118+
'event.kind': ['enrichment'],
119+
'event.module': ['threatintel'],
120+
'event.reference': [
121+
'https://urlhaus-api.abuse.ch/v1/download/15b012e6f626d0f88c2926d2bf4ca394d7b8ee07cc06d2ec05ea76bed3e8a05e/',
122+
],
123+
'event.type': ['indicator'],
124+
'fileset.name': ['abusemalware'],
125+
'input.type': ['httpjson'],
126+
'matched.atomic': ['5529de7b60601aeb36f57824ed0e1ae8'],
127+
'matched.field': ['file.hash.md5'],
128+
'matched.id': ['31408415b6d5601a92d29b86c2519658f210c194057588ae396d55cc20b3f03d'],
129+
'matched.index': ['filebeat-8.0.0-2021.05.28-000001'],
130+
'matched.type': ['investigation_time'],
131+
'related.hash': [
132+
'5529de7b60601aeb36f57824ed0e1ae8',
133+
'15b012e6f626d0f88c2926d2bf4ca394d7b8ee07cc06d2ec05ea76bed3e8a05e',
134+
'768:NXSFGJ/ooP6FawrB7Bo1MWnF/jRmhJImp:1SFXIqBo1Mwj2p',
135+
],
136+
'service.type': ['threatintel'],
137+
tags: ['threatintel-abusemalware', 'forwarded'],
138+
'threatintel.indicator.file.hash.md5': ['5529de7b60601aeb36f57824ed0e1ae8'],
139+
'threatintel.indicator.file.hash.sha256': [
140+
'15b012e6f626d0f88c2926d2bf4ca394d7b8ee07cc06d2ec05ea76bed3e8a05e',
141+
],
142+
'threatintel.indicator.file.hash.ssdeep': [
143+
'768:NXSFGJ/ooP6FawrB7Bo1MWnF/jRmhJImp:1SFXIqBo1Mwj2p',
144+
],
145+
'threatintel.indicator.file.hash.tlsh': [
146+
'FFB20B82F6617061C32784E2712F7A46B179B04FD1EA54A0F28CD8E9CFE4CAA1617F1C',
147+
],
148+
'threatintel.indicator.file.size': [24738],
149+
'threatintel.indicator.file.type': ['html'],
150+
'threatintel.indicator.first_seen': ['2021-05-28T18:33:29.000Z'],
151+
'threatintel.indicator.type': ['file'],
152+
...overrides,
153+
});
154+
102155
export const buildEventEnrichmentResponseMock = (
103156
overrides: Partial<CtiEventEnrichmentStrategyResponse> = {}
104157
): CtiEventEnrichmentStrategyResponse => ({
105158
...buildEventEnrichmentRawResponseMock(),
106-
enrichments: [],
159+
enrichments: [buildEventEnrichmentMock()],
107160
inspect: { dsl: ['{"mocked": "json"}'] },
108161
totalCount: 0,
109162
...overrides,

x-pack/plugins/security_solution/common/search_strategy/security_solution/cti/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { IEsSearchResponse } from 'src/plugins/data/public';
9+
import { EVENT_ENRICHMENT_INDICATOR_FIELD_MAP } from '../../../cti/constants';
910
import { Inspect } from '../../common';
1011
import { RequestBasicOptions } from '..';
1112

@@ -18,9 +19,24 @@ export interface CtiEventEnrichmentRequestOptions extends RequestBasicOptions {
1819
}
1920

2021
export type CtiEnrichment = Record<string, unknown[]>;
22+
export type EventFields = Record<string, unknown>;
23+
24+
export interface CtiEnrichmentIdentifiers {
25+
id: string | undefined;
26+
field: string | undefined;
27+
value: string | undefined;
28+
type: string | undefined;
29+
provider: string | undefined;
30+
}
2131

2232
export interface CtiEventEnrichmentStrategyResponse extends IEsSearchResponse {
2333
enrichments: CtiEnrichment[];
24-
inspect?: Inspect;
34+
inspect: Inspect;
2535
totalCount: number;
2636
}
37+
38+
export type EventField = keyof typeof EVENT_ENRICHMENT_INDICATOR_FIELD_MAP;
39+
export const validEventFields = Object.keys(EVENT_ENRICHMENT_INDICATOR_FIELD_MAP) as EventField[];
40+
41+
export const isValidEventField = (field: string): field is EventField =>
42+
validEventFields.includes(field as EventField);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { getFirstElement } from './data_retrieval';
9+
10+
describe('getFirstElement', () => {
11+
it('returns undefined if array is undefined', () => {
12+
expect(getFirstElement(undefined)).toEqual(undefined);
13+
});
14+
15+
it('returns undefined if array is empty', () => {
16+
expect(getFirstElement([])).toEqual(undefined);
17+
});
18+
19+
it('returns the first element if present', () => {
20+
expect(getFirstElement(['hi mom'])).toEqual('hi mom');
21+
});
22+
23+
it('returns the first element of multiple', () => {
24+
expect(getFirstElement(['hi mom', 'hello world'])).toEqual('hi mom');
25+
});
26+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
/**
9+
* Retrieves the first element of the given array.
10+
*
11+
* @param array the array to retrieve a value from
12+
* @returns the first element of the array, or undefined if the array is undefined
13+
*/
14+
export const getFirstElement: <T = unknown>(array: T[] | undefined) => T | undefined = (array) =>
15+
array ? array[0] : undefined;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { newThreatIndicatorRule } from '../../objects/rule';
9+
import { cleanKibana, reload } from '../../tasks/common';
10+
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
11+
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
12+
import {
13+
JSON_LINES,
14+
TABLE_CELL,
15+
TABLE_ROWS,
16+
THREAT_CONTENT,
17+
THREAT_DETAILS_VIEW,
18+
THREAT_INTEL_TAB,
19+
THREAT_SUMMARY_VIEW,
20+
TITLE,
21+
} from '../../screens/alerts_details';
22+
import { TIMELINE_FIELD } from '../../screens/rule_details';
23+
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
24+
import { expandFirstAlert, goToManageAlertsDetectionRules } from '../../tasks/alerts';
25+
import { createCustomIndicatorRule } from '../../tasks/api_calls/rules';
26+
import {
27+
openJsonView,
28+
openThreatIndicatorDetails,
29+
scrollJsonViewToBottom,
30+
} from '../../tasks/alerts_details';
31+
32+
import { ALERTS_URL } from '../../urls/navigation';
33+
import { addsFieldsToTimeline } from '../../tasks/rule_details';
34+
35+
describe('CTI Enrichment', () => {
36+
before(() => {
37+
cleanKibana();
38+
esArchiverLoad('threat_indicator');
39+
esArchiverLoad('suspicious_source_event');
40+
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
41+
goToManageAlertsDetectionRules();
42+
createCustomIndicatorRule(newThreatIndicatorRule);
43+
reload();
44+
});
45+
46+
after(() => {
47+
esArchiverUnload('threat_indicator');
48+
esArchiverUnload('suspicious_source_event');
49+
});
50+
51+
beforeEach(() => {
52+
loginAndWaitForPageWithoutDateRange(ALERTS_URL);
53+
goToManageAlertsDetectionRules();
54+
goToRuleDetails();
55+
});
56+
57+
it('Displays enrichment matched.* fields on the timeline', () => {
58+
const expectedFields = {
59+
'threat.indicator.matched.atomic': newThreatIndicatorRule.atomic,
60+
'threat.indicator.matched.type': 'indicator_match_rule',
61+
'threat.indicator.matched.field': newThreatIndicatorRule.indicatorMappingField,
62+
};
63+
const fields = Object.keys(expectedFields) as Array<keyof typeof expectedFields>;
64+
65+
addsFieldsToTimeline('threat.indicator.matched', fields);
66+
67+
fields.forEach((field) => {
68+
cy.get(TIMELINE_FIELD(field)).should('have.text', expectedFields[field]);
69+
});
70+
});
71+
72+
it('Displays persisted enrichments on the JSON view', () => {
73+
const expectedEnrichment = [
74+
{ line: 4, text: ' "threat": {' },
75+
{
76+
line: 3,
77+
text:
78+
' "indicator": "{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\",\\"event\\":{\\"reference\\":\\"https://urlhaus-api.abuse.ch/v1/download/a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3/\\",\\"ingested\\":\\"2021-03-10T14:51:09.809069Z\\",\\"created\\":\\"2021-03-10T14:51:07.663Z\\",\\"kind\\":\\"enrichment\\",\\"module\\":\\"threatintel\\",\\"category\\":\\"threat\\",\\"type\\":\\"indicator\\",\\"dataset\\":\\"threatintel.abusemalware\\"},\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"filebeat-7.12.0-2021.03.10-000001\\",\\"type\\":\\"indicator_match_rule\\"}}"',
79+
},
80+
{ line: 2, text: ' }' },
81+
];
82+
83+
expandFirstAlert();
84+
openJsonView();
85+
scrollJsonViewToBottom();
86+
87+
cy.get(JSON_LINES).then((elements) => {
88+
const length = elements.length;
89+
expectedEnrichment.forEach((enrichment) => {
90+
cy.wrap(elements)
91+
.eq(length - enrichment.line)
92+
.should('have.text', enrichment.text);
93+
});
94+
});
95+
});
96+
97+
it('Displays threat indicator details on the threat intel tab', () => {
98+
const expectedThreatIndicatorData = [
99+
{ field: 'event.category', value: 'threat' },
100+
{ field: 'event.created', value: '2021-03-10T14:51:07.663Z' },
101+
{ field: 'event.dataset', value: 'threatintel.abusemalware' },
102+
{ field: 'event.ingested', value: '2021-03-10T14:51:09.809069Z' },
103+
{ field: 'event.kind', value: 'enrichment' },
104+
{ field: 'event.module', value: 'threatintel' },
105+
{
106+
field: 'event.reference',
107+
value:
108+
'https://urlhaus-api.abuse.ch/v1/download/a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3/(opens in a new tab or window)',
109+
},
110+
{ field: 'event.type', value: 'indicator' },
111+
{ field: 'file.hash.md5', value: '9b6c3518a91d23ed77504b5416bfb5b3' },
112+
{
113+
field: 'file.hash.sha256',
114+
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
115+
},
116+
{
117+
field: 'file.hash.ssdeep',
118+
value: '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL',
119+
},
120+
{
121+
field: 'file.hash.tlsh',
122+
value: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE',
123+
},
124+
{ field: 'file.size', value: '80280' },
125+
{ field: 'file.type', value: 'elf' },
126+
{ field: 'first_seen', value: '2021-03-10T08:02:14.000Z' },
127+
{
128+
field: 'matched.atomic',
129+
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
130+
},
131+
{ field: 'matched.field', value: 'myhash.mysha256' },
132+
{
133+
field: 'matched.id',
134+
value: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f',
135+
},
136+
{ field: 'matched.index', value: 'filebeat-7.12.0-2021.03.10-000001' },
137+
{ field: 'matched.type', value: 'indicator_match_rule' },
138+
{ field: 'type', value: 'file' },
139+
];
140+
141+
expandFirstAlert();
142+
openThreatIndicatorDetails();
143+
144+
cy.get(THREAT_INTEL_TAB).should('have.text', 'Threat Intel (1)');
145+
cy.get(THREAT_DETAILS_VIEW).within(() => {
146+
cy.get(TABLE_ROWS).should('have.length', expectedThreatIndicatorData.length);
147+
expectedThreatIndicatorData.forEach((row, index) => {
148+
cy.get(TABLE_ROWS)
149+
.eq(index)
150+
.within(() => {
151+
cy.get(TABLE_CELL).eq(0).should('have.text', row.field);
152+
cy.get(TABLE_CELL).eq(1).should('have.text', row.value);
153+
});
154+
});
155+
});
156+
});
157+
158+
describe('with additional indicators', () => {
159+
before(() => {
160+
esArchiverLoad('threat_indicator2');
161+
});
162+
163+
after(() => {
164+
esArchiverUnload('threat_indicator2');
165+
});
166+
167+
it('Displays matched fields from both indicator match rules and investigation time enrichments on Alerts Summary tab', () => {
168+
const indicatorMatchRuleEnrichment = {
169+
field: 'myhash.mysha256',
170+
value: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
171+
};
172+
const investigationTimeEnrichment = {
173+
field: 'source.ip',
174+
value: '192.168.1.1',
175+
};
176+
const expectedMatches = [indicatorMatchRuleEnrichment, investigationTimeEnrichment];
177+
178+
expandFirstAlert();
179+
180+
cy.get(THREAT_SUMMARY_VIEW).within(() => {
181+
cy.get(TABLE_ROWS).should('have.length', expectedMatches.length);
182+
expectedMatches.forEach((row, index) => {
183+
cy.get(TABLE_ROWS)
184+
.eq(index)
185+
.within(() => {
186+
cy.get(TITLE).should('have.text', row.field);
187+
cy.get(THREAT_CONTENT).should('have.text', row.value);
188+
});
189+
});
190+
});
191+
});
192+
});
193+
});

0 commit comments

Comments
 (0)