[Security Solution] Fixing exceptions flyout is not auto filled with all highlighted fields listed on Alert details page (#161673)

## Summary

- Addresses https://github.com/elastic/kibana/issues/161460
- Exclude `agent.id` in case the Alert's `agent.type` is not `endpoint`
- Handle Alert `event.category` array field ex. `[process]`
- For the `Threshold Rule` there are two additional fields the Alert
Summary populates the `Event Count` and the `Event Cardinality` which
can be ignored as they are not relevant to the Rule exception


![image](8972a647-6490-4d54-8af8-54f90d4ac438)


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Wafaa Nasr 2023-07-12 16:50:02 +01:00 committed by GitHub
parent ccb36d929a
commit 130e9deea5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 7 deletions

View file

@ -51,6 +51,7 @@ import {
ALERT_ORIGINAL_EVENT_KIND,
ALERT_ORIGINAL_EVENT_MODULE,
} from '../../../../common/field_maps/field_names';
import { AGENT_ID } from './highlighted_fields_config';
jest.mock('uuid', () => ({
v4: jest.fn().mockReturnValue('123'),
}));
@ -1528,6 +1529,7 @@ describe('Exception helpers', () => {
'event.category': 'malware',
'event.type': 'creation',
'event.dataset': 'endpoint',
'kibana.alert.rule.uuid': '123',
'kibana.alert.rule.exceptions_list': [
{
id: 'endpoint_list',
@ -1714,17 +1716,18 @@ describe('Exception helpers', () => {
describe('filterHighlightedFields', () => {
const prefixesToExclude = ['agent', 'cloud'];
it('should not filter any field if no prefixes passed ', () => {
const filteredFields = filterHighlightedFields(expectedHighlightedFields, []);
const filteredFields = filterHighlightedFields(expectedHighlightedFields, [], alertData);
expect(filteredFields).toEqual(expectedHighlightedFields);
});
it('should not filter any field if no fields passed ', () => {
const filteredFields = filterHighlightedFields([], prefixesToExclude);
const filteredFields = filterHighlightedFields([], prefixesToExclude, alertData);
expect(filteredFields).toEqual([]);
});
it('should filter out the passed prefixes successfully', () => {
const filteredFields = filterHighlightedFields(
expectedHighlightedFields,
prefixesToExclude
prefixesToExclude,
alertData
);
expect(filteredFields).not.toEqual(
expect.arrayContaining([
@ -1845,6 +1848,24 @@ describe('Exception helpers', () => {
},
]);
});
it('should return the process highlighted fields correctly when eventCategory is an array', () => {
const alertDataEventCategoryProcessArray = { ...alertData, 'event.category': ['process'] };
const res = getAlertHighlightedFields(alertDataEventCategoryProcessArray);
expect(res).not.toEqual(
expect.arrayContaining([
{ id: 'file.name' },
{ id: 'file.hash.sha256' },
{ id: 'file.directory' },
])
);
expect(res).toEqual(
expect.arrayContaining([
{ id: 'process.name' },
{ id: 'process.parent.name' },
{ id: 'process.args' },
])
);
});
it('should return all highlighted fields even when the "kibana.alert.rule.type" is not in the alertData', () => {
const alertDataWithoutEventCategory = { ...alertData, 'kibana.alert.rule.type': null };
const res = getAlertHighlightedFields(alertDataWithoutEventCategory);
@ -1856,6 +1877,22 @@ describe('Exception helpers', () => {
const res = getAlertHighlightedFields(alertData);
expect(res).toEqual(allHighlightFields);
});
it('should exclude the "agent.id" from highlighted fields when agent.type is not "endpoint"', () => {
jest.mock('./highlighted_fields_config', () => ({ highlightedFieldsPrefixToExclude: [] }));
const alertDataWithoutAgentType = { ...alertData, agent: { ...alertData.agent, type: '' } };
const res = getAlertHighlightedFields(alertDataWithoutAgentType);
expect(res).toEqual(allHighlightFields.filter((field) => field.id !== AGENT_ID));
});
it('should exclude the "agent.id" from highlighted fields when "kibana.alert.rule.uuid" is not part of the alertData', () => {
jest.mock('./highlighted_fields_config', () => ({ highlightedFieldsPrefixToExclude: [] }));
const alertDataWithoutRuleUUID = { ...alertData, 'kibana.alert.rule.uuid': '' };
const res = getAlertHighlightedFields(alertDataWithoutRuleUUID);
expect(res).toEqual(allHighlightFields.filter((field) => field.id !== AGENT_ID));
});
});
describe('getPrepopulatedRuleExceptionWithHighlightFields', () => {
it('should not create any exception and return null if there are no highlighted fields', () => {

View file

@ -59,6 +59,10 @@ import {
getKibanaAlertIdField,
highlightedFieldsPrefixToExclude,
KIBANA_ALERT_RULE_TYPE,
AGENT_ID,
AGENT_TYPE,
KIBANA_ALERT_RULE_UUID,
ENDPOINT_ALERT,
} from './highlighted_fields_config';
export const filterIndexPatterns = (
@ -958,13 +962,19 @@ export const getPrepopulatedRuleExceptionWithHighlightFields = ({
/**
Filters out the irrelevant highlighted fields for Rule exceptions using
the "highlightedFieldsPrefixToExclude" array.
1. The "highlightedFieldsPrefixToExclude" array
2. Agent.id field in case the alert was not generated from Endpoint
3. Threshold Rule
*/
export const filterHighlightedFields = (
fields: EventSummaryField[],
prefixesToExclude: string[]
prefixesToExclude: string[],
alertData: AlertData
): EventSummaryField[] => {
return fields.filter(({ id }) => {
// Exclude agent.id field only if the agent type was not Endpoint
if (id === AGENT_ID) return isAlertFromEndpointEvent(alertData);
return !prefixesToExclude.some((field: string) => id.startsWith(field));
});
};
@ -974,6 +984,7 @@ export const filterHighlightedFields = (
* * event.category
* * event.code
* * kibana.alert.rule.type
* * Alert field ids filters
* @param alertData The Alert data object
*/
export const getAlertHighlightedFields = (alertData: AlertData): EventSummaryField[] => {
@ -982,7 +993,7 @@ export const getAlertHighlightedFields = (alertData: AlertData): EventSummaryFie
const eventRuleType = get(alertData, KIBANA_ALERT_RULE_TYPE);
const eventCategories = {
primaryEventCategory: eventCategory,
primaryEventCategory: Array.isArray(eventCategory) ? eventCategory[0] : eventCategory,
allEventCategories: [eventCategory],
};
@ -991,5 +1002,19 @@ export const getAlertHighlightedFields = (alertData: AlertData): EventSummaryFie
eventCode,
eventRuleType,
});
return filterHighlightedFields(fieldsToDisplay, highlightedFieldsPrefixToExclude);
return filterHighlightedFields(fieldsToDisplay, highlightedFieldsPrefixToExclude, alertData);
};
/**
* Checks to see if the given set of Timeline event detail items includes data that indicates its
* an endpoint Alert
*/
export const isAlertFromEndpointEvent = (alertData: AlertData) => {
// Check to see if a timeline event item is an Alert
const isTimelineEventItemAnAlert = get(alertData, KIBANA_ALERT_RULE_UUID);
if (!isTimelineEventItemAnAlert) return false;
const agentTypes = get(alertData, AGENT_TYPE);
const agentType = Array.isArray(agentTypes) ? agentTypes[0] : agentTypes;
return agentType === ENDPOINT_ALERT;
};

View file

@ -18,3 +18,7 @@ export const getKibanaAlertIdField = (id: string) => `kibana.alert.${id}`;
export const EVENT_CATEGORY = 'event.category';
export const EVENT_CODE = 'event.code';
export const KIBANA_ALERT_RULE_TYPE = 'kibana.alert.rule.type';
export const AGENT_ID = 'agent.id';
export const AGENT_TYPE = 'agent.type';
export const KIBANA_ALERT_RULE_UUID = 'kibana.alert.rule.uuid';
export const ENDPOINT_ALERT = 'endpoint';