mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[AI4DSOC] Change the Attack Discovery page to use the AI for SOC alerts table (#218736)
## Summary While testing, we realized that the Attack Discovery alerts tab was showingn the `DetectionEngineAlertsTable`, even in the AI4DSOC tier. This PR updates the logic to show the correct alerts table depending on the tier: - AI4DSOC will show the same table as the Alert summary page - the other tiers will continue showing the same table as the Alerts page (`DetectionEngineAlertsTable`) Switching the table allows us to tackle at once all the other related issues: - wrong flyout was being shown - too many actions were being shown - wrong default columns, and wrong cell renderes ### Notes The approach is not ideal. We shouldn't have to check for the following ```typescript const AIForSOC = capabilities[SECURITY_FEATURE_ID].configurations; ``` in the code, but because of time constraints, this was the best approach. [A ticket](https://github.com/elastic/kibana/issues/218731) has been opened to make sure we come back to this and implement the check the correct way later. Current (wrong) behavior https://github.com/user-attachments/assets/c41a25f1-ae9a-4bbf-9c02-9b1054f3a0e3 New behavior https://github.com/user-attachments/assets/0eb20a2f-ba00-42c0-9353-7ac788c9bea0 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Relates to https://github.com/elastic/security-team/issues/11973
This commit is contained in:
parent
361d38acfc
commit
c8cbe87040
7 changed files with 532 additions and 26 deletions
|
@ -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 React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import type { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { createStubDataView } from '@kbn/data-views-plugin/common/data_views/data_view.stub';
|
||||
import { TestProviders } from '../../../../../../../common/mock';
|
||||
import { Table } from './table';
|
||||
import type { PackageListItem } from '@kbn/fleet-plugin/common';
|
||||
import { installationStatuses } from '@kbn/fleet-plugin/common/constants';
|
||||
|
||||
const dataView: DataView = createStubDataView({ spec: {} });
|
||||
const packages: PackageListItem[] = [
|
||||
{
|
||||
id: 'splunk',
|
||||
icons: [{ src: 'icon.svg', path: 'mypath/icon.svg', type: 'image/svg+xml' }],
|
||||
name: 'splunk',
|
||||
status: installationStatuses.NotInstalled,
|
||||
title: 'Splunk',
|
||||
version: '0.1.0',
|
||||
},
|
||||
];
|
||||
const ruleResponse = {
|
||||
rules: [],
|
||||
isLoading: false,
|
||||
};
|
||||
const id = 'id';
|
||||
const query = { ids: { values: ['abcdef'] } };
|
||||
|
||||
describe('<Table />', () => {
|
||||
it('should render all components', () => {
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<Table
|
||||
dataView={dataView}
|
||||
id={id}
|
||||
packages={packages}
|
||||
query={query}
|
||||
ruleResponse={ruleResponse}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getByTestId('alertsTableErrorPrompt')).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { AlertsTable } from '@kbn/response-ops-alerts-table';
|
||||
import type { PackageListItem } from '@kbn/fleet-plugin/common';
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { AdditionalTableContext } from '../../../../../../../detections/components/alert_summary/table/table';
|
||||
import {
|
||||
ACTION_COLUMN_WIDTH,
|
||||
ALERT_TABLE_CONSUMERS,
|
||||
columns,
|
||||
GRID_STYLE,
|
||||
ROW_HEIGHTS_OPTIONS,
|
||||
RULE_TYPE_IDS,
|
||||
TOOLBAR_VISIBILITY,
|
||||
} from '../../../../../../../detections/components/alert_summary/table/table';
|
||||
import { ActionsCell } from '../../../../../../../detections/components/alert_summary/table/actions_cell';
|
||||
import { getDataViewStateFromIndexFields } from '../../../../../../../common/containers/source/use_data_view';
|
||||
import { useKibana } from '../../../../../../../common/lib/kibana';
|
||||
import { CellValue } from '../../../../../../../detections/components/alert_summary/table/render_cell';
|
||||
import type { RuleResponse } from '../../../../../../../../common/api/detection_engine';
|
||||
|
||||
export interface TableProps {
|
||||
/**
|
||||
* DataView created for the alert summary page
|
||||
*/
|
||||
dataView: DataView;
|
||||
/**
|
||||
* Id to pass down to the ResponseOps alerts table
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* List of installed AI for SOC integrations
|
||||
*/
|
||||
packages: PackageListItem[];
|
||||
/**
|
||||
* Query that contains the id of the alerts to display in the table
|
||||
*/
|
||||
query: Pick<QueryDslQueryContainer, 'bool' | 'ids'>;
|
||||
/**
|
||||
* Result from the useQuery to fetch all rules
|
||||
*/
|
||||
ruleResponse: {
|
||||
/**
|
||||
* Result from fetching all rules
|
||||
*/
|
||||
rules: RuleResponse[];
|
||||
/**
|
||||
* True while rules are being fetched
|
||||
*/
|
||||
isLoading: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used in the Attack Discovery alerts table, only in the AI4DSOC tier.
|
||||
* It leverages a lot of configurations and constants from the Alert summary page alerts table, and renders the ResponseOps AlertsTable.
|
||||
*/
|
||||
export const Table = memo(({ dataView, id, packages, query, ruleResponse }: TableProps) => {
|
||||
const {
|
||||
services: { application, data, fieldFormats, http, licensing, notifications, settings },
|
||||
} = useKibana();
|
||||
const services = useMemo(
|
||||
() => ({
|
||||
data,
|
||||
http,
|
||||
notifications,
|
||||
fieldFormats,
|
||||
application,
|
||||
licensing,
|
||||
settings,
|
||||
}),
|
||||
[application, data, fieldFormats, http, licensing, notifications, settings]
|
||||
);
|
||||
|
||||
const dataViewSpec = useMemo(() => dataView.toSpec(), [dataView]);
|
||||
|
||||
const { browserFields } = useMemo(
|
||||
() => getDataViewStateFromIndexFields('', dataViewSpec.fields),
|
||||
[dataViewSpec.fields]
|
||||
);
|
||||
|
||||
const additionalContext: AdditionalTableContext = useMemo(
|
||||
() => ({
|
||||
packages,
|
||||
ruleResponse,
|
||||
}),
|
||||
[packages, ruleResponse]
|
||||
);
|
||||
|
||||
return (
|
||||
<AlertsTable
|
||||
actionsColumnWidth={ACTION_COLUMN_WIDTH}
|
||||
additionalContext={additionalContext}
|
||||
browserFields={browserFields}
|
||||
columns={columns}
|
||||
consumers={ALERT_TABLE_CONSUMERS}
|
||||
gridStyle={GRID_STYLE}
|
||||
id={id}
|
||||
query={query}
|
||||
renderActionsCell={ActionsCell}
|
||||
renderCellValue={CellValue}
|
||||
rowHeightsOptions={ROW_HEIGHTS_OPTIONS}
|
||||
ruleTypeIds={RULE_TYPE_IDS}
|
||||
services={services}
|
||||
toolbarVisibility={TOOLBAR_VISIBILITY}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Table.displayName = 'Table';
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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 { act, render } from '@testing-library/react';
|
||||
import {
|
||||
AiForSOCAlertsTab,
|
||||
CONTENT_TEST_ID,
|
||||
ERROR_TEST_ID,
|
||||
LOADING_PROMPT_TEST_ID,
|
||||
SKELETON_TEST_ID,
|
||||
} from './wrapper';
|
||||
import { useKibana } from '../../../../../../../common/lib/kibana';
|
||||
import { TestProviders } from '../../../../../../../common/mock';
|
||||
import { useFetchIntegrations } from '../../../../../../../detections/hooks/alert_summary/use_fetch_integrations';
|
||||
import { useFindRulesQuery } from '../../../../../../../detection_engine/rule_management/api/hooks/use_find_rules_query';
|
||||
|
||||
jest.mock('./table', () => ({
|
||||
Table: () => <div />,
|
||||
}));
|
||||
jest.mock('../../../../../../../common/lib/kibana');
|
||||
jest.mock('../../../../../../../detections/hooks/alert_summary/use_fetch_integrations');
|
||||
jest.mock('../../../../../../../detection_engine/rule_management/api/hooks/use_find_rules_query');
|
||||
|
||||
const id = 'id';
|
||||
const query = { ids: { values: ['abcdef'] } };
|
||||
|
||||
describe('<AiForSOCAlertsTab />', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
(useFetchIntegrations as jest.Mock).mockReturnValue({
|
||||
installedPackages: [],
|
||||
isLoading: false,
|
||||
});
|
||||
(useFindRulesQuery as jest.Mock).mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a loading skeleton while creating the dataView', async () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
data: {
|
||||
dataViews: {
|
||||
create: jest.fn(),
|
||||
clearInstanceCache: jest.fn(),
|
||||
},
|
||||
},
|
||||
http: { basePath: { prepend: jest.fn() } },
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const { getByTestId } = render(<AiForSOCAlertsTab id={id} query={query} />);
|
||||
|
||||
expect(getByTestId(LOADING_PROMPT_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(SKELETON_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a loading skeleton while fetching packages (integrations)', async () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
data: {
|
||||
dataViews: {
|
||||
create: jest.fn(),
|
||||
clearInstanceCache: jest.fn(),
|
||||
},
|
||||
},
|
||||
http: { basePath: { prepend: jest.fn() } },
|
||||
},
|
||||
});
|
||||
(useFetchIntegrations as jest.Mock).mockReturnValue({
|
||||
installedPackages: [],
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const { getByTestId } = render(<AiForSOCAlertsTab id={id} query={query} />);
|
||||
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(getByTestId(LOADING_PROMPT_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(SKELETON_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render an error if the dataView fail to be created correctly', async () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
data: {
|
||||
dataViews: {
|
||||
create: jest.fn().mockReturnValue(undefined),
|
||||
clearInstanceCache: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useEffect: jest.fn((f) => f()),
|
||||
}));
|
||||
|
||||
await act(async () => {
|
||||
const { getByTestId } = render(<AiForSOCAlertsTab id={id} query={query} />);
|
||||
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(getByTestId(LOADING_PROMPT_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(ERROR_TEST_ID)).toHaveTextContent('Unable to create data view');
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the content', async () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
data: {
|
||||
dataViews: {
|
||||
create: jest
|
||||
.fn()
|
||||
.mockReturnValue({ getIndexPattern: jest.fn(), id: 'id', toSpec: jest.fn() }),
|
||||
clearInstanceCache: jest.fn(),
|
||||
},
|
||||
query: { filterManager: { getFilters: jest.fn() } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useEffect: jest.fn((f) => f()),
|
||||
}));
|
||||
|
||||
await act(async () => {
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<AiForSOCAlertsTab id={id} query={query} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(getByTestId(LOADING_PROMPT_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(CONTENT_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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, useEffect, useMemo, useState } from 'react';
|
||||
import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/common';
|
||||
import { EuiEmptyPrompt, EuiSkeletonLoading, EuiSkeletonRectangle } from '@elastic/eui';
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Table } from './table';
|
||||
import { useFetchIntegrations } from '../../../../../../../detections/hooks/alert_summary/use_fetch_integrations';
|
||||
import { useFindRulesQuery } from '../../../../../../../detection_engine/rule_management/api/hooks/use_find_rules_query';
|
||||
import { useKibana } from '../../../../../../../common/lib/kibana';
|
||||
|
||||
const DATAVIEW_ERROR = i18n.translate(
|
||||
'xpack.securitySolution.attackDiscovery.aiForSocTableTab.dataViewError',
|
||||
{
|
||||
defaultMessage: 'Unable to create data view',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOADING_PROMPT_TEST_ID = 'attack-discovery-alert-loading-prompt';
|
||||
export const ERROR_TEST_ID = 'attack-discovery-alert-error';
|
||||
export const SKELETON_TEST_ID = 'attack-discovery-alert-skeleton';
|
||||
export const CONTENT_TEST_ID = 'attack-discovery-alert-content';
|
||||
|
||||
const dataViewSpec: DataViewSpec = { title: '.alerts-security.alerts-default' };
|
||||
|
||||
interface AiForSOCAlertsTabProps {
|
||||
/**
|
||||
* Id to pass down to the ResponseOps alerts table
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Query that contains the id of the alerts to display in the table
|
||||
*/
|
||||
query: Pick<QueryDslQueryContainer, 'bool' | 'ids'>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component used in the Attack Discovery alerts table, only in the AI4DSOC tier.
|
||||
* It fetches rules, packages (integrations) and creates a local dataView.
|
||||
* It renders a loading skeleton while packages are being fetched and while the dataView is being created.
|
||||
*/
|
||||
export const AiForSOCAlertsTab = memo(({ id, query }: AiForSOCAlertsTabProps) => {
|
||||
const { data } = useKibana().services;
|
||||
const [dataView, setDataView] = useState<DataView | undefined>(undefined);
|
||||
const [dataViewLoading, setDataViewLoading] = useState<boolean>(true);
|
||||
|
||||
// Fetch all integrations
|
||||
const { installedPackages, isLoading: integrationIsLoading } = useFetchIntegrations();
|
||||
|
||||
// Fetch all rules. For the AI for SOC effort, there should only be one rule per integration (which means for now 5-6 rules total)
|
||||
const { data: ruleData, isLoading: ruleIsLoading } = useFindRulesQuery({});
|
||||
const ruleResponse = useMemo(
|
||||
() => ({
|
||||
rules: ruleData?.rules || [],
|
||||
isLoading: ruleIsLoading,
|
||||
}),
|
||||
[ruleData, ruleIsLoading]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let dv: DataView;
|
||||
const createDataView = async () => {
|
||||
dv = await data.dataViews.create(dataViewSpec);
|
||||
setDataView(dv);
|
||||
setDataViewLoading(false);
|
||||
};
|
||||
createDataView();
|
||||
|
||||
// clearing after leaving the page
|
||||
return () => {
|
||||
if (dv?.id) {
|
||||
data.dataViews.clearInstanceCache(dv?.id);
|
||||
}
|
||||
};
|
||||
}, [data.dataViews]);
|
||||
|
||||
return (
|
||||
<EuiSkeletonLoading
|
||||
data-test-subj={LOADING_PROMPT_TEST_ID}
|
||||
isLoading={integrationIsLoading || dataViewLoading}
|
||||
loadingContent={
|
||||
<EuiSkeletonRectangle data-test-subj={SKELETON_TEST_ID} height={400} width="100%" />
|
||||
}
|
||||
loadedContent={
|
||||
<>
|
||||
{!dataView || !dataView.id ? (
|
||||
<EuiEmptyPrompt
|
||||
color="danger"
|
||||
data-test-subj={ERROR_TEST_ID}
|
||||
iconType="error"
|
||||
title={<h2>{DATAVIEW_ERROR}</h2>}
|
||||
/>
|
||||
) : (
|
||||
<div data-test-subj={CONTENT_TEST_ID}>
|
||||
<Table
|
||||
dataView={dataView}
|
||||
id={id}
|
||||
packages={installedPackages}
|
||||
query={query}
|
||||
ruleResponse={ruleResponse}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
AiForSOCAlertsTab.displayName = 'AiForSOCAlertsTab';
|
|
@ -5,23 +5,71 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { TestProviders } from '../../../../../../common/mock';
|
||||
import { mockAttackDiscovery } from '../../../../mock/mock_attack_discovery';
|
||||
import { AlertsTab } from '.';
|
||||
import { useKibana } from '../../../../../../common/lib/kibana';
|
||||
import { SECURITY_FEATURE_ID } from '../../../../../../../common';
|
||||
|
||||
jest.mock('../../../../../../common/lib/kibana');
|
||||
jest.mock('../../../../../../detections/components/alerts_table', () => ({
|
||||
DetectionEngineAlertsTable: () => <div />,
|
||||
}));
|
||||
jest.mock('./ai_for_soc/wrapper', () => ({
|
||||
AiForSOCAlertsTab: () => <div />,
|
||||
}));
|
||||
|
||||
describe('AlertsTab', () => {
|
||||
it('renders the alerts tab', () => {
|
||||
render(
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the alerts tab with DetectionEngineAlertsTable', () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
application: {
|
||||
capabilities: {
|
||||
[SECURITY_FEATURE_ID]: {
|
||||
configurations: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<AlertsTab attackDiscovery={mockAttackDiscovery} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
const alertsTab = screen.getByTestId('alertsTab');
|
||||
expect(getByTestId('alertsTab')).toBeInTheDocument();
|
||||
expect(getByTestId('detection-engine-alerts-table')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(alertsTab).toBeInTheDocument();
|
||||
it('renders the alerts tab with AI4DSOC alerts table', () => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
application: {
|
||||
capabilities: {
|
||||
[SECURITY_FEATURE_ID]: {
|
||||
configurations: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<AlertsTab attackDiscovery={mockAttackDiscovery} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getByTestId('alertsTab')).toBeInTheDocument();
|
||||
expect(getByTestId('ai4dsoc-alerts-table')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,9 @@ import type { AttackDiscovery, Replacements } from '@kbn/elastic-assistant-commo
|
|||
import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules';
|
||||
|
||||
import { TableId } from '@kbn/securitysolution-data-table';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { AiForSOCAlertsTab } from './ai_for_soc/wrapper';
|
||||
import { useKibana } from '../../../../../../common/lib/kibana';
|
||||
import { SECURITY_FEATURE_ID } from '../../../../../../../common';
|
||||
import { DetectionEngineAlertsTable } from '../../../../../../detections/components/alerts_table';
|
||||
|
||||
interface Props {
|
||||
|
@ -19,6 +21,15 @@ interface Props {
|
|||
}
|
||||
|
||||
const AlertsTabComponent: React.FC<Props> = ({ attackDiscovery, replacements }) => {
|
||||
const {
|
||||
application: { capabilities },
|
||||
} = useKibana().services;
|
||||
|
||||
// TODO We shouldn't have to check capabilities here, this should be done at a much higher level.
|
||||
// https://github.com/elastic/kibana/issues/218731
|
||||
// For the AI for SOC we need to show the Alert summary page alerts table
|
||||
const AIForSOC = capabilities[SECURITY_FEATURE_ID].configurations;
|
||||
|
||||
const originalAlertIds = useMemo(
|
||||
() =>
|
||||
attackDiscovery.alertIds.map((alertId) =>
|
||||
|
@ -36,16 +47,25 @@ const AlertsTabComponent: React.FC<Props> = ({ attackDiscovery, replacements })
|
|||
[originalAlertIds]
|
||||
);
|
||||
|
||||
const id = useMemo(() => `attack-discovery-alerts-${attackDiscovery.id}`, [attackDiscovery.id]);
|
||||
|
||||
return (
|
||||
<div data-test-subj="alertsTab">
|
||||
<DetectionEngineAlertsTable
|
||||
id={`attack-discovery-alerts-${attackDiscovery.id}`}
|
||||
tableType={TableId.alertsOnCasePage}
|
||||
ruleTypeIds={SECURITY_SOLUTION_RULE_TYPE_IDS}
|
||||
consumers={[AlertConsumers.SIEM]}
|
||||
query={alertIdsQuery}
|
||||
showAlertStatusWithFlapping={false}
|
||||
/>
|
||||
{AIForSOC ? (
|
||||
<div data-test-subj="ai4dsoc-alerts-table">
|
||||
<AiForSOCAlertsTab id={id} query={alertIdsQuery} />
|
||||
</div>
|
||||
) : (
|
||||
<div data-test-subj="detection-engine-alerts-table">
|
||||
<DetectionEngineAlertsTable
|
||||
id={id}
|
||||
tableType={TableId.alertsOnCasePage}
|
||||
ruleTypeIds={SECURITY_SOLUTION_RULE_TYPE_IDS}
|
||||
query={alertIdsQuery}
|
||||
showAlertStatusWithFlapping={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -39,24 +39,24 @@ import { buildTimeRangeFilter } from '../../alerts_table/helpers';
|
|||
import { useGlobalTime } from '../../../../common/containers/use_global_time';
|
||||
import type { RuleResponse } from '../../../../../common/api/detection_engine';
|
||||
|
||||
const TIMESTAMP_COLUMN = i18n.translate(
|
||||
export const TIMESTAMP_COLUMN = i18n.translate(
|
||||
'xpack.securitySolution.alertSummary.table.column.timeStamp',
|
||||
{ defaultMessage: 'Timestamp' }
|
||||
);
|
||||
const RELATION_INTEGRATION_COLUMN = i18n.translate(
|
||||
export const RELATION_INTEGRATION_COLUMN = i18n.translate(
|
||||
'xpack.securitySolution.alertSummary.table.column.relatedIntegrationName',
|
||||
{ defaultMessage: 'Integration' }
|
||||
);
|
||||
const SEVERITY_COLUMN = i18n.translate(
|
||||
export const SEVERITY_COLUMN = i18n.translate(
|
||||
'xpack.securitySolution.alertSummary.table.column.severity',
|
||||
{ defaultMessage: 'Severity' }
|
||||
);
|
||||
const RULE_NAME_COLUMN = i18n.translate(
|
||||
export const RULE_NAME_COLUMN = i18n.translate(
|
||||
'xpack.securitySolution.alertSummary.table.column.ruleName',
|
||||
{ defaultMessage: 'Rule' }
|
||||
);
|
||||
|
||||
const columns: EuiDataGridProps['columns'] = [
|
||||
export const columns: EuiDataGridProps['columns'] = [
|
||||
{
|
||||
id: TIMESTAMP,
|
||||
displayAsText: TIMESTAMP_COLUMN,
|
||||
|
@ -75,18 +75,18 @@ const columns: EuiDataGridProps['columns'] = [
|
|||
},
|
||||
];
|
||||
|
||||
const ACTION_COLUMN_WIDTH = 98; // px
|
||||
const ALERT_TABLE_CONSUMERS: AlertsTableProps['consumers'] = [AlertConsumers.SIEM];
|
||||
const RULE_TYPE_IDS = [ESQL_RULE_TYPE_ID, QUERY_RULE_TYPE_ID];
|
||||
const ROW_HEIGHTS_OPTIONS = { defaultHeight: 40 };
|
||||
const TOOLBAR_VISIBILITY: EuiDataGridToolBarVisibilityOptions = {
|
||||
export const ACTION_COLUMN_WIDTH = 98; // px
|
||||
export const ALERT_TABLE_CONSUMERS: AlertsTableProps['consumers'] = [AlertConsumers.SIEM];
|
||||
export const RULE_TYPE_IDS = [ESQL_RULE_TYPE_ID, QUERY_RULE_TYPE_ID];
|
||||
export const ROW_HEIGHTS_OPTIONS = { defaultHeight: 40 };
|
||||
export const TOOLBAR_VISIBILITY: EuiDataGridToolBarVisibilityOptions = {
|
||||
showDisplaySelector: false,
|
||||
showKeyboardShortcuts: false,
|
||||
showFullScreenSelector: false,
|
||||
};
|
||||
const GRID_STYLE: EuiDataGridStyle = { border: 'horizontal' };
|
||||
export const GRID_STYLE: EuiDataGridStyle = { border: 'horizontal' };
|
||||
|
||||
interface AdditionalTableContext {
|
||||
export interface AdditionalTableContext {
|
||||
/**
|
||||
* List of installed AI for SOC integrations
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue