diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.test.tsx
new file mode 100644
index 0000000000000..f48b8891e3a50
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.test.tsx
@@ -0,0 +1,135 @@
+/*
+ * 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 } from '@testing-library/react';
+import type { Alert } from '@kbn/alerting-types';
+import { BasicCellRenderer } from './basic_cell_renderer';
+import { TestProviders } from '../../../../common/mock';
+import { getEmptyValue } from '../../../../common/components/empty_value';
+import { CellValue } from './render_cell';
+
+describe('BasicCellRenderer', () => {
+ it('should handle missing field', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 'value1',
+ };
+ const field = 'field';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText(getEmptyValue())).toBeInTheDocument();
+ });
+
+ it('should handle string value', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 'value1',
+ };
+ const field = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('value1')).toBeInTheDocument();
+ });
+
+ it('should handle number value', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 123,
+ };
+ const columnId = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('123')).toBeInTheDocument();
+ });
+
+ it('should handle array of booleans', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [true, false],
+ };
+ const field = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('true, false')).toBeInTheDocument();
+ });
+
+ it('should handle array of numbers', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [1, 2],
+ };
+ const field = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('1, 2')).toBeInTheDocument();
+ });
+
+ it('should handle array of null', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [null, null],
+ };
+ const field = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText(',')).toBeInTheDocument();
+ });
+
+ it('should join array of JsonObjects', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [{ subField1: 'value1', subField2: 'value2' }],
+ };
+ const field = 'field1';
+
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('[object Object]')).toBeInTheDocument();
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.tsx
new file mode 100644
index 0000000000000..96add07d402c7
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/basic_cell_renderer.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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, { memo, useMemo } from 'react';
+import type { Alert } from '@kbn/alerting-types';
+import { getOrEmptyTagFromValue } from '../../../../common/components/empty_value';
+import { getAlertFieldValueAsStringOrNull } from '../../../utils/type_utils';
+
+export interface BasicCellRendererProps {
+ /**
+ * Alert data passed from the renderCellValue callback via the AlertWithLegacyFormats interface
+ */
+ alert: Alert;
+ /**
+ * Column id passed from the renderCellValue callback via EuiDataGridProps['renderCellValue'] interface
+ */
+ field: string;
+}
+
+/**
+ * Renders all the basic table cell values.
+ * Component used in the AI for SOC alert summary table.
+ */
+export const BasicCellRenderer = memo(({ alert, field }: BasicCellRendererProps) => {
+ const displayValue: string | null = useMemo(
+ () => getAlertFieldValueAsStringOrNull(alert, field),
+ [alert, field]
+ );
+
+ return <>{getOrEmptyTagFromValue(displayValue)}>;
+});
+
+BasicCellRenderer.displayName = 'BasicCellRenderer';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.test.tsx
new file mode 100644
index 0000000000000..15db07b321448
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.test.tsx
@@ -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 React from 'react';
+import { render } from '@testing-library/react';
+import type { Alert } from '@kbn/alerting-types';
+import {
+ ICON_TEST_ID,
+ KibanaAlertRelatedIntegrationsCellRenderer,
+ SKELETON_TEST_ID,
+} from './kibana_alert_related_integrations_cell_renderer';
+import { useGetIntegrationFromPackageName } from '../../../hooks/alert_summary/use_get_integration_from_package_name';
+import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
+
+jest.mock('../../../hooks/alert_summary/use_get_integration_from_package_name');
+
+describe('KibanaAlertRelatedIntegrationsCellRenderer', () => {
+ it('should handle missing field', () => {
+ (useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
+ integration: null,
+ isLoading: false,
+ });
+
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ };
+
+ const { queryByTestId } = render();
+
+ expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
+ expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
+ });
+
+ it('should handle not finding matching integration', () => {
+ (useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
+ integration: null,
+ isLoading: false,
+ });
+
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_RULE_PARAMETERS]: ['splunk'],
+ };
+
+ const { queryByTestId } = render();
+
+ expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
+ expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
+ });
+
+ it('should show loading', () => {
+ (useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
+ integration: null,
+ isLoading: true,
+ });
+
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_RULE_PARAMETERS]: ['splunk'],
+ };
+
+ const { getByTestId, queryByTestId } = render(
+
+ );
+
+ expect(getByTestId(SKELETON_TEST_ID)).toBeInTheDocument();
+ expect(queryByTestId(ICON_TEST_ID)).not.toBeInTheDocument();
+ });
+
+ it('should show integration icon', () => {
+ (useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
+ integration: { name: 'Splunk', icon: ['icon'] },
+ isLoading: false,
+ });
+
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_RULE_PARAMETERS]: ['splunk'],
+ };
+
+ const { getByTestId, queryByTestId } = render(
+
+ );
+
+ expect(queryByTestId(SKELETON_TEST_ID)).not.toBeInTheDocument();
+ expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument();
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.tsx
new file mode 100644
index 0000000000000..3a8716855824c
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_related_integrations_cell_renderer.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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, { memo, useMemo } from 'react';
+import type { JsonValue } from '@kbn/utility-types';
+import { CardIcon } from '@kbn/fleet-plugin/public';
+import { EuiSkeletonText } from '@elastic/eui';
+import type { Alert } from '@kbn/alerting-types';
+import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
+import { useGetIntegrationFromPackageName } from '../../../hooks/alert_summary/use_get_integration_from_package_name';
+import { getAlertFieldValueAsStringOrNull, isJsonObjectValue } from '../../../utils/type_utils';
+
+export const SKELETON_TEST_ID = 'alert-summary-table-related-integrations-cell-renderer-skeleton';
+export const ICON_TEST_ID = 'alert-summary-table-related-integrations-cell-renderer-icon';
+
+const RELATED_INTEGRATIONS_FIELD = 'related_integrations';
+const PACKAGE_FIELD = 'package';
+
+// function is_string(value: unknown): value is string {}
+
+export interface KibanaAlertRelatedIntegrationsCellRendererProps {
+ /**
+ * Alert data passed from the renderCellValue callback via the AlertWithLegacyFormats interface
+ */
+ alert: Alert;
+}
+
+/**
+ * Renders an integration/package icon. Retrieves the package name from the kibana.alert.rule.parameters field in the alert,
+ * fetches all integrations/packages and use the icon from the one that matches by name.
+ * Used in AI for SOC alert summary table.
+ */
+export const KibanaAlertRelatedIntegrationsCellRenderer = memo(
+ ({ alert }: KibanaAlertRelatedIntegrationsCellRendererProps) => {
+ const packageName: string | null = useMemo(() => {
+ const values: JsonValue[] | undefined = alert[ALERT_RULE_PARAMETERS];
+
+ if (Array.isArray(values) && values.length === 1) {
+ const value: JsonValue = values[0];
+ if (!isJsonObjectValue(value)) return null;
+
+ const relatedIntegration = value[RELATED_INTEGRATIONS_FIELD];
+ if (!isJsonObjectValue(relatedIntegration)) return null;
+
+ return getAlertFieldValueAsStringOrNull(relatedIntegration as Alert, PACKAGE_FIELD);
+ }
+
+ return null;
+ }, [alert]);
+
+ const { integration, isLoading } = useGetIntegrationFromPackageName({ packageName });
+
+ return (
+
+ {integration ? (
+
+ ) : null}
+
+ );
+ }
+);
+
+KibanaAlertRelatedIntegrationsCellRenderer.displayName =
+ 'KibanaAlertRelatedIntegrationsCellRenderer';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.test.tsx
new file mode 100644
index 0000000000000..f4800556365cd
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.test.tsx
@@ -0,0 +1,101 @@
+/*
+ * 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 } from '@testing-library/react';
+import type { Alert } from '@kbn/alerting-types';
+import { TestProviders } from '../../../../common/mock';
+import {
+ BADGE_TEST_ID,
+ KibanaAlertSeverityCellRenderer,
+} from './kibana_alert_severity_cell_renderer';
+import { ALERT_SEVERITY } from '@kbn/rule-data-utils';
+
+describe('KibanaAlertSeverityCellRenderer', () => {
+ it('should handle missing field', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ };
+
+ const { container } = render(
+
+
+
+ );
+
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it('should show low', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_SEVERITY]: ['low'],
+ };
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(BADGE_TEST_ID)).toHaveTextContent('Low');
+ expect(getByTestId(BADGE_TEST_ID)).toHaveStyle('--euiBadgeBackgroundColor: #54B399');
+ });
+
+ it('should show medium', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_SEVERITY]: ['medium'],
+ };
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(BADGE_TEST_ID)).toHaveTextContent('Medium');
+ expect(getByTestId(BADGE_TEST_ID)).toHaveStyle('--euiBadgeBackgroundColor: #D6BF57');
+ });
+
+ it('should show high', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_SEVERITY]: ['high'],
+ };
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(BADGE_TEST_ID)).toHaveTextContent('High');
+ expect(getByTestId(BADGE_TEST_ID)).toHaveStyle('--euiBadgeBackgroundColor: #DA8B45');
+ });
+
+ it('should show critical', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_SEVERITY]: ['critical'],
+ };
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(BADGE_TEST_ID)).toHaveTextContent('Critical');
+ expect(getByTestId(BADGE_TEST_ID)).toHaveStyle('--euiBadgeBackgroundColor: #E7664C');
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.tsx
new file mode 100644
index 0000000000000..2e506c2d4a6d9
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/kibana_alert_severity_cell_renderer.tsx
@@ -0,0 +1,66 @@
+/*
+ * 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, { memo, useMemo } from 'react';
+import { EuiBadge, useEuiTheme } from '@elastic/eui';
+import type { Alert } from '@kbn/alerting-types';
+import { ALERT_SEVERITY } from '@kbn/rule-data-utils';
+import type { JsonValue } from '@kbn/utility-types';
+import { getSeverityColor } from '../../alerts_kpis/severity_level_panel/helpers';
+
+export const BADGE_TEST_ID = 'alert-summary-table-severity-cell-renderer';
+
+/**
+ * Return the same string with the first letter capitalized
+ */
+const capitalizeFirstLetter = (value: string): string =>
+ String(value).charAt(0).toUpperCase() + String(value).slice(1);
+
+export interface KibanaAlertSeverityCellRendererProps {
+ /**
+ * Alert data passed from the renderCellValue callback via the AlertWithLegacyFormats interface
+ */
+ alert: Alert;
+}
+
+/**
+ * Renders a EuiBadge for the kibana.alert.severity field.
+ * Used in AI for SOC alert summary table.
+ */
+export const KibanaAlertSeverityCellRenderer = memo(
+ ({ alert }: KibanaAlertSeverityCellRendererProps) => {
+ const { euiTheme } = useEuiTheme();
+
+ const displayValue: string | null = useMemo(() => {
+ const values: JsonValue[] | undefined = alert[ALERT_SEVERITY];
+
+ if (Array.isArray(values) && values.length === 1) {
+ const value: JsonValue = values[0];
+ return value && typeof value === 'string' ? capitalizeFirstLetter(value) : null;
+ }
+
+ return null;
+ }, [alert]);
+
+ const color: string = useMemo(
+ () => getSeverityColor(displayValue || '', euiTheme),
+ [displayValue, euiTheme]
+ );
+
+ return (
+ <>
+ {displayValue && (
+
+ {displayValue}
+
+ )}
+ >
+ );
+ }
+);
+
+KibanaAlertSeverityCellRenderer.displayName = 'KibanaAlertSeverityCellRenderer';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.test.tsx
index 9a4d46e532416..880c8c4551bd2 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.test.tsx
@@ -11,6 +11,12 @@ import type { Alert } from '@kbn/alerting-types';
import { CellValue } from './render_cell';
import { TestProviders } from '../../../../common/mock';
import { getEmptyValue } from '../../../../common/components/empty_value';
+import { ALERT_RULE_PARAMETERS, ALERT_SEVERITY } from '@kbn/rule-data-utils';
+import { ICON_TEST_ID } from './kibana_alert_related_integrations_cell_renderer';
+import { useGetIntegrationFromPackageName } from '../../../hooks/alert_summary/use_get_integration_from_package_name';
+import { BADGE_TEST_ID } from './kibana_alert_severity_cell_renderer';
+
+jest.mock('../../../hooks/alert_summary/use_get_integration_from_package_name');
describe('CellValue', () => {
it('should handle missing field', () => {
@@ -131,4 +137,42 @@ describe('CellValue', () => {
expect(getByText('[object Object]')).toBeInTheDocument();
});
+
+ it('should use related integration renderer', () => {
+ (useGetIntegrationFromPackageName as jest.Mock).mockReturnValue({
+ integration: {},
+ isLoading: false,
+ });
+
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ };
+ const columnId = ALERT_RULE_PARAMETERS;
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(ICON_TEST_ID)).toBeInTheDocument();
+ });
+
+ it('should use severity renderer', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ [ALERT_SEVERITY]: ['low'],
+ };
+ const columnId = ALERT_SEVERITY;
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId(BADGE_TEST_ID)).toBeInTheDocument();
+ });
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.tsx
index 080b849d7edb4..a7de92212a0ac 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/render_cell.tsx
@@ -5,11 +5,14 @@
* 2.0.
*/
-import React, { memo, useMemo } from 'react';
+import React, { memo } from 'react';
import type { Alert } from '@kbn/alerting-types';
-import type { JsonValue } from '@kbn/utility-types';
-import { getOrEmptyTagFromValue } from '../../../../common/components/empty_value';
+import { ALERT_RULE_PARAMETERS, ALERT_SEVERITY } from '@kbn/rule-data-utils';
+import { BasicCellRenderer } from './basic_cell_renderer';
+import { KibanaAlertSeverityCellRenderer } from './kibana_alert_severity_cell_renderer';
+import { KibanaAlertRelatedIntegrationsCellRenderer } from './kibana_alert_related_integrations_cell_renderer';
+// guarantees that all cells will have their values vertically centered
const styles = { display: 'flex', alignItems: 'center', height: '100%' };
export interface CellValueProps {
@@ -29,36 +32,23 @@ export interface CellValueProps {
* It will be soon improved to support custom renders for specific fields (like kibana.alert.rule.parameters and kibana.alert.severity).
*/
export const CellValue = memo(({ alert, columnId }: CellValueProps) => {
- const displayValue: string | null = useMemo(() => {
- const cellValues: string | number | JsonValue[] = alert[columnId];
+ let component;
- // Displays string as is.
- // Joins values of array with more than one element.
- // Returns null if the value is null.
- // Return the string of the value otherwise.
- if (typeof cellValues === 'string') {
- return cellValues;
- } else if (typeof cellValues === 'number') {
- return cellValues.toString();
- } else if (Array.isArray(cellValues)) {
- if (cellValues.length > 1) {
- return cellValues.join(', ');
- } else {
- const value: JsonValue = cellValues[0];
- if (typeof value === 'string') {
- return value;
- } else if (value == null) {
- return null;
- } else {
- return value.toString();
- }
- }
- } else {
- return null;
- }
- }, [alert, columnId]);
+ switch (columnId) {
+ case ALERT_RULE_PARAMETERS:
+ component = ;
+ break;
- return
{getOrEmptyTagFromValue(displayValue)}
;
+ case ALERT_SEVERITY:
+ component = ;
+ break;
+
+ default:
+ component = ;
+ break;
+ }
+
+ return {component}
;
});
CellValue.displayName = 'CellValue';
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/table.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/table.tsx
index bba449d9b140f..f1764890aca33 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/table.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alert_summary/table/table.tsx
@@ -13,7 +13,13 @@ import { i18n } from '@kbn/i18n';
import { TableId } from '@kbn/securitysolution-data-table';
import { AlertsTable } from '@kbn/response-ops-alerts-table';
import type { AlertsTableProps } from '@kbn/response-ops-alerts-table/types';
-import { AlertConsumers } from '@kbn/rule-data-utils';
+import {
+ ALERT_RULE_NAME,
+ ALERT_RULE_PARAMETERS,
+ ALERT_SEVERITY,
+ AlertConsumers,
+ TIMESTAMP,
+} from '@kbn/rule-data-utils';
import { ESQL_RULE_TYPE_ID, QUERY_RULE_TYPE_ID } from '@kbn/securitysolution-rules';
import type {
EuiDataGridProps,
@@ -48,26 +54,21 @@ const RULE_NAME_COLUMN = i18n.translate(
{ defaultMessage: 'Rule' }
);
-const TIMESTAMP = '@timestamp';
-const RELATED_INTEGRATION = 'kibana.alert.rule.parameters';
-const SEVERITY = 'kibana.alert.severity';
-const RULE_NAME = 'kibana.alert.rule.name';
-
const columns: EuiDataGridProps['columns'] = [
{
id: TIMESTAMP,
displayAsText: TIMESTAMP_COLUMN,
},
{
- id: RELATED_INTEGRATION,
+ id: ALERT_RULE_PARAMETERS,
displayAsText: RELATION_INTEGRATION_COLUMN,
},
{
- id: SEVERITY,
+ id: ALERT_SEVERITY,
displayAsText: SEVERITY_COLUMN,
},
{
- id: RULE_NAME,
+ id: ALERT_RULE_NAME,
displayAsText: RULE_NAME_COLUMN,
},
];
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/alert_summary/use_get_integration_from_package_name.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/alert_summary/use_get_integration_from_package_name.ts
new file mode 100644
index 0000000000000..7c3bf90ee684f
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/hooks/alert_summary/use_get_integration_from_package_name.ts
@@ -0,0 +1,51 @@
+/*
+ * 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 type { PackageListItem } from '@kbn/fleet-plugin/common';
+import { useFetchIntegrations } from './use_fetch_integrations';
+
+export interface UseGetIntegrationFromRuleIdParams {
+ /**
+ *
+ */
+ packageName: string | null;
+}
+
+export interface UseGetIntegrationFromRuleIdResult {
+ /**
+ * List of integrations ready to be consumed by the IntegrationFilterButton component
+ */
+ integration: PackageListItem | undefined;
+ /**
+ * True while rules are being fetched
+ */
+ isLoading: boolean;
+}
+
+/**
+ *
+ */
+export const useGetIntegrationFromPackageName = ({
+ packageName,
+}: UseGetIntegrationFromRuleIdParams): UseGetIntegrationFromRuleIdResult => {
+ // Fetch all packages
+ const { installedPackages, isLoading } = useFetchIntegrations();
+
+ const integration = useMemo(
+ () => installedPackages.find((installedPackage) => installedPackage.name === packageName),
+ [installedPackages, packageName]
+ );
+
+ return useMemo(
+ () => ({
+ integration,
+ isLoading,
+ }),
+ [integration, isLoading]
+ );
+};
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.test.tsx
new file mode 100644
index 0000000000000..943b64f5a883c
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.test.tsx
@@ -0,0 +1,153 @@
+/*
+ * 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 type { Alert } from '@kbn/alerting-types';
+import { getAlertFieldValueAsStringOrNull, isJsonObjectValue } from './type_utils';
+import type { JsonValue } from '@kbn/utility-types';
+
+describe('getAlertFieldValueAsStringOrNull', () => {
+ it('should handle missing field', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 'value1',
+ };
+ const field = 'columnId';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toBe(null);
+ });
+
+ it('should handle string value', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 'value1',
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual('value1');
+ });
+
+ it('should handle a number value', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: 123,
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual('123');
+ });
+
+ it('should handle array of booleans', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [true, false],
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual('true, false');
+ });
+
+ it('should handle array of numbers', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [1, 2],
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual('1, 2');
+ });
+
+ it('should handle array of null', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [null, null],
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual(', ');
+ });
+
+ it('should join array of JsonObjects', () => {
+ const alert: Alert = {
+ _id: '_id',
+ _index: '_index',
+ field1: [{ subField1: 'value1', subField2: 'value2' }],
+ };
+ const field = 'field1';
+
+ const result = getAlertFieldValueAsStringOrNull(alert, field);
+
+ expect(result).toEqual('[object Object]');
+ });
+});
+
+describe('isJsonObjectValue', () => {
+ it('should return true for JsonObject', () => {
+ const value: JsonValue = { test: 'value' };
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(true);
+ });
+
+ it('should return false for null', () => {
+ const value: JsonValue = null;
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(false);
+ });
+
+ it('should return false for string', () => {
+ const value: JsonValue = 'test';
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(false);
+ });
+
+ it('should return false for number', () => {
+ const value: JsonValue = 123;
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(false);
+ });
+
+ it('should return false for boolean', () => {
+ const value: JsonValue = true;
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(false);
+ });
+
+ it('should return false for array', () => {
+ const value: JsonValue = ['test', 123, true];
+
+ const result = isJsonObjectValue(value);
+
+ expect(result).toBe(false);
+ });
+});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.ts
new file mode 100644
index 0000000000000..f05bdef660eed
--- /dev/null
+++ b/x-pack/solutions/security/plugins/security_solution/public/detections/utils/type_utils.ts
@@ -0,0 +1,54 @@
+/*
+ * 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 type { JsonObject, JsonValue } from '@kbn/utility-types';
+import type { Alert } from '@kbn/alerting-types';
+
+/**
+ * Takes an Alert object and a field string as input and returns the value for the field as a string.
+ * If the value is already a string, return it.
+ * If the value is an array, join the values.
+ * If null the value is null.
+ * Return the string of the value otherwise.
+ */
+export const getAlertFieldValueAsStringOrNull = (alert: Alert, field: string): string | null => {
+ const cellValues: string | number | JsonValue[] = alert[field];
+
+ if (typeof cellValues === 'string') {
+ return cellValues;
+ } else if (typeof cellValues === 'number') {
+ return cellValues.toString();
+ } else if (Array.isArray(cellValues)) {
+ if (cellValues.length > 1) {
+ return cellValues.join(', ');
+ } else {
+ const value: JsonValue = cellValues[0];
+ if (typeof value === 'string') {
+ return value;
+ } else if (value == null) {
+ return null;
+ } else {
+ return value.toString();
+ }
+ }
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Guaratees that the value is of type JsonObject
+ */
+export const isJsonObjectValue = (value: JsonValue): value is JsonObject => {
+ return (
+ value != null &&
+ typeof value !== 'string' &&
+ typeof value !== 'number' &&
+ typeof value !== 'boolean' &&
+ !Array.isArray(value)
+ );
+};