Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ export const allowedExperimentalValues = Object.freeze({
*/
newExpandableFlyoutNavigationDisabled: false,

/**
* Enables the ability to edit highlighted fields in the alertflyout
*/
editHighlightedFieldsEnabled: false,

/**
* Enables CrowdStrike's RunScript RTR command
* Release: 8.18/9.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.
*/

import { alwaysDisplayedFields, getHighlightedFieldsToDisplay } from './get_alert_summary_rows';

describe('getHighlightedFieldsToDisplay', () => {
it('should return custom highlighted fields correctly', () => {
const result = getHighlightedFieldsToDisplay({
eventCategories: {},
ruleCustomHighlightedFields: ['customField1', 'customField2'],
type: 'custom',
});
expect(result).toEqual([{ id: 'customField1' }, { id: 'customField2' }]);
});

it('should return the default highlighted fields correctly', () => {
const result = getHighlightedFieldsToDisplay({
eventCategories: {},
ruleCustomHighlightedFields: ['customField1', 'customField2'],
type: 'default',
});
expect(result).toEqual(alwaysDisplayedFields);
});

it('should return both custom and default highlighted fields correctly', () => {
const result = getHighlightedFieldsToDisplay({
eventCategories: {},
ruleCustomHighlightedFields: ['customField1', 'customField2'],
});
expect(result).toEqual([
{ id: 'customField1' },
{ id: 'customField2' },
...alwaysDisplayedFields,
]);
});

it('should return a list of unique fields', () => {
const result = getHighlightedFieldsToDisplay({
eventCategories: {},
ruleCustomHighlightedFields: ['customField1', 'customField2', 'host.name'],
});
expect(result).toEqual([
{ id: 'customField1' },
{ id: 'customField2' },
...alwaysDisplayedFields,
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const RULE_TYPE = i18n.translate('xpack.securitySolution.detections.alerts.ruleT
});

/** Always show these fields */
const alwaysDisplayedFields: EventSummaryField[] = [
export const alwaysDisplayedFields: EventSummaryField[] = [
{ id: 'host.name' },

// Add all fields used to identify the agent ID in alert events and override them to
Expand All @@ -68,8 +68,6 @@ const alwaysDisplayedFields: EventSummaryField[] = [
{ id: 'rule.name' },
{ id: 'cloud.provider' },
{ id: 'cloud.region' },
{ id: 'cloud.provider' },
{ id: 'cloud.region' },
{ id: 'orchestrator.cluster.id' },
{ id: 'orchestrator.cluster.name' },
{ id: 'container.image.name' },
Expand Down Expand Up @@ -239,7 +237,7 @@ function getFieldsByRuleType(ruleType?: string): EventSummaryField[] {
* @param customs The list of custom-defined fields to display
* @returns The list of custom-defined fields to display
*/
function getHighlightedFieldsOverride(customs: string[]): EventSummaryField[] {
function getCustomHighlightedFields(customs: string[]): EventSummaryField[] {
return customs.map((field) => ({ id: field }));
}

Expand All @@ -253,27 +251,36 @@ function getHighlightedFieldsOverride(customs: string[]): EventSummaryField[] {
/**
* Assembles a list of fields to display based on the event
*/
export function getEventFieldsToDisplay({
export function getHighlightedFieldsToDisplay({
eventCategories,
eventCode,
eventRuleType,
highlightedFieldsOverride,
ruleCustomHighlightedFields,
type = 'all',
}: {
eventCategories: EventCategories;
eventCode?: string;
eventRuleType?: string;
highlightedFieldsOverride: string[];
ruleCustomHighlightedFields: string[];
type?: 'default' | 'custom' | 'all';
}): EventSummaryField[] {
const fields = [
...getHighlightedFieldsOverride(highlightedFieldsOverride),
const customHighlightedFields = getCustomHighlightedFields(ruleCustomHighlightedFields);
const defaultHighlightedFields = [
...alwaysDisplayedFields,
...getFieldsByCategory(eventCategories),
...getFieldsByEventCode(eventCode, eventCategories),
...getFieldsByRuleType(eventRuleType),
];

// Filter all fields by their id to make sure there are no duplicates
return uniqBy('id', fields);
if (type === 'default') {
return uniqBy('id', defaultHighlightedFields);
}

if (type === 'custom') {
return customHighlightedFields;
}

return uniqBy('id', [...customHighlightedFields, ...defaultHighlightedFields]);
}

interface EventCategories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { removeIdFromExceptionItemsEntries } from '@kbn/securitysolution-list-ho

import type { EcsSecurityExtension as Ecs, CodeSignature } from '@kbn/securitysolution-ecs';
import type { EventSummaryField } from '../../../common/components/event_details/types';
import { getEventFieldsToDisplay } from '../../../common/components/event_details/get_alert_summary_rows';
import { getHighlightedFieldsToDisplay } from '../../../common/components/event_details/get_alert_summary_rows';
import * as i18n from './translations';
import type { AlertData, Flattened } from './types';

Expand Down Expand Up @@ -987,11 +987,11 @@ export const getAlertHighlightedFields = (
allEventCategories: Array.isArray(eventCategory) ? eventCategory : [eventCategory],
};

const fieldsToDisplay = getEventFieldsToDisplay({
const fieldsToDisplay = getHighlightedFieldsToDisplay({
eventCategories,
eventCode,
eventRuleType,
highlightedFieldsOverride: ruleCustomHighlightedFields,
ruleCustomHighlightedFields,
});
return filterHighlightedFields(fieldsToDisplay, highlightedFieldsPrefixToExclude, alertData);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,33 @@
import React from 'react';
import { render } from '@testing-library/react';
import { DocumentDetailsContext } from '../../shared/context';
import { HIGHLIGHTED_FIELDS_DETAILS_TEST_ID, HIGHLIGHTED_FIELDS_TITLE_TEST_ID } from './test_ids';
import {
HIGHLIGHTED_FIELDS_DETAILS_TEST_ID,
HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
HIGHLIGHTED_FIELDS_TITLE_TEST_ID,
} from './test_ids';
import { HighlightedFields } from './highlighted_fields';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
import { TestProviders } from '../../../../common/mock';
import { useRuleWithFallback } from '../../../../detection_engine/rule_management/logic/use_rule_with_fallback';
import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
import { mockContextValue } from '../../shared/mocks/mock_context';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
import { useRuleDetails } from '../../../rule_details/hooks/use_rule_details';
import type { RuleResponse } from '../../../../../common/api/detection_engine';

jest.mock('../../shared/hooks/use_highlighted_fields');
jest.mock('../../../../detection_engine/rule_management/logic/use_rule_with_fallback');
jest.mock('../../../../detection_engine/rule_creation_ui/pages/form');
jest.mock('../../../../common/hooks/use_experimental_features');
jest.mock('../../shared/hooks/use_highlighted_fields_privilege');
jest.mock('../../../rule_details/hooks/use_rule_details');
const mockAddSuccess = jest.fn();
jest.mock('../../../../common/hooks/use_app_toasts', () => ({
useAppToasts: () => ({
addSuccess: mockAddSuccess,
}),
}));

const renderHighlightedFields = (contextValue: DocumentDetailsContext) =>
render(
Expand All @@ -30,35 +48,97 @@ const renderHighlightedFields = (contextValue: DocumentDetailsContext) =>
const NO_DATA_MESSAGE = "There's no highlighted fields for this alert.";

describe('<HighlightedFields />', () => {
beforeEach(() => {
(useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: undefined });
});
describe('when editHighlightedFieldsEnabled is false', () => {
beforeEach(() => {
jest.clearAllMocks();
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
(useHighlightedFieldsPrivilege as jest.Mock).mockReturnValue({
isEditHighlightedFieldsDisabled: false,
tooltipContent: 'tooltip content',
});
(useRuleIndexPattern as jest.Mock).mockReturnValue({
indexPattern: { fields: ['field'] },
isIndexPatternLoading: false,
});
(useRuleDetails as jest.Mock).mockReturnValue({
rule: null,
isExistingRule: true,
loading: false,
});
});

it('should render the component', () => {
(useHighlightedFields as jest.Mock).mockReturnValue({
field: {
values: ['value'],
},
});

const { getByTestId, queryByTestId } = renderHighlightedFields(mockContextValue);

it('should render the component', () => {
const contextValue = {
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
scopeId: 'scopeId',
} as unknown as DocumentDetailsContext;
(useHighlightedFields as jest.Mock).mockReturnValue({
field: {
values: ['value'],
},
expect(getByTestId(HIGHLIGHTED_FIELDS_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(HIGHLIGHTED_FIELDS_DETAILS_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).not.toBeInTheDocument();
});

const { getByTestId } = renderHighlightedFields(contextValue);
it(`should render no data message if there aren't any highlighted fields`, () => {
(useHighlightedFields as jest.Mock).mockReturnValue({});

expect(getByTestId(HIGHLIGHTED_FIELDS_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(HIGHLIGHTED_FIELDS_DETAILS_TEST_ID)).toBeInTheDocument();
const { getByText, queryByTestId } = renderHighlightedFields(mockContextValue);
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
expect(queryByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).not.toBeInTheDocument();
});
});

it(`should render no data message if there aren't any highlighted fields`, () => {
const contextValue = {
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
scopeId: 'scopeId',
} as unknown as DocumentDetailsContext;
(useHighlightedFields as jest.Mock).mockReturnValue({});
describe('when editHighlightedFieldsEnabled is true', () => {
beforeEach(() => {
jest.clearAllMocks();
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useHighlightedFieldsPrivilege as jest.Mock).mockReturnValue({
isEditHighlightedFieldsDisabled: false,
tooltipContent: 'tooltip content',
});
(useRuleIndexPattern as jest.Mock).mockReturnValue({
indexPattern: { fields: ['field'] },
isIndexPatternLoading: false,
});
(useRuleDetails as jest.Mock).mockReturnValue({
rule: { id: '123' } as RuleResponse,
isExistingRule: true,
loading: false,
});
});

it('should render the component', () => {
(useHighlightedFields as jest.Mock).mockReturnValue({
field: {
values: ['value'],
},
});

const { getByText } = renderHighlightedFields(contextValue);
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
const { getByTestId } = renderHighlightedFields(mockContextValue);

expect(getByTestId(HIGHLIGHTED_FIELDS_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(HIGHLIGHTED_FIELDS_DETAILS_TEST_ID)).toBeInTheDocument();
expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).toBeInTheDocument();
});

it(`should render no data message if there aren't any highlighted fields`, () => {
(useHighlightedFields as jest.Mock).mockReturnValue({});

const { getByText, getByTestId } = renderHighlightedFields(mockContextValue);
expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).toBeInTheDocument();
});

it('should not render edit button if rule is null', () => {
(useRuleDetails as jest.Mock).mockReturnValue({
rule: null,
isExistingRule: true,
loading: false,
});
const { queryByTestId } = renderHighlightedFields(mockContextValue);
expect(queryByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).not.toBeInTheDocument();
});
});
});
Loading