From 63549cbc159bdaff1d127ef62f0da2bcaa4e22d1 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 18 Jul 2023 06:20:36 -0400 Subject: [PATCH] [8.9] [Security Solution][Fix] Empty Alert Table when upgrading from 8.8.x -> 8.9 (#162063) (#162120) # Backport This will backport the following commits from `main` to `8.9`: - [[Security Solution][Fix] Empty Alert Table when upgrading from 8.8.x -> 8.9 (#162063)](https://github.com/elastic/kibana/pull/162063) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Jatin Kathuria --- .../containers/local_storage/index.test.ts | 186 +++++- .../containers/local_storage/index.tsx | 59 +- .../containers/local_storage/test.data.ts | 579 ++++++++++++++++++ 3 files changed, 798 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/timelines/containers/local_storage/test.data.ts diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts index e9482e0c152e6..f715169e2abb9 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts @@ -15,6 +15,7 @@ import { getAllDataTablesInStorage, addTableInStorage, migrateAlertTableStateToTriggerActionsState, + migrateTriggerActionsVisibleColumnsAlertTable88xTo89, } from '.'; import { mockDataTableModel, createSecuritySolutionStorageMock } from '../../../common/mock'; @@ -22,6 +23,7 @@ import { useKibana } from '../../../common/lib/kibana'; import { VIEW_SELECTION } from '../../../../common/constants'; import type { DataTableModel, DataTableState } from '@kbn/securitysolution-data-table'; import { TableId } from '@kbn/securitysolution-data-table'; +import { v88xAlertOrignalData, v89xAlertsOriginalData } from './test.data'; jest.mock('../../../common/lib/kibana'); @@ -788,7 +790,7 @@ describe('SiemLocalStorage', () => { }); }); - describe('Trigger Actions Alert Table Migration', () => { + describe('Trigger Actions Alert Table Migration -> Migration from 8.7', () => { const legacyDataTableState: DataTableState['dataTable']['tableById'] = { 'alerts-page': { queryFields: [], @@ -1261,17 +1263,66 @@ describe('SiemLocalStorage', () => { ], sort: [{ '@timestamp': { order: 'desc' } }], visibleColumns: [ - '@timestamp', - 'kibana.alert.rule.name', - 'kibana.alert.severity', - 'kibana.alert.risk_score', - 'kibana.alert.reason', - 'host.name', - 'user.name', - 'process.name', - 'file.name', - 'source.ip', - 'destination.ip', + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + initialWidth: 180, + linkField: 'kibana.alert.rule.uuid', + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'process.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'file.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + initialWidth: 180, + }, ], }, }, @@ -1344,17 +1395,66 @@ describe('SiemLocalStorage', () => { { 'kibana.alert.rule.name': { order: 'desc' } }, ], visibleColumns: [ - '@timestamp', - 'kibana.alert.rule.name', - 'kibana.alert.severity', - 'kibana.alert.risk_score', - 'kibana.alert.reason', - 'host.name', - 'user.name', - 'process.name', - 'file.name', - 'source.ip', - 'destination.ip', + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + initialWidth: 180, + linkField: 'kibana.alert.rule.uuid', + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'process.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'file.name', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + initialWidth: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + initialWidth: 180, + }, ], }, }, @@ -1387,4 +1487,44 @@ describe('SiemLocalStorage', () => { } }); }); + + describe('should migrate Alert Table visible columns from v8.8.x', () => { + // PR: https://github.com/elastic/kibana/pull/161054 + beforeEach(() => storage.clear()); + it('should migrate correctly when upgrading from 8.8.x -> 8.9', () => { + Object.keys(v88xAlertOrignalData).forEach((k) => { + storage.set(k, v88xAlertOrignalData[k as keyof typeof v88xAlertOrignalData]); + }); + + migrateTriggerActionsVisibleColumnsAlertTable88xTo89(storage); + + Object.keys(v89xAlertsOriginalData).forEach((k) => { + const expectedResult = v89xAlertsOriginalData[k as keyof typeof v89xAlertsOriginalData]; + expect(storage.get(k)).toMatchObject(expectedResult); + }); + }); + it('should be a no-op when reinstalling from 8.9 when data is already present.', () => { + Object.keys(v89xAlertsOriginalData).forEach((k) => { + storage.set(k, v89xAlertsOriginalData[k as keyof typeof v89xAlertsOriginalData]); + }); + + migrateTriggerActionsVisibleColumnsAlertTable88xTo89(storage); + + Object.keys(v89xAlertsOriginalData).forEach((k) => { + const expectedResult = v89xAlertsOriginalData[k as keyof typeof v89xAlertsOriginalData]; + expect(storage.get(k)).toMatchObject(expectedResult); + }); + }); + + it('should be a no-op when installing 8.9 for the first time', () => { + migrateTriggerActionsVisibleColumnsAlertTable88xTo89(storage); + + expect( + storage.get('detection-engine-alert-table-securitySolution-alerts-page-gridView') + ).toBeNull(); + expect( + storage.get('detection-engine-alert-table-securitySolution-rule-details-gridView') + ).toBeNull(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx index c0bab6bfce78b..bab84294e0a67 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx @@ -77,6 +77,18 @@ export const migrateLegacyTimelinesToSecurityDataTable = (legacyTimelineTables: }, {} as { [K in TableIdLiteral]: DataTableModel }); }; +/* + * + * This migraiton only works for upgrading from + * 8.7 -> 8.8. Please do not edit this migration for any + * future release. + * + * If there is a migration that is required to be done for + * any future release. It should written as a saperate piece of code + * and should be called after this migration + * + * **/ + export const migrateAlertTableStateToTriggerActionsState = ( storage: Storage, legacyDataTableState: DataTableState['dataTable']['tableById'] @@ -100,7 +112,7 @@ export const migrateAlertTableStateToTriggerActionsState = ( sort: legacyDataTableState[tableKey].sort.map((sortCandidate) => ({ [sortCandidate.columnId]: { order: sortCandidate.sortDirection }, })), - visibleColumns: legacyDataTableState[tableKey].columns.map((c) => c.id), + visibleColumns: legacyDataTableState[tableKey].columns, }, }; }); @@ -110,7 +122,47 @@ export const migrateAlertTableStateToTriggerActionsState = ( storage.set(key, stateObj[key]); }) ); - return Object.assign(legacyDataTableState, triggersActionsState); +}; + +/* + * + * Used for migrating Alert Table from 8.8 => 8.9 + * */ +export const migrateTriggerActionsVisibleColumnsAlertTable88xTo89 = (storage: Storage) => { + const localStorageKeys = [ + `detection-engine-alert-table-${ALERTS_TABLE_REGISTRY_CONFIG_IDS.ALERTS_PAGE}-gridView`, + `detection-engine-alert-table-${ALERTS_TABLE_REGISTRY_CONFIG_IDS.RULE_DETAILS}-gridView`, + ]; + + localStorageKeys.forEach((key) => { + const alertTableData = storage.get(key); + if (!alertTableData) { + return; + } + if ('visibleColumns' in alertTableData) { + const visibleColumns = + alertTableData.visibleColumns as DataTableState['dataTable']['tableById'][string]['columns']; + const v89CompliantFormat = visibleColumns.every((val: unknown) => typeof val === 'string'); + if (v89CompliantFormat) { + return; + } + const newVisibleColumns = visibleColumns + .map((visibleCol) => { + if (typeof visibleCol === 'string') { + // if column format is 8.9 compliant already + return visibleCol; + } + // if column format is 8.8 + return visibleCol.id; + }) + .filter(Boolean); + + storage.set(key, { + ...alertTableData, + visibleColumns: newVisibleColumns, + }); + } + }); }; /** @@ -155,7 +207,8 @@ export const getDataTablesInStorageByIds = (storage: Storage, tableIds: TableIdL } } - allDataTables = migrateAlertTableStateToTriggerActionsState(storage, allDataTables); + migrateAlertTableStateToTriggerActionsState(storage, allDataTables); + migrateTriggerActionsVisibleColumnsAlertTable88xTo89(storage); return tableIds.reduce((acc, tableId) => { const tableModel = allDataTables[tableId]; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/test.data.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/test.data.ts new file mode 100644 index 0000000000000..2d77315ae84e3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/test.data.ts @@ -0,0 +1,579 @@ +/* + * 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. + */ + +export const v88xAlertOrignalData = { + 'detection-engine-alert-table-securitySolution-rule-details-gridView': { + columns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + schema: 'datetime', + }, + { + id: '_id', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + schema: 'string', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + schema: 'string', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + schema: 'numeric', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + schema: 'ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + schema: 'ip', + }, + ], + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + visibleColumns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + }, + ], + }, + 'detection-engine-alert-table-securitySolution-alerts-page-gridView': { + columns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + schema: 'datetime', + }, + { + id: '_id', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + schema: 'string', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + schema: 'string', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + schema: 'numeric', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + schema: 'ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + schema: 'ip', + }, + ], + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + visibleColumns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + }, + ], + }, +}; + +// when upgrading from 8.9.x => 8.9.y +export const v89xAlertsOriginalData = { + 'detection-engine-alert-table-securitySolution-rule-details-gridView': { + columns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + schema: 'datetime', + }, + { + id: '_id', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + schema: 'string', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + schema: 'string', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + schema: 'numeric', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + schema: 'ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + schema: 'ip', + }, + ], + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + visibleColumns: [ + '@timestamp', + 'kibana.alert.rule.name', + 'kibana.alert.severity', + 'kibana.alert.risk_score', + 'kibana.alert.reason', + 'host.name', + 'host.risk.calculated_level', + 'user.name', + 'user.risk.calculated_level', + 'process.name', + 'file.name', + 'source.ip', + 'destination.ip', + ], + }, + 'detection-engine-alert-table-securitySolution-alerts-page-gridView': { + columns: [ + { + initialWidth: 200, + columnHeaderType: 'not-filtered', + id: '@timestamp', + schema: 'datetime', + }, + { + id: '_id', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + displayAsText: 'Rule', + id: 'kibana.alert.rule.name', + linkField: 'kibana.alert.rule.uuid', + schema: 'string', + }, + { + initialWidth: 105, + columnHeaderType: 'not-filtered', + displayAsText: 'Severity', + id: 'kibana.alert.severity', + schema: 'string', + }, + { + initialWidth: 100, + columnHeaderType: 'not-filtered', + displayAsText: 'Risk Score', + id: 'kibana.alert.risk_score', + schema: 'numeric', + }, + { + initialWidth: 450, + columnHeaderType: 'not-filtered', + displayAsText: 'Reason', + id: 'kibana.alert.reason', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'host.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'user.risk.calculated_level', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'process.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'file.name', + schema: 'string', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'source.ip', + schema: 'ip', + }, + { + initialWidth: 180, + columnHeaderType: 'not-filtered', + id: 'destination.ip', + schema: 'ip', + }, + ], + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + visibleColumns: [ + '@timestamp', + 'kibana.alert.rule.name', + 'kibana.alert.severity', + 'kibana.alert.risk_score', + 'kibana.alert.reason', + 'host.name', + 'host.risk.calculated_level', + 'user.name', + 'user.risk.calculated_level', + 'process.name', + 'file.name', + 'source.ip', + 'destination.ip', + ], + }, +};