Repository: elastic/kibana
Pull request #216740: [Security Solution][Alert flyout] Edit highlighted fields in overview tab
Author: login: christineweng
Labels: name: release_note:feature
description: Makes this part of the condensed release notes
name: Team:Threat Hunting:Investigations
description: Security Solution Threat Hunting Investigations Team
name: backport:version
description: Backport to applied version labels
name: v9.1.0
name: v8.19.0
Created: 2025-04-02T00:46:30Z | Merged: 2025-04-15T17:18:36Z

## Summary

This PR allows user to edit highlighted fields in alert flyout, under `Investigations`. The modal shows default highlighted fields that are defined by Elastic, and allow user to edit custom highlighted fields. 

Currently this feature is behind feature flag `editHighlightedFieldsEnabled` (not enabled by default).


https://github.com/user-attachments/assets/35b3d09e-5e21-42ea-80e9-e8c0753985c9



#### Disabled when:

<details>
<summary>User does not have security privilege</summary>

![image](https://github.com/user-attachments/assets/69ba7bc7-2d9b-4a2c-ae8e-e9c14f396a31)

</details>

<details>
<summary>Prebuilt rule w/o enterprise license (showing upsell)</summary>

![image](https://github.com/user-attachments/assets/a9c38e20-85b2-4082-af5e-a8707b2098cb)

</details>

#### Do not show the button when:
<details>
<summary>Not an alert </summary>

![image](https://github.com/user-attachments/assets/b5e9afde-f0d0-4a88-aaed-7481ba586850)

</details>

<details>
<summary>rule preview</summary>

![image](https://github.com/user-attachments/assets/283d7a83-50b2-48ab-af2d-11692501c205)

</details>

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [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
- [x] The PR  description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)


<!--ONMERGE {"backportTargets":["8.x"]} ONMERGE-->


Diff:
diff --git a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts
index 00d26bf3802d4..3744c902d76bc 100644
--- a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts
+++ b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts
@@ -240,6 +240,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
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.test.tsx
new file mode 100644
index 0000000000000..d6102a4c23a75
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.test.tsx
@@ -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,
+    ]);
+  });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
index 02c598ce833a6..4494a5290799b 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
@@ -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
@@ -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' },
@@ -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 }));
 }
 
@@ -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 {
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx
index 08e4dc4289bbc..d879b79a042f7 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/utils/helpers.tsx
@@ -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';
 
@@ -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);
 };
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.test.tsx
index f4033bba7bce9..4850463ae6c33 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.test.tsx
@@ -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(
@@ -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();
+    });
   });
 });
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx
index 3824860bf5677..ac7b3b3a647e4 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx
@@ -6,18 +6,18 @@
  */
 
 import type { FC } from 'react';
-import React, { useMemo } from 'react';
+import React, { useMemo, useState } from 'react';
 import type { EuiBasicTableColumn } from '@elastic/eui';
 import { EuiFlexGroup, EuiFlexItem, EuiInMemoryTable, EuiPanel, EuiTitle } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n-react';
 import { convertHighlightedFieldsToTableRow } from '../../shared/utils/highlighted_fields_helpers';
-import { useRuleWithFallback } from '../../../../detection_engine/rule_management/logic/use_rule_with_fallback';
-import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data';
 import { HighlightedFieldsCell } from './highlighted_fields_cell';
 import { CellActions } from '../../shared/components/cell_actions';
 import { HIGHLIGHTED_FIELDS_DETAILS_TEST_ID, HIGHLIGHTED_FIELDS_TITLE_TEST_ID } from './test_ids';
 import { useDocumentDetailsContext } from '../../shared/context';
 import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import { EditHighlightedFieldsButton } from './highlighted_fields_button';
+import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
 
 export interface HighlightedFieldsTableRow {
   /**
@@ -92,13 +92,17 @@ const columns: Array<EuiBasicTableColumn<HighlightedFieldsTableRow>> = [
  * Component that displays the highlighted fields in the right panel under the Investigation section.
  */
 export const HighlightedFields: FC = () => {
-  const { dataFormattedForFieldBrowser, scopeId, isPreview } = useDocumentDetailsContext();
-  const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
-  const { loading, rule: maybeRule } = useRuleWithFallback(ruleId);
+  const { dataFormattedForFieldBrowser, scopeId, isPreview, investigationFields } =
+    useDocumentDetailsContext();
+
+  const [isEditLoading, setIsEditLoading] = useState(false);
+  const editHighlightedFieldsEnabled = useIsExperimentalFeatureEnabled(
+    'editHighlightedFieldsEnabled'
+  );
 
   const highlightedFields = useHighlightedFields({
     dataFormattedForFieldBrowser,
-    investigationFields: maybeRule?.investigation_fields?.field_names ?? [],
+    investigationFields,
   });
   const items = useMemo(
     () => convertHighlightedFieldsToTableRow(highlightedFields, scopeId, isPreview),
@@ -106,16 +110,29 @@ export const HighlightedFields: FC = () => {
   );
 
   return (
-    <EuiFlexGroup direction="column" gutterSize="s">
-      <EuiFlexItem data-test-subj={HIGHLIGHTED_FIELDS_TITLE_TEST_ID}>
-        <EuiTitle size="xxs">
-          <h5>
-            <FormattedMessage
-              id="xpack.securitySolution.flyout.right.investigation.highlightedFields.highlightedFieldsTitle"
-              defaultMessage="Highlighted fields"
-            />
-          </h5>
-        </EuiTitle>
+    <EuiFlexGroup direction="column" gutterSize="none">
+      <EuiFlexItem>
+        <EuiFlexGroup alignItems="center" css={{ minHeight: '40px' }}>
+          <EuiFlexItem data-test-subj={HIGHLIGHTED_FIELDS_TITLE_TEST_ID}>
+            <EuiTitle size="xxs">
+              <h5>
+                <FormattedMessage
+                  id="xpack.securitySolution.flyout.right.investigation.highlightedFields.highlightedFieldsTitle"
+                  defaultMessage="Highlighted fields"
+                />
+              </h5>
+            </EuiTitle>
+          </EuiFlexItem>
+          {editHighlightedFieldsEnabled && (
+            <EuiFlexItem grow={false}>
+              <EditHighlightedFieldsButton
+                customHighlightedFields={investigationFields}
+                dataFormattedForFieldBrowser={dataFormattedForFieldBrowser}
+                setIsEditLoading={setIsEditLoading}
+              />
+            </EuiFlexItem>
+          )}
+        </EuiFlexGroup>
       </EuiFlexItem>
       <EuiFlexItem data-test-subj={HIGHLIGHTED_FIELDS_DETAILS_TEST_ID}>
         <EuiPanel hasBorder hasShadow={false}>
@@ -123,7 +140,7 @@ export const HighlightedFields: FC = () => {
             items={items}
             columns={columns}
             compressed
-            loading={loading}
+            loading={isEditLoading}
             message={
               <FormattedMessage
                 id="xpack.securitySolution.flyout.right.investigation.highlightedFields.noDataDescription"
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.test.tsx
new file mode 100644
index 0000000000000..6bb5286e36b84
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.test.tsx
@@ -0,0 +1,112 @@
+/*
+ * 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 React from 'react';
+import { render, fireEvent } from '@testing-library/react';
+import { DocumentDetailsContext } from '../../shared/context';
+import { TestProviders } from '../../../../common/mock';
+import { EditHighlightedFieldsButton } from './highlighted_fields_button';
+import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_LOADING_TEST_ID,
+} from './test_ids';
+import { mockContextValue } from '../../shared/mocks/mock_context';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { useRuleDetails } from '../../../rule_details/hooks/use_rule_details';
+
+jest.mock(
+  '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message'
+);
+jest.mock('../../../../detection_engine/rule_creation_ui/pages/form');
+jest.mock('../../shared/hooks/use_highlighted_fields_privilege');
+jest.mock('../../../rule_details/hooks/use_rule_details');
+
+const mockSetIsEditLoading = jest.fn();
+const mockCustomHighlightedFields = ['field1', 'field2'];
+const defaultProps = {
+  rule: { id: '123', index: ['index1', 'index2'] } as RuleResponse,
+  customHighlightedFields: mockCustomHighlightedFields,
+  dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
+  setIsEditLoading: mockSetIsEditLoading,
+  isExistingRule: true,
+};
+
+const renderEditHighlighedFieldsButton = (props = defaultProps) =>
+  render(
+    <TestProviders>
+      <DocumentDetailsContext.Provider value={mockContextValue}>
+        <EditHighlightedFieldsButton {...props} />
+      </DocumentDetailsContext.Provider>
+    </TestProviders>
+  );
+
+describe('<EditHighlighedFieldsButton />', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    (useHighlightedFieldsPrivilege as jest.Mock).mockReturnValue({
+      isDisabled: false,
+      tooltipContent: 'tooltip content',
+    });
+    (useRuleIndexPattern as jest.Mock).mockReturnValue({
+      indexPattern: { fields: [{ name: 'field1' }, { name: 'field2' }] },
+      isIndexPatternLoading: false,
+    });
+    (useRuleDetails as jest.Mock).mockReturnValue({
+      rule: { id: '123' } as RuleResponse,
+      isExistingRule: true,
+      loading: false,
+    });
+  });
+
+  it('should render button when user has privilege to edit rule', () => {
+    const { getByTestId } = renderEditHighlighedFieldsButton();
+    expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).toBeInTheDocument();
+    expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).not.toBeDisabled();
+  });
+
+  it('should render disabled button when user does not have privilege to edit a prebuilt rule', () => {
+    (useHighlightedFieldsPrivilege as jest.Mock).mockReturnValue({
+      isDisabled: true,
+      tooltipContent: 'tooltip content',
+    });
+    const { getByTestId } = renderEditHighlighedFieldsButton();
+    expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).toBeInTheDocument();
+    expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID)).toBeDisabled();
+  });
+
+  it('should render modal when button is clicked', () => {
+    const { getByTestId } = renderEditHighlighedFieldsButton();
+
+    const button = getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID);
+    fireEvent.click(button);
+    expect(getByTestId(HIGHLIGHTED_FIELDS_MODAL_TEST_ID)).toBeInTheDocument();
+  });
+
+  it('should render loading spinner when rule is loading', () => {
+    (useRuleDetails as jest.Mock).mockReturnValue({
+      rule: null,
+      isExistingRule: true,
+      loading: true,
+    });
+    const { getByTestId } = renderEditHighlighedFieldsButton();
+    expect(getByTestId(HIGHLIGHTED_FIELDS_EDIT_BUTTON_LOADING_TEST_ID)).toBeInTheDocument();
+  });
+
+  it('should not render button when rule is not found', () => {
+    (useRuleDetails as jest.Mock).mockReturnValue({
+      rule: null,
+      isExistingRule: false,
+      loading: false,
+    });
+    const { container } = renderEditHighlighedFieldsButton();
+    expect(container).toBeEmptyDOMElement();
+  });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
new file mode 100644
index 0000000000000..c54f672e72e99
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
@@ -0,0 +1,95 @@
+/*
+ * 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 React, { useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip, EuiLoadingSpinner } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data';
+import { useRuleDetails } from '../../../rule_details/hooks/use_rule_details';
+import { HighlightedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_LOADING_TEST_ID,
+} from './test_ids';
+
+interface EditHighlightedFieldsButtonProps {
+  /**
+   * Preselected custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlightedFieldsButton: FC<EditHighlightedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+}) => {
+  const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
+  const { rule, isExistingRule, loading: isRuleLoading } = useRuleDetails({ ruleId });
+
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const { isDisabled, tooltipContent } = useHighlightedFieldsPrivilege({
+    rule,
+    isExistingRule,
+  });
+
+  if (isRuleLoading) {
+    return (
+      <EuiLoadingSpinner size="m" data-test-subj={HIGHLIGHTED_FIELDS_EDIT_BUTTON_LOADING_TEST_ID} />
+    );
+  }
+
+  if (!rule) {
+    return null;
+  }
+
+  return (
+    <>
+      <EuiToolTip
+        content={tooltipContent}
+        data-test-subj={HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID}
+      >
+        <EuiButtonEmpty
+          iconType={'gear'}
+          onClick={onClick}
+          disabled={isDisabled}
+          data-test-subj={HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID}
+        >
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsButton"
+            defaultMessage="Edit"
+          />
+        </EuiButtonEmpty>
+      </EuiToolTip>
+      {isModalVisible && (
+        <HighlightedFieldsModal
+          customHighlightedFields={customHighlightedFields}
+          dataFormattedForFieldBrowser={dataFormattedForFieldBrowser}
+          rule={rule}
+          setIsEditLoading={setIsEditLoading}
+          setIsModalVisible={setIsModalVisible}
+        />
+      )}
+    </>
+  );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.test.tsx
new file mode 100644
index 0000000000000..37705ca5bf196
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.test.tsx
@@ -0,0 +1,150 @@
+/*
+ * 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 React from 'react';
+import { fireEvent, render, act } from '@testing-library/react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import { TestProviders } from '../../../../common/mock';
+import { DocumentDetailsContext } from '../../shared/context';
+import { mockContextValue } from '../../shared/mocks/mock_context';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { HighlightedFieldsModal } from './highlighted_fields_modal';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+} from './test_ids';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+
+jest.mock(
+  '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message'
+);
+jest.mock('../../../../detection_engine/rule_creation_ui/pages/form');
+jest.mock('../../../../detection_engine/rule_management/logic/use_update_rule');
+jest.mock('../../shared/hooks/use_highlighted_fields');
+jest.mock('../../../rule_details/hooks/use_rule_details');
+
+const mockAddSuccess = jest.fn();
+jest.mock('../../../../common/hooks/use_app_toasts', () => ({
+  useAppToasts: () => ({
+    addSuccess: mockAddSuccess,
+  }),
+}));
+
+const mockSetIsEditLoading = jest.fn();
+const mockSetIsModalVisible = jest.fn();
+const mockFieldOptions = [{ name: 'field1' }, { name: 'field2' }] as DataViewFieldBase[];
+const mockUpdateRule = jest.fn();
+const mockRule = { id: '123', name: 'test rule' } as RuleResponse;
+
+const defaultProps = {
+  rule: mockRule,
+  customHighlightedFields: [] as string[],
+  dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
+  setIsEditLoading: mockSetIsEditLoading,
+  setIsModalVisible: mockSetIsModalVisible,
+};
+
+const renderHighlighedFieldsModal = (props = defaultProps) =>
+  render(
+    <TestProviders>
+      <DocumentDetailsContext.Provider value={mockContextValue}>
+        <HighlightedFieldsModal {...props} />
+      </DocumentDetailsContext.Provider>
+    </TestProviders>
+  );
+
+describe('<HighlighedFieldsModal />', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    (usePrebuiltRuleCustomizationUpsellingMessage as jest.Mock).mockReturnValue('upsell message');
+    (useRuleIndexPattern as jest.Mock).mockReturnValue({
+      indexPattern: { fields: [{ name: 'option1' }, { name: 'option2' }] },
+      isIndexPatternLoading: false,
+    });
+    (useUpdateRule as jest.Mock).mockReturnValue({
+      mutateAsync: jest.fn(),
+    });
+    (useHighlightedFields as jest.Mock).mockReturnValue({
+      default1: { values: ['test'] },
+      default2: { values: ['test2'] },
+    });
+    (useRuleIndexPattern as jest.Mock).mockReturnValue({
+      indexPattern: { fields: mockFieldOptions },
+      isIndexPatternLoading: false,
+    });
+  });
+
+  it('should render modal without preselected custom fields', async () => {
+    const { getByTestId, queryByTestId } = renderHighlighedFieldsModal();
+
+    expect(getByTestId(HIGHLIGHTED_FIELDS_MODAL_TEST_ID)).toBeInTheDocument();
+
+    const fields = getByTestId(HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID);
+    for (const f of ['default1', 'default2']) {
+      expect(fields).toHaveTextContent(f);
+    }
+
+    expect(getByTestId(HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID)).toBeInTheDocument();
+    expect(queryByTestId('euiComboBoxPill')).not.toBeInTheDocument(); // no preselected custom fields
+  });
+
+  it('should render modal with preselectedcustom fields', () => {
+    const { getByTestId, getAllByTestId } = renderHighlighedFieldsModal({
+      ...defaultProps,
+      customHighlightedFields: ['custom1', 'custom2'],
+    });
+
+    expect(getByTestId(HIGHLIGHTED_FIELDS_MODAL_TEST_ID)).toBeInTheDocument();
+
+    expect(getByTestId(HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID)).toBeInTheDocument();
+    expect(getAllByTestId('euiComboBoxPill')).toHaveLength(2);
+    expect(getAllByTestId('euiComboBoxPill')[0]).toHaveTextContent('custom1');
+    expect(getAllByTestId('euiComboBoxPill')[1]).toHaveTextContent('custom2');
+  });
+
+  it('should close modal when cancel button is clicked', async () => {
+    const { getByTestId } = renderHighlighedFieldsModal();
+    const cancelButton = getByTestId(HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID);
+    await act(async () => {
+      fireEvent.click(cancelButton);
+    });
+    expect(mockSetIsModalVisible).toHaveBeenCalledWith(false);
+  });
+
+  it('should update rule when save button is clicked', async () => {
+    (useUpdateRule as jest.Mock).mockReturnValue({ mutateAsync: mockUpdateRule });
+    mockUpdateRule.mockResolvedValue({
+      name: 'updated rule',
+    } as RuleResponse);
+
+    const { getByTestId } = renderHighlighedFieldsModal({
+      ...defaultProps,
+      customHighlightedFields: ['custom1', 'custom2'],
+    });
+
+    await act(async () => {
+      getByTestId(HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID).click();
+    });
+
+    expect(mockUpdateRule).toHaveBeenCalledWith({
+      name: mockRule.name,
+      investigation_fields: { field_names: ['custom1', 'custom2'] },
+    } as RuleUpdateProps);
+
+    expect(mockAddSuccess).toHaveBeenCalledWith('updated rule was saved');
+    expect(mockUpdateRule).toHaveBeenCalled();
+    expect(mockSetIsEditLoading).toHaveBeenCalledTimes(2);
+    expect(mockSetIsModalVisible).toHaveBeenCalledWith(false);
+  });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
new file mode 100644
index 0000000000000..b6036a5c44e35
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
@@ -0,0 +1,287 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+const formConfig = {
+  ...schema.customHighlightedFields,
+  label: ADD_CUSTOM_FIELD_LABEL,
+};
+
+interface HighlightedFieldsModalProps {
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlightedFieldsModal: FC<HighlightedFieldsModalProps> = ({
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
+
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const options = useMemo(() => {
+    const allFields = dataView.fields;
+    return allFields
+      .filter((field) => !defaultFieldsArray.includes(field.name))
+      .map((field) => ({ label: field.name }));
+  }, [dataView, defaultFieldsArray]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
+    setIsEditLoading(false);
+    setIsModalVisible(false);
+  }, [updateRule, addSuccess, rule, setIsModalVisible, setIsEditLoading, selectedOptions]);
+
+  const componentProps = useMemo(
+    () => ({
+      idAria: 'customizeHighlightedFields',
+      'data-test-subj': HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+      euiFieldProps: {
+        fullWidth: true,
+        noSuggestions: false,
+        onChange: (fields: Array<{ label: string }>) => setSelectedOptions(fields),
+        options,
+        placeholder: SELECT_PLACEHOLDER,
+        selectedOptions,
+      },
+    }),
+    [options, selectedOptions]
+  );
+
+  return (
+    <EuiModal
+      css={{ width: '600px' }}
+      data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TEST_ID}
+      id={modalTitleId}
+      onClose={onCancel}
+    >
+      <EuiModalHeader data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID}>
+        <EuiModalHeaderTitle id={modalTitleId} size="s">
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalTitle"
+            defaultMessage="Edit highlighted fields"
+          />
+        </EuiModalHeaderTitle>
+      </EuiModalHeader>
+      <EuiModalBody>
+        <EuiText size="s" data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID}>
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDescription"
+            defaultMessage="Changes made here will be applied to the {ruleName} rule. Any custom fields you add will be displayed in all alerts generated by this rule."
+            values={{ ruleName: <b>{rule.name}</b> }}
+          />
+        </EuiText>
+        <EuiSpacer size="m" />
+        <EuiText
+          size="xs"
+          css={css`
+            font-weight: ${euiTheme.font.weight.semiBold};
+          `}
+        >
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDefaultFieldsTitle"
+            defaultMessage="Default"
+          />
+        </EuiText>
+        <EuiSpacer size="s" />
+        <EuiFlexGroup
+          data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID}
+          gutterSize="xs"
+          justifyContent="flexStart"
+          wrap
+        >
+          {defaultFieldsArray.map((field: string) => (
+            <EuiFlexItem key={field} grow={false}>
+              <EuiBadge color="hollow">{field}</EuiBadge>
+            </EuiFlexItem>
+          ))}
+        </EuiFlexGroup>
+        <EuiSpacer size="l" />
+        <Form form={form}>
+          <CommonUseField
+            path="customHighlightedFields"
+            config={formConfig}
+            componentProps={componentProps}
+          />
+        </Form>
+      </EuiModalBody>
+      <EuiModalFooter>
+        <EuiFlexGroup justifyContent="flexEnd">
+          <EuiFlexItem grow={false}>
+            <EuiButtonEmpty
+              data-test-subj={HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID}
+              flush="left"
+              onClick={onCancel}
+            >
+              <FormattedMessage
+                id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalCancelButton"
+                defaultMessage="Cancel"
+              />
+            </EuiButtonEmpty>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiButton
+              data-test-subj={HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID}
+              fill
+              onClick={onSubmit}
+            >
+              <FormattedMessage
+                id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSaveButton"
+                defaultMessage="Save"
+              />
+            </EuiButton>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiModalFooter>
+    </EuiModal>
+  );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/investigation_section.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/investigation_section.test.tsx
index 9a25043abbdf4..60f0ae5efcfd0 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/investigation_section.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/investigation_section.test.tsx
@@ -23,11 +23,20 @@ import { mockContextValue } from '../../shared/mocks/mock_context';
 import { useExpandSection } from '../hooks/use_expand_section';
 import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
 import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
+import { useFetchIndex } from '../../../../common/containers/source';
 
 jest.mock('../../../../detection_engine/rule_management/logic/use_rule_with_fallback');
 jest.mock('../hooks/use_expand_section');
 jest.mock('../../shared/hooks/use_highlighted_fields');
 jest.mock('../../../../common/hooks/use_experimental_features');
+jest.mock('../../../../common/containers/source');
+
+const mockAddSuccess = jest.fn();
+jest.mock('../../../../common/hooks/use_app_toasts', () => ({
+  useAppToasts: () => ({
+    addSuccess: mockAddSuccess,
+  }),
+}));
 
 const panelContextValue = {
   ...mockContextValue,
@@ -52,6 +61,7 @@ describe('<InvestigationSection />', () => {
     jest.clearAllMocks();
     (useRuleWithFallback as jest.Mock).mockReturnValue({ rule: { note: 'test note' } });
     (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
+    (useFetchIndex as jest.Mock).mockReturnValue([false, { indexPatterns: { fields: ['field'] } }]);
   });
 
   it('should render investigation component', () => {
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
index c73c41062f4f8..779f11326a2e4 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
@@ -101,6 +101,26 @@ export const HIGHLIGHTED_FIELDS_LINKED_CELL_TEST_ID =
 export const HIGHLIGHTED_FIELDS_AGENT_STATUS_CELL_TEST_ID =
   `${HIGHLIGHTED_FIELDS_TEST_ID}AgentStatusCell` as const;
 
+export const HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_TEST_ID}EditButton` as const;
+export const HIGHLIGHTED_FIELDS_EDIT_BUTTON_LOADING_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID}Loading` as const;
+export const HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID}Tooltip` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_TEST_ID = `${HIGHLIGHTED_FIELDS_TEST_ID}Modal` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}Title` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}Description` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}DefaultFields` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}CustomFields` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}SaveButton` as const;
+export const HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID =
+  `${HIGHLIGHTED_FIELDS_MODAL_TEST_ID}CancelButton` as const;
+
 /* Insights section */
 
 export const INSIGHTS_TEST_ID = `${PREFIX}Insights` as const;
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/context.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/context.tsx
index 72da35f9286b2..842df3497fecb 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/context.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/context.tsx
@@ -138,19 +138,19 @@ export const DocumentDetailsProvider = memo(
             }
           : undefined,
       [
-        id,
-        indexName,
-        scopeId,
+        browserFields,
         dataAsNestedObject,
         dataFormattedForFieldBrowser,
-        searchHit,
-        browserFields,
-        maybeRule?.investigation_fields?.field_names,
-        refetchFlyoutData,
         getFieldsData,
+        id,
+        indexName,
         isPreviewMode,
-        jumpToEntityId,
         jumpToCursor,
+        jumpToEntityId,
+        refetchFlyoutData,
+        scopeId,
+        searchHit,
+        maybeRule,
       ]
     );
 
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.test.tsx
index 6bffb7c58ae3f..337fc6038e5d1 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.test.tsx
@@ -21,7 +21,12 @@ const dataFormattedForFieldBrowser = mockDataFormattedForFieldBrowser;
 
 describe('useHighlightedFields', () => {
   it('should return data', () => {
-    const hookResult = renderHook(() => useHighlightedFields({ dataFormattedForFieldBrowser }));
+    const hookResult = renderHook(() =>
+      useHighlightedFields({
+        dataFormattedForFieldBrowser,
+        investigationFields: [],
+      })
+    );
     expect(hookResult.result.current).toEqual({
       'host.name': {
         values: ['host-name'],
@@ -39,6 +44,7 @@ describe('useHighlightedFields', () => {
     const hookResult = renderHook(() =>
       useHighlightedFields({
         dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowserWithOverridenField,
+        investigationFields: [],
       })
     );
 
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
index 571e01b9a2e22..7ded63adec2c3 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
@@ -12,7 +12,7 @@ import { useAlertResponseActionsSupport } from '../../../../common/hooks/endpoin
 import { isResponseActionsAlertAgentIdField } from '../../../../common/lib/endpoint';
 import {
   getEventCategoriesFromData,
-  getEventFieldsToDisplay,
+  getHighlightedFieldsToDisplay,
 } from '../../../../common/components/event_details/get_alert_summary_rows';
 
 export interface UseHighlightedFieldsParams {
@@ -23,7 +23,14 @@ export interface UseHighlightedFieldsParams {
   /**
    * An array of fields user has selected to highlight, defined on rule
    */
-  investigationFields?: string[];
+  investigationFields: string[];
+  /**
+   * Optional prop to specify the type of highlighted fields to display
+   * Custom: fields defined on the rule
+   * Default: fields defined by elastic
+   * All: both custom and default fields
+   */
+  type?: 'default' | 'custom' | 'all';
 }
 
 export interface UseHighlightedFieldsResult {
@@ -45,6 +52,7 @@ export interface UseHighlightedFieldsResult {
 export const useHighlightedFields = ({
   dataFormattedForFieldBrowser,
   investigationFields,
+  type,
 }: UseHighlightedFieldsParams): UseHighlightedFieldsResult => {
   const responseActionsSupport = useAlertResponseActionsSupport(dataFormattedForFieldBrowser);
   const eventCategories = getEventCategoriesFromData(dataFormattedForFieldBrowser);
@@ -67,11 +75,12 @@ export const useHighlightedFields = ({
     ? eventRuleTypeField?.originalValue?.[0]
     : eventRuleTypeField?.originalValue;
 
-  const tableFields = getEventFieldsToDisplay({
+  const tableFields = getHighlightedFieldsToDisplay({
     eventCategories,
     eventCode,
     eventRuleType,
-    highlightedFieldsOverride: investigationFields ?? [],
+    ruleCustomHighlightedFields: investigationFields,
+    type,
   });
 
   return tableFields.reduce<UseHighlightedFieldsResult>((acc, field) => {
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.test.tsx
new file mode 100644
index 0000000000000..bfa299a6e19ab
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.test.tsx
@@ -0,0 +1,138 @@
+/*
+ * 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 { renderHook } from '@testing-library/react';
+import { useHighlightedFieldsPrivilege } from './use_highlighted_fields_privilege';
+import type { UseHighlightedFieldsPrivilegeParams } from './use_highlighted_fields_privilege';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import {
+  LACK_OF_KIBANA_SECURITY_PRIVILEGES,
+  ML_RULES_DISABLED_MESSAGE,
+} from '../../../../detection_engine/common/translations';
+import { useUserData } from '../../../../detections/components/user_info';
+
+jest.mock('../../../../common/components/ml/hooks/use_ml_capabilities');
+jest.mock(
+  '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message'
+);
+jest.mock('../../../../../common/machine_learning/has_ml_license');
+jest.mock('../../../../../common/machine_learning/has_ml_admin_permissions');
+jest.mock('../../../../detections/components/user_info');
+
+const defaultProps = {
+  rule: {} as RuleResponse,
+  isExistingRule: true,
+};
+
+const renderUseHighlightedFieldsPrivilege = (props: UseHighlightedFieldsPrivilegeParams) =>
+  renderHook(() => useHighlightedFieldsPrivilege(props));
+
+describe('useHighlightedFieldsPrivilege', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    (useUserData as jest.Mock).mockReturnValue([{ canUserCRUD: true }]);
+    (hasMlAdminPermissions as jest.Mock).mockReturnValue(false);
+    (hasMlLicense as jest.Mock).mockReturnValue(false);
+    (usePrebuiltRuleCustomizationUpsellingMessage as jest.Mock).mockReturnValue(undefined);
+  });
+
+  it('should return isDisabled as true when rule is null', () => {
+    const { result } = renderUseHighlightedFieldsPrivilege({
+      ...defaultProps,
+      rule: null,
+    });
+
+    expect(result.current.isDisabled).toBe(true);
+    expect(result.current.tooltipContent).toBe('Deleted rule cannot be edited.');
+  });
+
+  it('should return isDisabled as true when rule does not exist', () => {
+    const { result } = renderUseHighlightedFieldsPrivilege({
+      ...defaultProps,
+      isExistingRule: false,
+    });
+
+    expect(result.current.isDisabled).toBe(true);
+    expect(result.current.tooltipContent).toBe('Deleted rule cannot be edited.');
+  });
+
+  it('should return isDisabled as true when user does not have CRUD privileges', () => {
+    (useUserData as jest.Mock).mockReturnValue([{ canUserCRUD: false }]);
+    const { result } = renderUseHighlightedFieldsPrivilege(defaultProps);
+    expect(result.current.isDisabled).toBe(true);
+    expect(result.current.tooltipContent).toContain(LACK_OF_KIBANA_SECURITY_PRIVILEGES);
+  });
+
+  describe('when rule is machine learning rule', () => {
+    it('should return isDisabled as true when user does not have ml permissions', () => {
+      (hasMlAdminPermissions as jest.Mock).mockReturnValue(false);
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'machine_learning' } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(true);
+      expect(result.current.tooltipContent).toContain(ML_RULES_DISABLED_MESSAGE);
+    });
+
+    it('should return isDisabled as true when user does not have ml license', () => {
+      (hasMlLicense as jest.Mock).mockReturnValue(false);
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'machine_learning' } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(true);
+      expect(result.current.tooltipContent).toContain(ML_RULES_DISABLED_MESSAGE);
+    });
+
+    it('should return isDisabled as false when user has ml permissions and proper license', () => {
+      (hasMlAdminPermissions as jest.Mock).mockReturnValue(true);
+      (hasMlLicense as jest.Mock).mockReturnValue(true);
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'machine_learning' } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(false);
+      expect(result.current.tooltipContent).toBe('Edit highlighted fields');
+    });
+  });
+
+  describe('when rule is not machine learning rule', () => {
+    it('should return isDisabled as false when rule is not immutable (custom rule)', () => {
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'query', immutable: false } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(false);
+      expect(result.current.tooltipContent).toBe('Edit highlighted fields');
+    });
+
+    it('should return isDisabled as false when rule is immutable (prebuilt rule) and upselling message is undefined', () => {
+      (usePrebuiltRuleCustomizationUpsellingMessage as jest.Mock).mockReturnValue(undefined);
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'query', immutable: true } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(false);
+      expect(result.current.tooltipContent).toContain('Edit highlighted fields');
+    });
+
+    it('should return isDisabled as true when rule is immutable (prebuilt rule) and upselling message is available', () => {
+      (usePrebuiltRuleCustomizationUpsellingMessage as jest.Mock).mockReturnValue(
+        'upselling message'
+      );
+      const { result } = renderUseHighlightedFieldsPrivilege({
+        ...defaultProps,
+        rule: { type: 'query', immutable: true } as RuleResponse,
+      });
+      expect(result.current.isDisabled).toBe(true);
+      expect(result.current.tooltipContent).toContain('upselling message');
+    });
+  });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
new file mode 100644
index 0000000000000..1f9422c8da7ec
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
@@ -0,0 +1,106 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+export interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse | null;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !rule ||
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule?.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
+    if (isEditRuleDisabled && (!isExistingRule || !rule)) {
+      return i18n.translate(
+        'xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsDeletedRuleTooltip',
+        { defaultMessage: 'Deleted rule cannot be edited.' }
+      );
+    }
+    if (upsellingMessage && rule?.immutable) {
+      return i18n.translate(
+        'xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsButtonUpsellingTooltip',
+        {
+          defaultMessage: '{upsellingMessage}',
+          values: { upsellingMessage },
+        }
+      );
+    }
+    return i18n.translate(
+      'xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsButtonTooltip',
+      { defaultMessage: 'Edit highlighted fields' }
+    );
+  }, [canUserCRUD, hasMlPermissions, isEditRuleDisabled, isExistingRule, rule, upsellingMessage]);
+
+  return useMemo(
+    () => ({
+      isDisabled,
+      tooltipContent,
+    }),
+    [isDisabled, tooltipContent]
+  );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_prevalence.ts b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_prevalence.ts
index 8811c91a0b34d..cf4e6416235ad 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_prevalence.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_prevalence.ts
@@ -41,7 +41,7 @@ export interface UsePrevalenceParams {
   /**
    * User defined fields to highlight (defined on the rule)
    */
-  investigationFields?: string[];
+  investigationFields: string[];
 }
 
 export interface UsePrevalenceResult {


Reviews:
user: login: lgestc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T14:57:57Z
user: login: PhilippeOberti
body: Desk tested and it look amazing! I left a few comments in the code, happy to discuss if some don't make sense!
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T17:52:30Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:15:20Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:15:32Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:15:50Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:17:38Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:21:45Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T01:49:58Z
user: login: PhilippeOberti
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T04:02:24Z
user: login: PhilippeOberti
body: Left one more comment/question I had missed in my first review, sorry!
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T04:23:26Z
user: login: christineweng
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T18:28:18Z
user: login: PhilippeOberti
body: Thank you for making all the changes, the functionality is awesome, users are going to love it! Great work on the UI, the code is super clean and thanks for adding plenty of unit tests as well!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T20:59:04Z
user: login: dhurley14
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T16:03:05Z

Review comments:
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: lgestc
body: typo:)
created_at: 2025-04-03T14:57:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: PhilippeOberti
body: We're having a discussion offline right now to see if there is a value in concatenating the alert index pattern here. Currently we cannot add any highlighted fields of type `kibana.xxx` which could be a bit limiting?

We're discussing the feasibility as well as if we want to do this here, should we do it as well on the rule details page. It could be confusing to user that more fields appear here but when they go edit the rule they can't add them...
created_at: 2025-04-03T16:31:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: PhilippeOberti
body: One more thing to think about, is that feasible if a user creates her/his very first rule. By that I mean do we have the alert index created? Is that a concern, when thinking about this logic in the rule creation workflow?
created_at: 2025-04-03T17:08:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -253,27 +253,36 @@ function getHighlightedFieldsOverride(customs: string[]): EventSummaryField[] {
 /**
  * Assembles a list of fields to display based on the event
  */
-export function getEventFieldsToDisplay({
+export function getHighlightedFieldsToDisplay({
path: x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
user: login: PhilippeOberti
body: I know we don't have any right now, but as we're modifying this function, would you be ok writing unit tests for it?
Especially because it's a function used in multiple places...
created_at: 2025-04-03T17:12:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -92,38 +92,63 @@ const columns: Array<EuiBasicTableColumn<HighlightedFieldsTableRow>> = [
  * Component that displays the highlighted fields in the right panel under the Investigation section.
  */
 export const HighlightedFields: FC = () => {
-  const { dataFormattedForFieldBrowser, scopeId, isPreview } = useDocumentDetailsContext();
-  const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
-  const { loading, rule: maybeRule } = useRuleWithFallback(ruleId);
+  const {
+    dataFormattedForFieldBrowser,
+    scopeId,
+    isPreview,
+    investigationFields,
+    rule,
+    isExistingRule,
+  } = useDocumentDetailsContext();
+  const [isEditLoading, setIsEditLoading] = useState(false);
+  const editHighlightedFieldsEnabled = useIsExperimentalFeatureEnabled(
+    'editHighlightedFieldsEnabled'
+  );
 
   const highlightedFields = useHighlightedFields({
     dataFormattedForFieldBrowser,
-    investigationFields: maybeRule?.investigation_fields?.field_names ?? [],
+    investigationFields,
+    type: 'all',
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx
user: login: PhilippeOberti
body: Minor thing but you don't need `type: 'all'` here as it's the default value in your hook
created_at: 2025-04-03T17:16:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: PhilippeOberti
body: are these default highlighted fields or custom highlighted fields? 😝 
created_at: 2025-04-03T17:18:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: PhilippeOberti
body: Would you be ok improving this documentation? It confused me at first. Then I looked at where we get that information from and this is what I found from the original hook returning the data

```
We try to fetch the rule first. If the request fails with 404, that could mean that the rule was deleted.
In that case, try to fetch the latest alert generated by the rule and retrieve the rule data from the alert (fallback).
```

So if I understand correctly, we could have a rule data but the rule could have been deleted, correct?
created_at: 2025-04-03T17:21:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: PhilippeOberti
body: Hmmmm 🤔 I think something does not add up here...

If we have the `rule` prop defined, but the `isExistingRule` set to `false` because it was removed, then could we get into a situation where `isEditRuleDisabled` could be `true`, but we wouldn't have any `explanation`?
created_at: 2025-04-03T17:31:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: PhilippeOberti
body: Also I just tried to delete a rule after having generated alerts, then go to the flyout, and the `useRuleWithFallback` hook returned me
```typescript
{
  rule: null;
  existingRule: false;
}
```

I guess this is just the case on local maybe, and there could be scenarios where while we have `existingRule: false` we still are able to retrieve the rules infos...
created_at: 2025-04-03T17:33:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: PhilippeOberti
body: I guess what I'm trying to say is, is there a way to simplify this whole logic but not necessarily needing both `rule` and `existingRule` props in our top level context... But maybe not 😆 
created_at: 2025-04-03T17:34:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,6 +52,7 @@ export interface UseHighlightedFieldsResult {
 export const useHighlightedFields = ({
   dataFormattedForFieldBrowser,
   investigationFields,
+  type = 'all',
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
user: login: PhilippeOberti
body: You might not have to have a default value here, as the only place where you use `type` in this hook is where you call `getHighlightedFieldsToDisplay` and the value is already defaulted to `all` there...
created_at: 2025-04-03T17:36:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
+    if (upsellingMessage && rule.immutable) {
+      return i18n.translate(
+        'xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsButtonUpsellingTooltip',
+        {
+          defaultMessage: '{upsellingMessage}',
+          values: { upsellingMessage },
+        }
+      );
+    }
+    return i18n.translate(
+      'xpack.securitySolution.flyout.right.investigation.highlightedFields.editHighlightedFieldsButtonTooltip',
+      { defaultMessage: 'Edit highlighted fields' }
+    );
+  }, [canUserCRUD, hasMlPermissions, isEditRuleDisabled, rule, upsellingMessage]);
+
+  return {
+    isDisabled,
+    tooltipContent,
+  };
+};
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: PhilippeOberti
body: Might be overkill but this could me memoized
created_at: 2025-04-03T17:39:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
+  fieldOptions,
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
+  );
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
+    setIsEditLoading(false);
+    setIsModalVisible(false);
+  }, [updateRule, addSuccess, rule, setIsModalVisible, setIsEditLoading, selectedOptions]);
+
+  return (
+    <EuiModal
+      css={{ width: '600px' }}
+      data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TEST_ID}
+      id={modalTitleId}
+      onClose={onCancel}
+    >
+      <EuiModalHeader data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID}>
+        <EuiModalHeaderTitle id={modalTitleId} size="s">
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalTitle"
+            defaultMessage="Edit highlighted fields"
+          />
+        </EuiModalHeaderTitle>
+      </EuiModalHeader>
+      <EuiModalBody>
+        <EuiText size="s" data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID}>
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDescription"
+            defaultMessage="Changes made here will be applied to the {ruleName} rule. Any custom fields you add will be displayed in all alerts generated by this rule."
+            values={{ ruleName: <b>{rule.name}</b> }}
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: PhilippeOberti
body: Nice touch to bold the rule name. When you were presenting your WIP work earlier I felt it was missing. This looks much better!
created_at: 2025-04-03T17:46:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
+  fieldOptions,
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
+  );
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
+    setIsEditLoading(false);
+    setIsModalVisible(false);
+  }, [updateRule, addSuccess, rule, setIsModalVisible, setIsEditLoading, selectedOptions]);
+
+  return (
+    <EuiModal
+      css={{ width: '600px' }}
+      data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TEST_ID}
+      id={modalTitleId}
+      onClose={onCancel}
+    >
+      <EuiModalHeader data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID}>
+        <EuiModalHeaderTitle id={modalTitleId} size="s">
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalTitle"
+            defaultMessage="Edit highlighted fields"
+          />
+        </EuiModalHeaderTitle>
+      </EuiModalHeader>
+      <EuiModalBody>
+        <EuiText size="s" data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID}>
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDescription"
+            defaultMessage="Changes made here will be applied to the {ruleName} rule. Any custom fields you add will be displayed in all alerts generated by this rule."
+            values={{ ruleName: <b>{rule.name}</b> }}
+          />
+        </EuiText>
+        <EuiSpacer size="m" />
+        <EuiText
+          size="xs"
+          css={css`
+            font-weight: ${euiTheme.font.weight.semiBold};
+          `}
+        >
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDefaultFieldsTitle"
+            defaultMessage="Default"
+          />
+        </EuiText>
+        <EuiSpacer size="s" />
+        <EuiFlexGroup
+          data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID}
+          gutterSize="xs"
+          justifyContent="flexStart"
+          wrap
+        >
+          {defaultFieldsArray.map((field: string) => (
+            <EuiFlexItem key={field} grow={false}>
+              <EuiBadge color="hollow">{field}</EuiBadge>
+            </EuiFlexItem>
+          ))}
+        </EuiFlexGroup>
+        <EuiSpacer size="l" />
+        <Form form={form}>
+          <CommonUseField
+            path="customHighlightedFields"
+            config={{ ...schema.customHighlightedFields, label: ADD_CUSTOM_FIELD_LABEL }}
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: PhilippeOberti
body: This could be extracted into a constant outside of the component to avoid creating a new object every time
```typescript
{ ...schema.customHighlightedFields, label: ADD_CUSTOM_FIELD_LABEL }
```
created_at: 2025-04-03T17:47:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
+  fieldOptions,
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
+  );
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
+    setIsEditLoading(false);
+    setIsModalVisible(false);
+  }, [updateRule, addSuccess, rule, setIsModalVisible, setIsEditLoading, selectedOptions]);
+
+  return (
+    <EuiModal
+      css={{ width: '600px' }}
+      data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TEST_ID}
+      id={modalTitleId}
+      onClose={onCancel}
+    >
+      <EuiModalHeader data-test-subj={HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID}>
+        <EuiModalHeaderTitle id={modalTitleId} size="s">
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalTitle"
+            defaultMessage="Edit highlighted fields"
+          />
+        </EuiModalHeaderTitle>
+      </EuiModalHeader>
+      <EuiModalBody>
+        <EuiText size="s" data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID}>
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDescription"
+            defaultMessage="Changes made here will be applied to the {ruleName} rule. Any custom fields you add will be displayed in all alerts generated by this rule."
+            values={{ ruleName: <b>{rule.name}</b> }}
+          />
+        </EuiText>
+        <EuiSpacer size="m" />
+        <EuiText
+          size="xs"
+          css={css`
+            font-weight: ${euiTheme.font.weight.semiBold};
+          `}
+        >
+          <FormattedMessage
+            id="xpack.securitySolution.flyout.right.investigation.highlightedFields.modalDefaultFieldsTitle"
+            defaultMessage="Default"
+          />
+        </EuiText>
+        <EuiSpacer size="s" />
+        <EuiFlexGroup
+          data-test-subj={HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID}
+          gutterSize="xs"
+          justifyContent="flexStart"
+          wrap
+        >
+          {defaultFieldsArray.map((field: string) => (
+            <EuiFlexItem key={field} grow={false}>
+              <EuiBadge color="hollow">{field}</EuiBadge>
+            </EuiFlexItem>
+          ))}
+        </EuiFlexGroup>
+        <EuiSpacer size="l" />
+        <Form form={form}>
+          <CommonUseField
+            path="customHighlightedFields"
+            config={{ ...schema.customHighlightedFields, label: ADD_CUSTOM_FIELD_LABEL }}
+            componentProps={{
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: PhilippeOberti
body: Memoize this or overkill?
created_at: 2025-04-03T17:49:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
+  fieldOptions,
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
+  );
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: PhilippeOberti
body: Shouldn't we handle a failure here?
created_at: 2025-04-03T17:51:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: christineweng
body: Thanks! I have since uncovered a bug: for some reason when fetching the rule in context, it does not update if the rule is deleted. I have since moved back the fetch rule logic in highlighted fields, hence the rule related contexts are removed. 

Once that bug is fixed, the scenario you mentioned do surface itself, I added another if logic to account for the tooltip

created_at: 2025-04-04T01:15:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,6 +52,7 @@ export interface UseHighlightedFieldsResult {
 export const useHighlightedFields = ({
   dataFormattedForFieldBrowser,
   investigationFields,
+  type = 'all',
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
user: login: christineweng
body: Does this contradict with the earlier comment about removing the `all`?  https://github.com/elastic/kibana/pull/216740/files#r2027440880
created_at: 2025-04-04T01:15:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,270 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+interface HighlighedFieldsModalProps {
+  /**
+   * Available fields to select from
+   */
+  fieldOptions: DataViewFieldBase[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlighedFieldsModal: FC<HighlighedFieldsModalProps> = ({
+  fieldOptions,
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
+  );
+
+  const { form } = useForm({
+    defaultValue: { customHighlightedFields: [] },
+    schema,
+  });
+
+  const onCancel = useCallback(() => {
+    setIsModalVisible(false);
+  }, [setIsModalVisible]);
+
+  const onSubmit = useCallback(async () => {
+    setIsEditLoading(true);
+
+    const updatedRule = await updateRule({
+      ...rule,
+      id: undefined,
+      investigation_fields:
+        selectedOptions.length > 0
+          ? { field_names: selectedOptions.map((option) => option.label) }
+          : undefined,
+    } as RuleUpdateProps);
+
+    addSuccess(SUCCESSFULLY_SAVED_RULE(updatedRule?.name ?? 'rule'));
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: christineweng
body: failure is handled in the hook https://github.com/elastic/kibana/blob/main/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management/logic/use_update_rule.ts#L12
created_at: 2025-04-04T01:15:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -253,27 +253,36 @@ function getHighlightedFieldsOverride(customs: string[]): EventSummaryField[] {
 /**
  * Assembles a list of fields to display based on the event
  */
-export function getEventFieldsToDisplay({
+export function getHighlightedFieldsToDisplay({
path: x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
user: login: christineweng
body: Hey! I added a file that covers custom fields and always displayed fields. I didn't go into each sub functions (events, rule type) because I think then it's just copy and pasting the entire logic. Let me know what you think!
created_at: 2025-04-04T01:17:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: christineweng
body: Looking at some of the `kibana.alert` fields, they are more about metadata of the rule/parameters when creating a rule. Not sure there is much value in showing them in highlighted fields? User can go to rule summary and get a better experience

Another drawback is that the values are populated at alert generation time, so if the rule description was changed, the value in the alert does not change, and we are showing outdated info

<img width="565" alt="image" src="https://github.com/user-attachments/assets/4ca722ae-1f56-4e9c-b95b-e02c4c5075af" />

created_at: 2025-04-04T01:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: christineweng
body: actually we do show some `kibana.alert` fields (rule description too lol)
<img width="447" alt="image" src="https://github.com/user-attachments/assets/2e45bf82-9d24-4d38-9848-0e5d32ae0f0c" />

created_at: 2025-04-04T01:49:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -253,27 +253,36 @@ function getHighlightedFieldsOverride(customs: string[]): EventSummaryField[] {
 /**
  * Assembles a list of fields to display based on the event
  */
-export function getEventFieldsToDisplay({
+export function getHighlightedFieldsToDisplay({
path: x-pack/solutions/security/plugins/security_solution/public/common/components/event_details/get_alert_summary_rows.tsx
user: login: PhilippeOberti
body: Perfect, thank you!
created_at: 2025-04-04T04:02:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -92,38 +92,61 @@ const columns: Array<EuiBasicTableColumn<HighlightedFieldsTableRow>> = [
  * Component that displays the highlighted fields in the right panel under the Investigation section.
  */
 export const HighlightedFields: FC = () => {
-  const { dataFormattedForFieldBrowser, scopeId, isPreview } = useDocumentDetailsContext();
-  const { ruleId } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
-  const { loading, rule: maybeRule } = useRuleWithFallback(ruleId);
+  const {
+    dataFormattedForFieldBrowser,
+    scopeId,
+    isPreview,
+    investigationFields,
+    // rule,
+    // isExistingRule,
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx
user: login: PhilippeOberti
body: remove commented code
created_at: 2025-04-04T04:02:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: PhilippeOberti
body: Like we discussed on Slack, we can keep the logic you have right now and we can revisit that later. Could you create a ticket for it and link it here?
created_at: 2025-04-04T04:15:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+/*
+ * 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 { useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
+import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
+import {
+  explainLackOfPermission,
+  hasUserCRUDPermission,
+} from '../../../../common/utils/privileges';
+import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
+import { useUserData } from '../../../../detections/components/user_info';
+import { isMlRule } from '../../../../../common/machine_learning/helpers';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+
+interface UseHighlightedFieldsPrivilegeParams {
+  /**
+   * The rule to be edited
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+}
+
+interface UseHighlightedFieldsPrivilegeResult {
+  /**
+   * Whether edit highlighted fields button is disabled
+   */
+  isDisabled: boolean;
+  /**
+   * The tooltip content
+   */
+  tooltipContent: string;
+}
+
+/**
+ * Returns whether the edit highlighted fields button is disabled and the tooltip content
+ */
+export const useHighlightedFieldsPrivilege = ({
+  rule,
+  isExistingRule,
+}: UseHighlightedFieldsPrivilegeParams): UseHighlightedFieldsPrivilegeResult => {
+  const [{ canUserCRUD }] = useUserData();
+  const mlCapabilities = useMlCapabilities();
+  const hasMlPermissions = hasMlLicense(mlCapabilities) && hasMlAdminPermissions(mlCapabilities);
+
+  const isEditRuleDisabled =
+    !isExistingRule ||
+    !hasUserCRUDPermission(canUserCRUD) ||
+    (isMlRule(rule?.type) && !hasMlPermissions);
+
+  const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
+    'prebuilt_rule_customization'
+  );
+
+  const isDisabled = isEditRuleDisabled || (Boolean(upsellingMessage) && rule.immutable);
+
+  const tooltipContent = useMemo(() => {
+    const explanation = explainLackOfPermission(
+      rule,
+      hasMlPermissions,
+      true, // default true because we don't need the message for lack of action privileges
+      canUserCRUD
+    );
+
+    if (isEditRuleDisabled && explanation) {
+      return explanation;
+    }
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields_privilege.tsx
user: login: PhilippeOberti
body: Very nice, I like the loading then the disabled button with tooltip. Awesome!
created_at: 2025-04-04T04:17:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,6 +52,7 @@ export interface UseHighlightedFieldsResult {
 export const useHighlightedFields = ({
   dataFormattedForFieldBrowser,
   investigationFields,
+  type = 'all',
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/hooks/use_highlighted_fields.ts
user: login: PhilippeOberti
body: Yes sorry! I think it should be safe to remove the most level one, which is `useHighlightedFields` (and not `getHighlightedFieldsToDisplay`). Makes sense?
created_at: 2025-04-04T04:19:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,286 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiText,
+  EuiSpacer,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  useGeneratedHtmlId,
+  EuiBadge,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import type { RuleResponse, RuleUpdateProps } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { useUpdateRule } from '../../../../detection_engine/rule_management/logic/use_update_rule';
+import {
+  Form,
+  Field,
+  getUseField,
+  useForm,
+  FIELD_TYPES,
+  fieldValidators,
+} from '../../../../shared_imports';
+import type { FormSchema } from '../../../../shared_imports';
+import { useHighlightedFields } from '../../shared/hooks/use_highlighted_fields';
+import {
+  HIGHLIGHTED_FIELDS_MODAL_CANCEL_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DESCRIPTION_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_SAVE_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_DEFAULT_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_CUSTOM_FIELDS_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TITLE_TEST_ID,
+  HIGHLIGHTED_FIELDS_MODAL_TEST_ID,
+} from './test_ids';
+
+const SUCCESSFULLY_SAVED_RULE = (ruleName: string) =>
+  i18n.translate('xpack.securitySolution.detectionEngine.rules.update.successfullySavedRuleTitle', {
+    values: { ruleName },
+    defaultMessage: '{ruleName} was saved',
+  });
+
+const ADD_CUSTOM_FIELD_LABEL = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalAddCustomFieldLabel',
+  { defaultMessage: 'Add custom' }
+);
+
+const SELECT_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.flyout.right.investigation.highlightedFields.modalSelectPlaceholder',
+  { defaultMessage: 'Select or search for options' }
+);
+
+const CommonUseField = getUseField({ component: Field });
+
+interface InvestigationFieldsFormData {
+  customHighlightedFields: string[];
+}
+
+const schema: FormSchema<InvestigationFieldsFormData> = {
+  customHighlightedFields: {
+    fieldsToValidateOnChange: ['customHighlightedFields'],
+    type: FIELD_TYPES.COMBO_BOX,
+    validations: [{ validator: fieldValidators.emptyField('error') }],
+  },
+};
+
+const formConfig = {
+  ...schema.customHighlightedFields,
+  label: ADD_CUSTOM_FIELD_LABEL,
+};
+
+interface HighlightedFieldsModalProps {
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * The custom highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+  /**
+   * The function to set the modal visible state
+   */
+  setIsModalVisible: (isModalVisible: boolean) => void;
+}
+
+/**
+ * Modal for editing the highlighted fields of a rule.
+ */
+export const HighlightedFieldsModal: FC<HighlightedFieldsModalProps> = ({
+  rule,
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  setIsEditLoading,
+  setIsModalVisible,
+}) => {
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
+  const fieldOptions = useMemo(() => dataView.fields, [dataView]);
+
+  const { addSuccess } = useAppToasts();
+  const { euiTheme } = useEuiTheme();
+  const { mutateAsync: updateRule } = useUpdateRule();
+  const modalTitleId = useGeneratedHtmlId();
+
+  const defaultFields = useHighlightedFields({
+    dataFormattedForFieldBrowser,
+    investigationFields: customHighlightedFields,
+    type: 'default',
+  });
+  const defaultFieldsArray = useMemo(() => Object.keys(defaultFields), [defaultFields]);
+
+  const customFields = useMemo(
+    () => customHighlightedFields.map((field: string) => ({ label: field })),
+    [customHighlightedFields]
+  );
+
+  const [selectedOptions, setSelectedOptions] = useState(customFields);
+
+  const options = useMemo(
+    () => fieldOptions.map((field) => ({ label: field.name })),
+    [fieldOptions]
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_modal.tsx
user: login: PhilippeOberti
body: Sorry for not catching this in my first review, but what do you think about filtering out the values that are already in the `defaultFieldsArray`?

To avoid having something like this:

https://github.com/user-attachments/assets/c6ac9a16-b85d-475c-827a-dc29acc27d76

It's not that big of a deal, but could be a nice touch, wdyt?
created_at: 2025-04-04T04:22:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/*
+ * 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 React, { useMemo, useState, useCallback } from 'react';
+import type { FC } from 'react';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleResponse } from '../../../../../common/api/detection_engine';
+import { getDefineStepsData } from '../../../../detection_engine/common/helpers';
+import { useRuleIndexPattern } from '../../../../detection_engine/rule_creation_ui/pages/form';
+import { useDefaultIndexPattern } from '../../../../detection_engine/rule_management/hooks/use_default_index_pattern';
+import { useHighlightedFieldsPrivilege } from '../../shared/hooks/use_highlighted_fields_privilege';
+import { HighlighedFieldsModal } from './highlighted_fields_modal';
+import {
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TEST_ID,
+  HIGHLIGHTED_FIELDS_EDIT_BUTTON_TOOLTIP_TEST_ID,
+} from './test_ids';
+
+interface EditHighlighedFieldsButtonProps {
+  /**
+   * The default highlighted fields
+   */
+  customHighlightedFields: string[];
+  /**
+   * The data formatted for field browser
+   */
+  dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
+  /**
+   * The rule
+   */
+  rule: RuleResponse;
+  /**
+   * Whether the rule exists
+   */
+  isExistingRule: boolean;
+  /**
+   * The function to set the edit loading state
+   */
+  setIsEditLoading: (isEditLoading: boolean) => void;
+}
+
+/**
+ * Component that displays the highlighted fields in the right panel under the Investigation section.
+ */
+export const EditHighlighedFieldsButton: FC<EditHighlighedFieldsButtonProps> = ({
+  customHighlightedFields,
+  dataFormattedForFieldBrowser,
+  rule,
+  isExistingRule,
+  setIsEditLoading,
+}) => {
+  const [isModalVisible, setIsModalVisible] = useState(false);
+  const onClick = useCallback(() => setIsModalVisible(true), []);
+
+  const defaultIndexPattern = useDefaultIndexPattern();
+  const { dataSourceType, index, dataViewId } = useMemo(() => getDefineStepsData(rule), [rule]);
+  const { indexPattern: dataView } = useRuleIndexPattern({
+    dataSourceType,
+    index: index.length > 0 ? index : defaultIndexPattern, // fallback to default index pattern if rule has no index patterns
+    dataViewId,
+  });
path: x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields_button.tsx
user: login: christineweng
body: Yeah I was implying user can access the information elsewhere, and the data could be overridden in the actual rule - so there might not be much value in including here? 

We can discuss more in backlog grooming though! Ticket-> https://github.com/elastic/kibana/issues/217244
created_at: 2025-04-04T18:28:18Z
author_association: CONTRIBUTOR

PR comments:
user: login: elasticmachine
created_at: 2025-04-02T21:15:17Z
body: Pinging @elastic/security-threat-hunting-investigations (Team:Threat Hunting:Investigations)
author_association: CONTRIBUTOR
user: login: elasticmachine
created_at: 2025-04-14T23:06:35Z
body: ## :broken_heart: Build Failed
* [Buildkite Build](https://buildkite.com/elastic/kibana-pull-request/builds/292973)
* Commit: 0a7a169c5692f6a15d73395cd19e2f2573759a52

### Failed CI Steps
* [Build and Deploy to Cloud](https://buildkite.com/elastic/kibana-pull-request/builds/292973#01963660-6e00-45c0-83b8-8d99a98fd62a)

### Metrics [[docs](https://www.elastic.co/guide/en/kibana/master/ci-metrics.html)]
#### Module Count
> Fewer modules leads to a faster build time


| id | [before](https://github.com/elastic/kibana/commit/170651ac78595f6fe8e2ab4924fd5300b8a4f14e) | [after](https://github.com/elastic/kibana/commit/0a7a169c5692f6a15d73395cd19e2f2573759a52) | diff |
| --- | --- | --- | --- |
| `securitySolution` | 7221 | 7224 | +3 |

#### Async chunks
> Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app


| id | [before](https://github.com/elastic/kibana/commit/170651ac78595f6fe8e2ab4924fd5300b8a4f14e) | [after](https://github.com/elastic/kibana/commit/0a7a169c5692f6a15d73395cd19e2f2573759a52) | diff |
| --- | --- | --- | --- |
| `securitySolution` | 9.0MB | 9.0MB | +9.0KB |

#### Page load bundle
> Size of the bundles that are downloaded on every page load. Target size is below 100kb


| id | [before](https://github.com/elastic/kibana/commit/170651ac78595f6fe8e2ab4924fd5300b8a4f14e) | [after](https://github.com/elastic/kibana/commit/0a7a169c5692f6a15d73395cd19e2f2573759a52) | diff |
| --- | --- | --- | --- |
| `securitySolution` | 88.8KB | 88.8KB | +32.0B |



### History
* :broken_heart: [Build #292060](https://buildkite.com/elastic/kibana-pull-request/builds/292060) failed bcddb08d89b43c722aa1d48be555fbe819286ce8
* :broken_heart: [Build #291626](https://buildkite.com/elastic/kibana-pull-request/builds/291626) failed 070f1ed6683254ee6a50a1102917bd202342cf43
* :green_heart: [Build #290445](https://buildkite.com/elastic/kibana-pull-request/builds/290445) succeeded 270a2cad6c200f16352af0a8e52426876ca43c41
* :broken_heart: [Build #290112](https://buildkite.com/elastic/kibana-pull-request/builds/290112) failed 886b0d5f001bc93b3de81ae7c46f53ae24203cf3
* :green_heart: [Build #289304](https://buildkite.com/elastic/kibana-pull-request/builds/289304) succeeded 2dbb3dfcfd82002d4dad327d387ea4d1e7ca59b6

cc @christineweng

<!--buildkite-pr-comment-kibana-pull-request
{"builds":[{"buildStatus":{"state":"passed","success":false,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/292973","number":292973,"commit":"0a7a169c5692f6a15d73395cd19e2f2573759a52"},{"buildStatus":{"state":"failed","success":false,"hasRetries":true,"hasNonPreemptionRetries":true},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/292060","number":292060,"commit":"bcddb08d89b43c722aa1d48be555fbe819286ce8"},{"buildStatus":{"state":"failed","success":false,"hasRetries":true,"hasNonPreemptionRetries":true},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/291626","number":291626,"commit":"070f1ed6683254ee6a50a1102917bd202342cf43"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/290445","number":290445,"commit":"270a2cad6c200f16352af0a8e52426876ca43c41"},{"buildStatus":{"state":"failed","success":false,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/kibana-pull-request/builds/290112","number":290112,"commit":"886b0d5f001bc93b3de81ae7c46f53ae24203cf3"}],"number":292973}
buildkite-pr-comment-->
author_association: CONTRIBUTOR
user: login: kibanamachine
created_at: 2025-04-15T17:18:54Z
body: Starting backport for target branches: 8.x

https://github.com/elastic/kibana/actions/runs/14475472732
author_association: CONTRIBUTOR
user: login: kibanamachine
created_at: 2025-04-15T17:34:30Z
body: ## 💚 All backports created successfully

| Status | Branch | Result |
|:------:|:------:|:------|
|✅|8.x|[<img src="https://img.shields.io/github/pulls/detail/state/elastic/kibana/218323">](https://github.com/elastic/kibana/pull/218323)|

Note: Successful backport PRs will be merged automatically after passing CI.

### Questions ?
Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport)

<!--- Backport version: 9.6.6 -->
author_association: CONTRIBUTOR