Skip to content

Commit

Permalink
[Security Solution] [Trigger Actions] Alert Table Refactoring (#149128)
Browse files Browse the repository at this point in the history
## Summary

This PR replaces the existing `Alert Table` used in Security solution &
cases with that of `triggers-actions-ui`.

Ideally, this PR does make any changes to the functionality of the
product and from user perspective. Nothing should Change.


‼️ Note for @elastic/security-threat-hunting-explore : This PR makes no
changes to the table used in Host/Users page.

## Things to test and changes

- @elastic/actionable-observability 
- The changes in observability plugin are done to accommodate the
changes in the API of `triggers-actions-ui` alert table.
- Requesting you to do desk-testing this PR once by using Alert Table as
you do on the daily basis.

- @elastic/response-ops  
- changes have been done in API as `security-solution` needed some
parameters/values to achieve the parity in the functionality as compared
to the current alert Table.

---------

Co-authored-by: Xavier Mouligneau <xavier.mouligneau@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people committed Feb 22, 2023
1 parent 3c262bb commit fe04a4c
Show file tree
Hide file tree
Showing 126 changed files with 4,503 additions and 1,007 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('Case View Page activity tab', () => {
await waitFor(async () => {
expect(getAlertsStateTableMock).toHaveBeenCalledWith({
alertsTableConfigurationRegistry: expect.anything(),
configurationId: 'securitySolution',
configurationId: 'securitySolution-case',
featureIds: ['siem', 'observability'],
id: 'case-details-alerts-securitySolution',
query: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ export const CaseViewAlerts = ({ caseData }: CaseViewAlertsProps) => {
const { isLoading: isLoadingAlertFeatureIds, data: alertFeatureIds } =
useGetFeatureIds(alertRegistrationContexts);

const configId =
caseData.owner === SECURITY_SOLUTION_OWNER ? `${caseData.owner}-case` : caseData.owner;

const alertStateProps = {
alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry,
configurationId: caseData.owner,
configurationId: configId,
id: `case-details-alerts-${caseData.owner}`,
flyoutSize: (alertFeatureIds?.includes('siem') ? 'm' : 's') as EuiFlyoutSize,
featureIds: alertFeatureIds ?? [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import type { GetRenderCellValue } from '@kbn/triggers-actions-ui-plugin/public';
import { TIMESTAMP } from '@kbn/rule-data-utils';
import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types';
import { casesFeatureId, observabilityFeatureId } from '../../common';
import { useBulkAddToCaseActions } from '../hooks/use_alert_bulk_case_actions';
import { useBulkAddToCaseTriggerActions } from '../hooks/use_alert_bulk_case_actions';
import { TopAlert, useToGetInternalFlyout } from '../pages/alerts';
import { getRenderCellValue } from '../pages/alerts/components/render_cell_value';
import { addDisplayNames } from '../pages/alerts/containers/alerts_table/add_display_names';
Expand All @@ -21,7 +22,7 @@ import type { ConfigSchema } from '../plugin';
const getO11yAlertsTableConfiguration = (
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry,
config: ConfigSchema
) => ({
): AlertsTableConfigurationRegistry => ({
id: observabilityFeatureId,
casesFeatureId,
columns: alertO11yColumns.map(addDisplayNames),
Expand All @@ -36,7 +37,7 @@ const getO11yAlertsTableConfiguration = (
},
],
useActionsColumn: getRowActions(observabilityRuleTypeRegistry, config),
useBulkActions: useBulkAddToCaseActions,
useBulkActions: useBulkAddToCaseTriggerActions,
useInternalFlyout: () => {
const { header, body, footer } = useToGetInternalFlyout(observabilityRuleTypeRegistry);
return { header, body, footer };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,12 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi
selectCaseModal,
]);
};

/*
* Wrapper hook to support trigger actions
* registry props for the alert table
*
* */
export const useBulkAddToCaseTriggerActions = () => {
return useBulkAddToCaseActions({});
};
15 changes: 14 additions & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* https://mariusschulz.com/blog/literal-type-widening-in-typescript
* Please follow this convention when adding to this file
*/

export const APP_ID = 'securitySolution' as const;
export const APP_UI_ID = 'securitySolutionUI' as const;
export const CASES_FEATURE_ID = 'securitySolutionCases' as const;
Expand Down Expand Up @@ -499,3 +498,17 @@ export const DEFAULT_DETECTION_PAGE_FILTERS = [
fieldName: 'host.name',
},
];

/** This local storage key stores the `Grid / Event rendered view` selection */
export const ALERTS_TABLE_VIEW_SELECTION_KEY = 'securitySolution.alerts.table.view-selection';

export const VIEW_SELECTION = {
gridView: 'gridView',
eventRenderedView: 'eventRenderedView',
} as const;

export const ALERTS_TABLE_REGISTRY_CONFIG_IDS = {
ALERTS_PAGE: `${APP_ID}-alerts-page`,
RULE_DETAILS: `${APP_ID}-rule-details`,
CASE: `${APP_ID}-case`,
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import * as runtimeTypes from 'io-ts';
import type { VIEW_SELECTION } from '../../constants';

export enum Direction {
asc = 'asc',
Expand Down Expand Up @@ -33,6 +34,7 @@ export enum TableId {
alternateTest = 'alternateTest',
rulePreview = 'rule-preview',
kubernetesPageSessions = 'kubernetes-page-sessions',
alertsOnCasePage = 'alerts-case-page',
}

const TableIdLiteralRt = runtimeTypes.union([
Expand All @@ -46,4 +48,9 @@ const TableIdLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TableId.rulePreview),
runtimeTypes.literal(TableId.kubernetesPageSessions),
]);

export type TableIdLiteral = runtimeTypes.TypeOf<typeof TableIdLiteralRt>;

export type ViewSelectionTypes = keyof typeof VIEW_SELECTION;

export type ViewSelection = typeof VIEW_SELECTION[ViewSelectionTypes];
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { JSON_TEXT } from '../../screens/alerts_details';

import { expandFirstAlert, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts';
import { expandFirstAlert, waitForAlerts } from '../../tasks/alerts';
import { openJsonView } from '../../tasks/alerts_details';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
Expand All @@ -25,7 +25,7 @@ describe('Alert details with unmapped fields', () => {
esArchiverCCSLoad('unmapped_fields');
createCustomRuleEnabled(getUnmappedCCSRule());
visitWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlerts();
expandFirstAlert();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { esArchiverCCSLoad } from '../../tasks/es_archiver';
import { getCCSEqlRule } from '../../objects/rule';

import { ALERT_DATA_GRID, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { ALERTS_COUNT, ALERT_DATA_GRID } from '../../screens/alerts';

import {
filterByCustomRules,
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('Detection rules', function () {
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();

cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts);
cy.get(ALERT_DATA_GRID)
.invoke('text')
.then((text) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,39 @@ import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { getNewRule } from '../../objects/rule';
import { refreshPage } from '../../tasks/security_header';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { openEventsViewerFieldsBrowser } from '../../tasks/hosts/events';
import { assertFieldDisplayed, createField } from '../../tasks/create_runtime_field';
import { createField } from '../../tasks/create_runtime_field';
import { openAlertsFieldBrowser } from '../../tasks/alerts';
import { deleteRuntimeField } from '../../tasks/sourcerer';
import { GET_DATA_GRID_HEADER } from '../../screens/common/data_grid';
import { GET_TIMELINE_HEADER } from '../../screens/timeline';

const alertRunTimeField = 'field.name.alert.page';
const timelineRuntimeField = 'field.name.timeline';

describe('Create DataView runtime field', () => {
before(() => {
deleteRuntimeField('security-solution-default', alertRunTimeField);
deleteRuntimeField('security-solution-default', timelineRuntimeField);
login();
});

it('adds field to alert table', () => {
const fieldName = 'field.name.alert.page';
visit(ALERTS_URL);
createCustomRuleEnabled(getNewRule());
refreshPage();
waitForAlertsToPopulate();
openEventsViewerFieldsBrowser();

createField(fieldName);
assertFieldDisplayed(fieldName, 'alerts');
openAlertsFieldBrowser();
createField(alertRunTimeField);
cy.get(GET_DATA_GRID_HEADER(alertRunTimeField)).should('exist');
});

it('adds field to timeline', () => {
const fieldName = 'field.name.timeline';
visit(HOSTS_URL);
openTimelineUsingToggle();
populateTimeline();
openTimelineFieldsBrowser();

createField(fieldName);
assertFieldDisplayed(fieldName);
createField(timelineRuntimeField);
cy.get(GET_TIMELINE_HEADER(timelineRuntimeField)).should('exist');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { expandFirstAlert, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts';
import { expandFirstAlert, waitForAlerts } from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
Expand Down Expand Up @@ -35,7 +35,7 @@ describe('Alert Details Page Navigation', () => {
describe('context menu', () => {
beforeEach(() => {
visit(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlerts();
});

it('should navigate to the details page from the alert context menu', () => {
Expand All @@ -55,7 +55,7 @@ describe('Alert Details Page Navigation', () => {
describe('flyout', () => {
beforeEach(() => {
visit(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
waitForAlerts();
});

it('should navigate to the details page from the alert flyout', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('Alerts cell actions', () => {
.first()
.invoke('text')
.then((severityVal) => {
scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER);
scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES);
filterForAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0);
cy.get(FILTER_BADGE)
.first()
Expand All @@ -75,7 +75,7 @@ describe('Alerts cell actions', () => {
.first()
.invoke('text')
.then((severityVal) => {
scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER);
scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES);
addAlertPropertyToTimeline(ALERT_TABLE_SEVERITY_VALUES, 0);
openActiveTimeline();
cy.get(PROVIDER_BADGE)
Expand All @@ -101,7 +101,7 @@ describe('Alerts cell actions', () => {
.first()
.invoke('text')
.then(() => {
scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER);
scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES);
showTopNAlertProperty(ALERT_TABLE_SEVERITY_VALUES, 0);
cy.get(SHOW_TOP_N_HEADER).first().should('have.text', `Top kibana.alert.severity`);
});
Expand All @@ -114,7 +114,7 @@ describe('Alerts cell actions', () => {
.first()
.invoke('text')
.then(() => {
scrollAlertTableColumnIntoView(ALERT_TABLE_FILE_NAME_HEADER);
scrollAlertTableColumnIntoView(ALERT_TABLE_SEVERITY_VALUES);
cy.window().then((win) => {
cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { APP_ID, DEFAULT_DETECTION_PAGE_FILTERS } from '../../../common/constant
import { formatPageFilterSearchParam } from '../../../common/utils/format_page_filter_search_param';
import {
markAcknowledgedFirstAlert,
refreshAlertPageFilter,
resetFilters,
selectCountTable,
waitForAlerts,
Expand Down Expand Up @@ -152,7 +151,7 @@ describe.skip('Detections : Page Filters', () => {
.then((noOfAlerts) => {
const originalAlertCount = noOfAlerts.split(' ')[0];
markAcknowledgedFirstAlert();
refreshAlertPageFilter();
waitForAlerts();
cy.get(OPTION_LIST_VALUES).eq(0).click();
cy.get(OPTION_SELECTABLE(0, 'acknowledged')).should('be.visible');
cy.get(ALERTS_COUNT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import { getNewRule } from '../../objects/rule';
import {
NUMBER_OF_ALERTS,
HOST_RISK_HEADER_COLIMN,
USER_RISK_HEADER_COLIMN,
HOST_RISK_COLUMN,
USER_RISK_COLUMN,
ACTION_COLUMN,
ALERTS_COUNT,
} from '../../screens/alerts';
import { ENRICHED_DATA_ROW } from '../../screens/alerts_details';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
Expand Down Expand Up @@ -56,7 +56,7 @@ describe('Enrichment', () => {
});

it('Should has enrichment fields', function () {
cy.get(NUMBER_OF_ALERTS)
cy.get(ALERTS_COUNT)
.invoke('text')
.should('match', /^[1-9].+$/); // Any number of alerts
cy.get(HOST_RISK_HEADER_COLIMN).contains('host.risk.calculated_level');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
getNewOverrideRule,
} from '../../objects/rule';
import { getTimeline } from '../../objects/timeline';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../screens/alerts';

import {
CUSTOM_RULES_BTN,
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('Custom query rules', () => {
waitForAlertsToPopulate();

cy.log('Asserting that alerts have been generated after the creation');
cy.get(NUMBER_OF_ALERTS)
cy.get(ALERTS_COUNT)
.invoke('text')
.should('match', /^[1-9].+$/); // Any number of alerts
cy.get(ALERT_GRID_CELL).contains(ruleFields.ruleName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { getDataViewRule } from '../../objects/rule';
import type { CompleteTimeline } from '../../objects/timeline';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../screens/alerts';

import {
CUSTOM_RULES_BTN,
Expand Down Expand Up @@ -160,7 +160,7 @@ describe('Custom query rules', () => {
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();

cy.get(NUMBER_OF_ALERTS)
cy.get(ALERTS_COUNT)
.invoke('text')
.should('match', /^[1-9].+$/);
cy.get(ALERT_GRID_CELL).contains(this.rule.name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../objects/rule';

import { ALERT_DATA_GRID, NUMBER_OF_ALERTS } from '../../screens/alerts';
import { ALERTS_COUNT, ALERT_DATA_GRID } from '../../screens/alerts';
import {
CUSTOM_RULES_BTN,
RISK_SCORE,
Expand Down Expand Up @@ -148,7 +148,7 @@ describe('EQL rules', () => {
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();

cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts);
cy.get(ALERT_DATA_GRID)
.invoke('text')
.then((text) => {
Expand Down Expand Up @@ -192,7 +192,7 @@ describe('EQL rules', () => {
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();

cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfSequenceAlerts);
cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfSequenceAlerts);
cy.get(ALERT_DATA_GRID)
.invoke('text')
.then((text) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
ALERT_RULE_NAME,
ALERT_RISK_SCORE,
ALERT_SEVERITY,
NUMBER_OF_ALERTS,
ALERTS_COUNT,
} from '../../screens/alerts';
import {
CUSTOM_RULES_BTN,
Expand Down Expand Up @@ -492,7 +492,7 @@ describe('indicator match', () => {
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();

cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts);
cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name);
cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase());
cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.riskScore);
Expand Down

0 comments on commit fe04a4c

Please sign in to comment.