diff --git a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts index 32300a2e66c96..14f6201cc8283 100644 --- a/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts +++ b/packages/kbn-securitysolution-utils/src/transform_data_to_ndjson/index.test.ts @@ -20,7 +20,6 @@ const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE) => ({ enabled: true, false_positives: ['false positive 1', 'false positive 2'], from: 'now-6m', - investigation_fields: ['custom.field1', 'custom.field2'], immutable: false, name: 'Query with a rule id', query: 'user.name: root or user.name: admin', diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts index db46af9091be1..9e0035e3e972b 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { listArray } from '@kbn/securitysolution-io-ts-list-types'; -import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types'; +import { NonEmptyString, version, UUID, NonEmptyArray } from '@kbn/securitysolution-io-ts-types'; import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types'; export type RuleObjectId = t.TypeOf; @@ -55,14 +55,6 @@ export const RuleAuthorArray = t.array(t.string); // should be non-empty strings export type RuleFalsePositiveArray = t.TypeOf; export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings? -/** - * User defined fields to display in areas such as alert details and exceptions auto-populate - * Field added in PR - https://github.com/elastic/kibana/pull/163235 - * @example const investigationFields: RuleCustomHighlightedFieldArray = ['host.os.name'] - */ -export type RuleCustomHighlightedFieldArray = t.TypeOf; -export const RuleCustomHighlightedFieldArray = t.array(NonEmptyString); - export type RuleReferenceArray = t.TypeOf; export const RuleReferenceArray = t.array(t.string); // should be non-empty strings? @@ -265,3 +257,32 @@ export const RelatedIntegration = t.exact( */ export type RelatedIntegrationArray = t.TypeOf; export const RelatedIntegrationArray = t.array(RelatedIntegration); + +/** + * Schema for fields relating to investigation fields, these are user defined fields we use to highlight + * in various features in the UI such as alert details flyout and exceptions auto-population from alert. + * Added in PR #163235 + * Right now we only have a single field but anticipate adding more related fields to store various + * configuration states such as `override` - where a user might say if they want only these fields to + * display, or if they want these fields + the fields we select. When expanding this field, it may look + * something like: + * export const investigationFields = t.intersection([ + * t.exact( + * t.type({ + * field_names: NonEmptyArray(NonEmptyString), + * }) + * ), + * t.exact( + * t.partial({ + * overide: t.boolean, + * }) + * ), + * ]); + * + */ +export type InvestigationFields = t.TypeOf; +export const InvestigationFields = t.exact( + t.type({ + field_names: NonEmptyArray(NonEmptyString), + }) +); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts index 9d15c355df60d..4d0e522ed734c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts @@ -1290,10 +1290,38 @@ describe('rules schema', () => { expect(getPaths(left(message.errors))).toEqual(['invalid keys "data_view_id"']); }); - test('You can optionally send in an array of investigation_fields', () => { + test('You can omit investigation_fields', () => { + // getCreateRulesSchemaMock doesn't include investigation_fields + const payload: RuleCreateProps = getCreateRulesSchemaMock(); + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('You cannot pass empty object for investigation_fields', () => { + const payload: Omit & { + investigation_fields: unknown; + } = { + ...getCreateRulesSchemaMock(), + investigation_fields: {}, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You can send in investigation_fields', () => { const payload: RuleCreateProps = { ...getCreateRulesSchemaMock(), - investigation_fields: ['field1', 'field2'], + investigation_fields: { field_names: ['field1', 'field2'] }, }; const decoded = RuleCreateProps.decode(payload); @@ -1303,19 +1331,49 @@ describe('rules schema', () => { expect(message.schema).toEqual(payload); }); - test('You cannot send in an array of investigation_fields that are numbers', () => { + test('You cannot send in an empty array of investigation_fields.field_names', () => { + const payload = { + ...getCreateRulesSchemaMock(), + investigation_fields: { field_names: [] }, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You cannot send in an array of investigation_fields.field_names that are numbers', () => { + const payload = { + ...getCreateRulesSchemaMock(), + investigation_fields: { field_names: [0, 1, 2] }, + }; + + const decoded = RuleCreateProps.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "0" supplied to "investigation_fields,field_names"', + 'Invalid value "1" supplied to "investigation_fields,field_names"', + 'Invalid value "2" supplied to "investigation_fields,field_names"', + ]); + expect(message.schema).toEqual({}); + }); + + test('You cannot send in investigation_fields without specifying fields', () => { const payload = { ...getCreateRulesSchemaMock(), - investigation_fields: [0, 1, 2], + investigation_fields: { foo: true }, }; const decoded = RuleCreateProps.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "0" supplied to "investigation_fields"', - 'Invalid value "1" supplied to "investigation_fields"', - 'Invalid value "2" supplied to "investigation_fields"', + 'Invalid value "undefined" supplied to "investigation_fields,field_names"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts index cd1562b1c4c48..da5ebfc27771e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts @@ -234,47 +234,73 @@ describe('Rule response schema', () => { }); describe('investigation_fields', () => { - test('it should validate rule with empty array for "investigation_fields"', () => { + test('it should validate rule with "investigation_fields"', () => { const payload = getRulesSchemaMock(); - payload.investigation_fields = []; + payload.investigation_fields = { field_names: ['foo', 'bar'] }; const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: [] }; + const expected = { + ...getRulesSchemaMock(), + investigation_fields: { field_names: ['foo', 'bar'] }, + }; expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); - test('it should validate rule with "investigation_fields"', () => { - const payload = getRulesSchemaMock(); - payload.investigation_fields = ['foo', 'bar']; + test('it should validate undefined for "investigation_fields"', () => { + const payload: RuleResponse = { + ...getRulesSchemaMock(), + investigation_fields: undefined, + }; const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: ['foo', 'bar'] }; + const expected = { ...getRulesSchemaMock(), investigation_fields: undefined }; expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); - test('it should validate undefined for "investigation_fields"', () => { + test('it should validate "investigation_fields" not in schema', () => { const payload: RuleResponse = { ...getRulesSchemaMock(), investigation_fields: undefined, }; + delete payload.investigation_fields; + const decoded = RuleResponse.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); - const expected = { ...getRulesSchemaMock(), investigation_fields: undefined }; + const expected = getRulesSchemaMock(); expect(getPaths(left(message.errors))).toEqual([]); expect(message.schema).toEqual(expected); }); + test('it should NOT validate an empty array for "investigation_fields.field_names"', () => { + const payload: RuleResponse = { + ...getRulesSchemaMock(), + investigation_fields: { + field_names: [], + }, + }; + + const decoded = RuleResponse.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "[]" supplied to "investigation_fields,field_names"', + 'Invalid value "{"field_names":[]}" supplied to "investigation_fields"', + ]); + expect(message.schema).toEqual({}); + }); + test('it should NOT validate a string for "investigation_fields"', () => { const payload: Omit & { investigation_fields: string; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts index acc429eb2f340..fd9f25b19cc0d 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.ts @@ -53,7 +53,7 @@ import { RelatedIntegrationArray, RequiredFieldArray, RuleAuthorArray, - RuleCustomHighlightedFieldArray, + InvestigationFields, RuleDescription, RuleFalsePositiveArray, RuleFilterArray, @@ -117,7 +117,7 @@ export const baseSchema = buildRuleSchemas({ output_index: AlertsIndex, namespace: AlertsIndexNamespace, meta: RuleMetadata, - investigation_fields: RuleCustomHighlightedFieldArray, + investigation_fields: InvestigationFields, // Throttle throttle: RuleActionThrottle, }, diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 7d0892d3cbf9d..2273389e53733 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -289,7 +289,7 @@ const EventDetailsComponent: React.FC = ({ isReadOnly, }} goToTable={goToTableTab} - investigationFields={maybeRule?.investigation_fields ?? []} + investigationFields={maybeRule?.investigation_fields?.field_names ?? []} /> { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -636,7 +636,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -661,7 +661,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -705,7 +705,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -758,7 +758,7 @@ describe('helpers', () => { ], }, ], - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); @@ -787,13 +787,13 @@ describe('helpers', () => { threat: getThreatMock(), timestamp_override: 'event.ingest', timestamp_override_fallback_disabled: true, - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; expect(result).toEqual(expected); }); - test('returns formatted object if investigation_fields is empty array', () => { + test('returns formatted object if investigationFields is empty array', () => { const mockStepData: AboutStepRule = { ...mockData, investigationFields: [], @@ -817,7 +817,7 @@ describe('helpers', () => { timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, threat: getThreatMock(), - investigation_fields: [], + investigation_fields: undefined, }; expect(result).toEqual(expected); @@ -843,7 +843,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, @@ -855,7 +855,7 @@ describe('helpers', () => { test('returns formatted object if investigation_fields includes empty string', () => { const mockStepData: AboutStepRule = { ...mockData, - investigationFields: [' '], + investigationFields: [' '], }; const result = formatAboutStepData(mockStepData); const expected: AboutStepRuleJson = { @@ -872,7 +872,7 @@ describe('helpers', () => { severity_mapping: [], tags: ['tag1', 'tag2'], threat: getThreatMock(), - investigation_fields: [], + investigation_fields: undefined, threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index c3cb11e907a14..c3055adac6ee4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -502,6 +502,7 @@ export const formatAboutStepData = ( const detectionExceptionLists = exceptionsList != null ? exceptionsList.filter((list) => list.type !== 'endpoint') : []; + const isinvestigationFieldsEmpty = investigationFields.every((item) => isEmpty(item.trim())); const resp = { author: author.filter((item) => !isEmpty(item)), @@ -525,7 +526,9 @@ export const formatAboutStepData = ( : {}), false_positives: falsePositives.filter((item) => !isEmpty(item)), references: references.filter((item) => !isEmpty(item)), - investigation_fields: investigationFields.filter((item) => !isEmpty(item.trim())), + investigation_fields: isinvestigationFieldsEmpty + ? undefined + : { field_names: investigationFields }, risk_score: riskScore.value, risk_score_mapping: riskScore.isMappingChecked ? riskScore.mapping.filter((m) => m.field != null && m.field !== '') diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx index 6150cb3b6d0e8..573419676a086 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx @@ -671,7 +671,7 @@ describe('When the add exception modal is opened', () => { rules={[ { ...getRulesSchemaMock(), - investigation_fields: ['foo.bar'], + investigation_fields: { field_names: ['foo.bar'] }, exceptions_list: [], } as Rule, ]} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index d7081f195fefc..15880fcd423fc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -348,7 +348,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ exceptionItemName, // With "rule_default" type, there is only ever one rule associated. // That is why it's ok to pull just the first item from rules array here. - ruleCustomHighlightedFields: rules?.[0]?.investigation_fields ?? [], + ruleCustomHighlightedFields: rules?.[0]?.investigation_fields?.field_names ?? [], }); if (populatedException) { setComment(i18n.ADD_RULE_EXCEPTION_FROM_ALERT_COMMENT(alertData._id)); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index 12ca5b5683f68..5c3002946baf6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -283,10 +283,12 @@ const prepareAboutSectionListItems = (rule: RuleResponse): EuiDescriptionListPro }); } - if (rule.investigation_fields && rule.investigation_fields.length > 0) { + if (rule.investigation_fields && rule.investigation_fields.field_names.length > 0) { aboutSectionListItems.push({ title: i18n.INVESTIGATION_FIELDS_FIELD_LABEL, - description: , + description: ( + + ), }); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts index 8c89032a7d15b..4bb86c26ca760 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts @@ -73,7 +73,7 @@ import { TimestampField, TimestampOverride, TimestampOverrideFallbackDisabled, - RuleCustomHighlightedFieldArray, + InvestigationFields, } from '../../../../common/api/detection_engine/model/rule_schema'; import type { @@ -202,7 +202,7 @@ export const RuleSchema = t.intersection([ version: RuleVersion, execution_summary: RuleExecutionSummary, alert_suppression: AlertSuppression, - investigation_fields: RuleCustomHighlightedFieldArray, + investigation_fields: InvestigationFields, }), ]); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 78c81f5c9b1d5..5cdf1ed93ac5d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -231,7 +231,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu isMappingChecked: riskScoreMapping.length > 0, }, falsePositives, - investigationFields: investigationFields ?? [], + investigationFields: investigationFields?.field_names ?? [], threat: threat as Threats, threatIndicatorPath, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index c5b98b1bfd39d..0d03c43d3ada5 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -34,6 +34,7 @@ import type { SetupGuide, TimestampOverride, AlertSuppressionMissingFields, + InvestigationFields, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../common/api/detection_engine'; import type { EqlOptionsSelected } from '../../../../../common/search_strategy'; @@ -239,7 +240,7 @@ export interface AboutStepRuleJson { timestamp_override?: TimestampOverride; timestamp_override_fallback_disabled?: boolean; note?: string; - investigation_fields?: string[]; + investigation_fields?: InvestigationFields; } export interface ScheduleStepRuleJson { diff --git a/x-pack/plugins/security_solution/public/flyout/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/left/context.tsx index b5c4f340d5485..15de2dfc9a78d 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/context.tsx @@ -105,7 +105,7 @@ export const LeftPanelProvider = ({ id, indexName, scopeId, children }: LeftPane dataAsNestedObject, dataFormattedForFieldBrowser, searchHit, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], getFieldsData, } : undefined, diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx index 5ae89154a05f2..2f21aab420af9 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../detection_engine/rule_management/logic/use_rule_with_fallbac describe('', () => { beforeEach(() => { - (useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: [] }); + (useRuleWithFallback as jest.Mock).mockReturnValue({ investigation_fields: undefined }); }); it('should render the component', () => { diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx index 908df4590aad2..4a0405c06b7e1 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx @@ -91,7 +91,7 @@ export const HighlightedFields: FC = () => { const highlightedFields = useHighlightedFields({ dataFormattedForFieldBrowser, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], }); const items = useMemo( () => convertHighlightedFieldsToTableRow(highlightedFields, scopeId), diff --git a/x-pack/plugins/security_solution/public/flyout/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/right/context.tsx index 31eec77707d2f..fb9424314deb2 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/context.tsx @@ -117,7 +117,7 @@ export const RightPanelProvider = ({ dataAsNestedObject, dataFormattedForFieldBrowser, searchHit, - investigationFields: maybeRule?.investigation_fields ?? [], + investigationFields: maybeRule?.investigation_fields?.field_names ?? [], refetchFlyoutData, getFieldsData, } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 003e55fa6f1b8..819bf87165e12 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -100,5 +100,5 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ namespace: undefined, data_view_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts index 18f6bf5f21fc8..1d6d0174dae75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/logic/notifications/schedule_notification_actions.test.ts @@ -44,7 +44,7 @@ describe('schedule_notification_actions', () => { responseActions: [], riskScore: 80, riskScoreMapping: [], - investigationFields: [], + investigationFields: undefined, ruleNameOverride: undefined, dataViewId: undefined, outputIndex: 'output-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index 5248e7f06938f..aa9d7c4f8303b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -45,7 +45,7 @@ export const updateRules = async ({ ruleId: existingRule.params.ruleId, falsePositives: ruleUpdate.false_positives ?? [], from: ruleUpdate.from ?? 'now-6m', - investigationFields: ruleUpdate.investigation_fields ?? [], + investigationFields: ruleUpdate.investigation_fields, // Unlike the create route, immutable comes from the existing rule here immutable: existingRule.params.immutable, license: ruleUpdate.license, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 662085c95d62b..6897fdaf0b92c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -136,7 +136,7 @@ describe('getExportAll', () => { note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 1, @@ -320,7 +320,7 @@ describe('getExportAll', () => { version: 1, revision: 0, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 1, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index b8f27c0d16a5c..c2fe91251d75d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -132,7 +132,7 @@ describe('get_export_by_object_ids', () => { note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }, exportDetails: { exported_exception_list_count: 0, @@ -328,7 +328,7 @@ describe('get_export_by_object_ids', () => { version: 1, revision: 0, exceptions_list: getListArrayMock(), - investigation_fields: [], + investigation_fields: undefined, }); expect(detailsJson).toEqual({ exported_exception_list_count: 0, @@ -525,7 +525,7 @@ describe('get_export_by_object_ids', () => { namespace: undefined, data_view_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }, ], }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 385fe889be40a..84acb597910ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -476,7 +476,7 @@ export const convertCreateAPIToInternalSchema = ( description: input.description, ruleId: newRuleId, falsePositives: input.false_positives ?? [], - investigationFields: input.investigation_fields ?? [], + investigationFields: input.investigation_fields, from: input.from ?? DEFAULT_FROM, immutable, license: input.license, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index d24bcaa5a7b9b..eb442f5fc37bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -78,7 +78,7 @@ export const ruleOutput = (): RuleResponse => ({ data_view_id: undefined, saved_id: undefined, alert_suppression: undefined, - investigation_fields: [], + investigation_fields: undefined, }); describe('validate', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index a6e6ee5282e89..0618c0ff722f1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -48,7 +48,6 @@ const getBaseRuleParams = (): BaseRuleParams => { timelineTitle: 'some-timeline-title', timestampOverride: undefined, timestampOverrideFallbackDisabled: undefined, - investigationFields: [], meta: { someMeta: 'someField', }, @@ -58,6 +57,7 @@ const getBaseRuleParams = (): BaseRuleParams => { relatedIntegrations: [], requiredFields: [], setup: '', + investigationFields: undefined, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index d3e66cf49148d..449ce553bf32b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -57,7 +57,7 @@ import { RuleAuthorArray, RuleDescription, RuleFalsePositiveArray, - RuleCustomHighlightedFieldArray, + InvestigationFields, RuleFilterArray, RuleLicense, RuleMetadata, @@ -98,7 +98,7 @@ export const baseRuleParams = t.exact( falsePositives: RuleFalsePositiveArray, from: RuleIntervalFrom, ruleId: RuleSignatureId, - investigationFields: t.union([RuleCustomHighlightedFieldArray, t.undefined]), + investigationFields: t.union([InvestigationFields, t.undefined]), immutable: IsRuleImmutable, license: t.union([RuleLicense, t.undefined]), outputIndex: AlertsIndex, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts index 8e06156bdc913..a9ae0d1d55696 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -165,7 +165,7 @@ describe('buildAlert', () => { index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], query: 'user.name: root or user.name: admin', filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }], - investigation_fields: [], + investigation_fields: undefined, }, [ALERT_RULE_INDICES]: completeRule.ruleParams.index, ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { @@ -359,7 +359,7 @@ describe('buildAlert', () => { index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], query: 'user.name: root or user.name: admin', filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }], - investigation_fields: [], + investigation_fields: undefined, }, ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { actions: [], diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts index 00307619d8477..fbbef20984642 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts @@ -77,7 +77,6 @@ export default ({ getService }: FtrProviderContext) => { author: [], created_by: 'elastic', description: 'Simple Rule Query', - investigation_fields: [], enabled: true, false_positives: [], from: 'now-6m', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts index aa32e1ebb114b..d8fc2ec1439be 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts @@ -213,7 +213,6 @@ export default ({ getService }: FtrProviderContext) => { risk_score_mapping: [], name: 'Simple Rule Query', query: 'user.name: root or user.name: admin', - investigation_fields: [], references: [], related_integrations: [], required_fields: [], diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts index 7a4afd6d44d85..44ac35cf9569d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts @@ -754,6 +754,5 @@ function expectToMatchRuleSchema(obj: RuleResponse): void { index: expect.arrayContaining([]), query: expect.any(String), actions: expect.arrayContaining([]), - investigation_fields: expect.arrayContaining([]), }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts index b14e0e87a37c0..b07a54f86d2f6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts @@ -25,6 +25,7 @@ import { deleteAllAlerts, getSimpleRule, getSimpleRuleAsNdjson, + getRulesAsNdjson, getSimpleRuleOutput, getThresholdRuleForSignalTesting, getWebHookAction, @@ -1868,6 +1869,21 @@ export default ({ getService }: FtrProviderContext): void => { }); }); }); + + it('should import a rule with "investigation_fields', async () => { + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_import`) + .set('kbn-xsrf', 'true') + .attach( + 'file', + getRulesAsNdjson([ + { ...getSimpleRule(), investigation_fields: { field_names: ['foo'] } }, + ]), + 'rules.ndjson' + ) + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts index 498dde28a632e..09e2c7d84c0a8 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts @@ -445,15 +445,15 @@ export default ({ getService }: FtrProviderContext) => { }); describe('investigation_fields', () => { - it('should overwrite investigation_fields value on update - non additive', async () => { + it('should overwrite investigation_fields value on patch - non additive', async () => { await createRule(supertest, log, { ...getSimpleRule('rule-1'), - investigation_fields: ['blob', 'boop'], + investigation_fields: { field_names: ['blob', 'boop'] }, }); const rulePatch = { rule_id: 'rule-1', - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; const { body } = await supertest @@ -463,7 +463,47 @@ export default ({ getService }: FtrProviderContext) => { .send(rulePatch) .expect(200); - expect(body.investigation_fields).to.eql(['foo', 'bar']); + expect(body.investigation_fields.field_names).to.eql(['foo', 'bar']); + }); + + it('should not allow field to be unset', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const rulePatch = { + rule_id: 'rule-1', + investigation_fields: undefined, + }; + + const { body } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rulePatch) + .expect(200); + + expect(body.investigation_fields).to.eql({ field_names: ['blob', 'boop'] }); + }); + + it('should not unset investigation_fields if not specified in patch', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const rulePatch = { + rule_id: 'rule-1', + name: 'New name', + }; + + const { body } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rulePatch) + .expect(200); + + expect(body.investigation_fields.field_names).to.eql(['blob', 'boop']); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index dad8b37321e7b..226432cbdbb39 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -870,12 +870,12 @@ export default ({ getService }: FtrProviderContext) => { it('should overwrite investigation_fields value on update - non additive', async () => { await createRule(supertest, log, { ...getSimpleRule('rule-1'), - investigation_fields: ['blob', 'boop'], + investigation_fields: { field_names: ['blob', 'boop'] }, }); const ruleUpdate = { ...getSimpleRuleUpdate('rule-1'), - investigation_fields: ['foo', 'bar'], + investigation_fields: { field_names: ['foo', 'bar'] }, }; const { body } = await supertest @@ -885,7 +885,27 @@ export default ({ getService }: FtrProviderContext) => { .send(ruleUpdate) .expect(200); - expect(body.investigation_fields).to.eql(['foo', 'bar']); + expect(body.investigation_fields.field_names).to.eql(['foo', 'bar']); + }); + + it('should unset investigation_fields', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + investigation_fields: { field_names: ['blob', 'boop'] }, + }); + + const ruleUpdate = { + ...getSimpleRuleUpdate('rule-1'), + investigation_fields: undefined, + }; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleUpdate) + .expect(200); + + expect(body.investigation_fields).to.eql(undefined); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts index 1435565286485..9e7b6265a2b9f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group6/alerts/alerts_compatibility.ts @@ -337,7 +337,6 @@ export default ({ getService }: FtrProviderContext) => { max_signals: 100, risk_score_mapping: [], severity_mapping: [], - investigation_fields: [], threat: [], to: 'now', references: [], @@ -513,7 +512,6 @@ export default ({ getService }: FtrProviderContext) => { related_integrations: [], required_fields: [], setup: '', - investigation_fields: [], }, 'kibana.alert.rule.actions': [], 'kibana.alert.rule.created_by': 'elastic', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts index b9f6b8caed951..792fcb30b6645 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts @@ -146,7 +146,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'machine_learning', version: 1, - investigation_fields: [], }, [ALERT_DEPTH]: 1, [ALERT_REASON]: `event with process store, by root on mothra created critical alert Test ML rule.`, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts index 72a71185065d9..0ac86c991015d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/new_terms.ts @@ -198,7 +198,6 @@ export default ({ getService }: FtrProviderContext) => { history_window_start: '2019-01-19T20:42:00.000Z', index: ['auditbeat-*'], language: 'kuery', - investigation_fields: [], }, 'kibana.alert.rule.actions': [], 'kibana.alert.rule.author': [], diff --git a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts index 9c35d6652935f..0115b00c4b46b 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts @@ -102,5 +102,4 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial = related_integrations: [], required_fields: [], setup: '', - investigation_fields: [], }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts b/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts new file mode 100644 index 0000000000000..24d448af21f1b --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rules_as_ndjson.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +/** + * Given an array of objects (assuming rules) this will return a ndjson buffer which is useful + * for testing uploads. + * @param rules Array of rules + */ +export const getRulesAsNdjson = (rules: unknown[]): Buffer => { + const stringOfRules = rules.map((rule) => { + return JSON.stringify(rule); + }); + return Buffer.from(stringOfRules.join('\n')); +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts index 92f427876e351..0a9eec4906a14 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts @@ -60,7 +60,7 @@ export const getMockSharedResponseSchema = ( timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, namespace: undefined, - investigation_fields: [], + investigation_fields: undefined, }); const getQueryRuleOutput = (ruleId = 'rule-1', enabled = false): RuleResponse => ({ diff --git a/x-pack/test/detection_engine_api_integration/utils/index.ts b/x-pack/test/detection_engine_api_integration/utils/index.ts index dc41cedbca00e..4ad3ec1f1a62e 100644 --- a/x-pack/test/detection_engine_api_integration/utils/index.ts +++ b/x-pack/test/detection_engine_api_integration/utils/index.ts @@ -46,6 +46,7 @@ export * from './get_query_signal_ids'; export * from './get_query_signals_ids'; export * from './get_query_signals_rule_id'; export * from './get_rule'; +export * from './get_rules_as_ndjson'; export * from './get_rule_for_signal_testing'; export * from './get_rule_so_by_id'; export * from './get_rule_for_signal_testing_with_timestamp_override'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts index 470c8494386e1..61bfbc8ee0958 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_details.cy.ts @@ -13,6 +13,7 @@ import { COPY_ALERT_FLYOUT_LINK, JSON_TEXT, OVERVIEW_RULE, + SUMMARY_VIEW, TABLE_CONTAINER, TABLE_ROWS, } from '../../../screens/alerts_details'; @@ -37,7 +38,7 @@ import { goToRuleDetails } from '../../../tasks/alerts_detection_rules'; describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { describe('Basic functions', () => { - before(() => { + beforeEach(() => { cleanKibana(); login(); disableExpandableFlyout(); @@ -48,6 +49,7 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { }); it('should update the table when status of the alert is updated', () => { + cy.get(OVERVIEW_RULE).should('be.visible'); cy.get(ALERTS_TABLE_COUNT).should('have.text', '2 alerts'); cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('contain.text', '2alerts'); expandFirstAlert(); @@ -61,7 +63,7 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); cy.task('esArchiverLoad', { archiveName: 'unmapped_fields' }); - createRule(getUnmappedRule()); + createRule({ ...getUnmappedRule(), investigation_fields: { field_names: ['event.kind'] } }); }); beforeEach(() => { @@ -76,6 +78,13 @@ describe('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => { cy.task('esArchiverUnload', 'unmapped_fields'); }); + it('should display user and system defined highlighted fields', () => { + cy.get(SUMMARY_VIEW) + .should('be.visible') + .and('contain.text', 'event.kind') + .and('contain.text', 'Rule type'); + }); + it('should display the unmapped field on the JSON view', () => { const expectedUnmappedValue = 'This is the unmapped field'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts index 97645c3df5490..4370acb6acd99 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_prevalence_tab.cy.ts @@ -35,7 +35,7 @@ describe('Alert details expandable flyout left panel prevalence', () => { beforeEach(() => { cleanKibana(); login(); - createRule(getNewRule()); + createRule({ ...getNewRule(), investigation_fields: { field_names: ['host.os.name'] } }); visit(ALERTS_URL); waitForAlertsToPopulate(); expandFirstAlertExpandableFlyout(); @@ -57,10 +57,12 @@ describe('Alert details expandable flyout left panel prevalence', () => { cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE).should('be.visible'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_TYPE_CELL) - .should('contain.text', 'host.name') + .should('contain.text', 'host.os.name') + .and('contain.text', 'host.name') .and('contain.text', 'user.name'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_NAME_CELL) - .should('contain.text', 'siem-kibana') + .should('contain.text', 'Mac OS X') + .and('contain.text', 'siem-kibana') .and('contain.text', 'test'); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_TABLE_ALERT_COUNT_CELL).should( 'contain.text', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index 6c9f14cbc7a04..dfd09cf4ca117 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -71,7 +71,7 @@ describe( 'Alert details expandable flyout right panel overview tab', { tags: ['@ess', '@brokenInServerless'] }, () => { - const rule = getNewRule(); + const rule = { ...getNewRule(), investigation_fields: { field_names: ['host.os.name'] } }; beforeEach(() => { cleanKibana(); @@ -177,7 +177,7 @@ describe( cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).scrollIntoView(); cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).should('be.visible'); - cy.log('highlighted fields'); + cy.log('highlighted fields section'); cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_HEADER_TITLE) .should('be.visible') @@ -186,6 +186,17 @@ describe( 'be.visible' ); + cy.log('custom highlighted fields'); + + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL) + .should('be.visible') + .and('contain.text', 'host.os.name'); + const customHighlightedField = + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_VALUE_CELL('Mac OS X'); + cy.get(customHighlightedField).should('be.visible').and('have.text', 'Mac OS X'); + + cy.log('system defined highlighted fields'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL) .should('be.visible') .and('contain.text', 'host.name'); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts index b4eaaeda35e5d..9d57a2b502f33 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts_details.ts @@ -45,7 +45,7 @@ export const OVERVIEW_STATUS = '[data-test-subj="eventDetails"] [data-test-subj= export const EVENT_DETAILS_ALERT_STATUS_POPOVER = '[data-test-subj="event-details-alertStatusPopover"]'; -const SUMMARY_VIEW = '[data-test-subj="summary-view"]'; +export const SUMMARY_VIEW = '[data-test-subj="summary-view"]'; export const TABLE_CELL = '.euiTableRowCell';