[Discover] Migrate es query legacy rule params (#129179)

* [Alerting] migrate es query legacy rule params

* [Discover] add closing bracket

* [Discover] apply suggestions

* [Discover] resolve comments

* [Discover] change version

* [Discover] apply suggestions

* [Discover] apply suggestions
This commit is contained in:
Dmitry Tomashevich 2022-05-06 12:15:37 +05:00 committed by GitHub
parent 0d4cc4dc32
commit 2c091706c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 570 additions and 133 deletions

View file

@ -217,3 +217,13 @@ export interface ShardFailure {
};
shard: number;
}
export function isSerializedSearchSource(
maybeSerializedSearchSource: unknown
): maybeSerializedSearchSource is SerializedSearchSourceFields {
return (
typeof maybeSerializedSearchSource === 'object' &&
maybeSerializedSearchSource !== null &&
!Array.isArray(maybeSerializedSearchSource)
);
}

View file

@ -350,6 +350,7 @@ Object {
testMigrateMatchAllQuery(migrationFn);
});
});
it('should apply search source migrations within saved search', () => {
const savedSearch = {
attributes: {
@ -379,4 +380,27 @@ Object {
},
});
});
it('should not apply search source migrations within saved search when searchSourceJSON is not an object', () => {
const savedSearch = {
attributes: {
kibanaSavedObjectMeta: {
searchSourceJSON: '5',
},
},
} as SavedObjectUnsanitizedDoc;
const versionToTest = '9.1.2';
const migrations = getAllMigrations({
[versionToTest]: (state) => ({ ...state, migrated: true }),
});
expect(migrations[versionToTest](savedSearch, {} as SavedObjectMigrationContext)).toEqual({
attributes: {
kibanaSavedObjectMeta: {
searchSourceJSON: '5',
},
},
});
});
});

View file

@ -17,7 +17,7 @@ import type {
import { mergeSavedObjectMigrationMaps } from '@kbn/core/server';
import { DEFAULT_QUERY_LANGUAGE } from '@kbn/data-plugin/server';
import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import type { SerializedSearchSourceFields } from '@kbn/data-plugin/common';
import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common';
export interface SavedSearchMigrationAttributes extends SavedObjectAttributes {
kibanaSavedObjectMeta: {
@ -135,27 +135,31 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn<any, any> = (doc) =
/**
* This creates a migration map that applies search source migrations
*/
const getSearchSourceMigrations = (searchSourceMigrations: MigrateFunctionsObject) =>
const getSearchSourceMigrations = (
searchSourceMigrations: MigrateFunctionsObject
): MigrateFunctionsObject =>
mapValues<MigrateFunctionsObject, MigrateFunction>(
searchSourceMigrations,
(migrate: MigrateFunction<SerializedSearchSourceFields>): MigrateFunction =>
(state) => {
const _state = state as unknown as { attributes: SavedSearchMigrationAttributes };
const _state = state as { attributes: SavedSearchMigrationAttributes };
const parsedSearchSourceJSON = _state.attributes.kibanaSavedObjectMeta.searchSourceJSON;
if (!parsedSearchSourceJSON) return _state;
return {
..._state,
attributes: {
..._state.attributes,
kibanaSavedObjectMeta: {
..._state.attributes.kibanaSavedObjectMeta,
searchSourceJSON: JSON.stringify(migrate(JSON.parse(parsedSearchSourceJSON))),
const parsedSearchSourceJSON = JSON.parse(
_state.attributes.kibanaSavedObjectMeta.searchSourceJSON
);
if (isSerializedSearchSource(parsedSearchSourceJSON)) {
return {
..._state,
attributes: {
..._state.attributes,
kibanaSavedObjectMeta: {
..._state.attributes.kibanaSavedObjectMeta,
searchSourceJSON: JSON.stringify(migrate(parsedSearchSourceJSON)),
},
},
},
};
};
}
return _state;
}
);
@ -171,6 +175,6 @@ export const getAllMigrations = (
): SavedObjectMigrationMap => {
return mergeSavedObjectMigrationMaps(
searchMigrations,
getSearchSourceMigrations(searchSourceMigrations) as unknown as SavedObjectMigrationMap
getSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap
);
};

View file

@ -2470,6 +2470,31 @@ describe('migration visualization', () => {
});
});
it('should not apply search source migrations within visualization when searchSourceJSON is not an object', () => {
const visualizationDoc = {
attributes: {
kibanaSavedObjectMeta: {
searchSourceJSON: '5',
},
},
} as SavedObjectUnsanitizedDoc;
const versionToTest = '1.2.4';
const visMigrations = getAllMigrations({
[versionToTest]: (state) => ({ ...state, migrated: true }),
});
expect(
visMigrations[versionToTest](visualizationDoc, {} as SavedObjectMigrationContext)
).toEqual({
attributes: {
kibanaSavedObjectMeta: {
searchSourceJSON: '5',
},
},
});
});
describe('8.1.0 pie - labels and addLegend migration', () => {
const getDoc = (addLegend: boolean, lastLevel: boolean = false) => ({
attributes: {

View file

@ -11,7 +11,11 @@ import type { SavedObjectMigrationFn, SavedObjectMigrationMap } from '@kbn/core/
import { mergeSavedObjectMigrationMaps } from '@kbn/core/server';
import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import { DEFAULT_QUERY_LANGUAGE, SerializedSearchSourceFields } from '@kbn/data-plugin/common';
import {
DEFAULT_QUERY_LANGUAGE,
isSerializedSearchSource,
SerializedSearchSourceFields,
} from '@kbn/data-plugin/common';
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common';
import {
commonAddSupportOfDualIndexSelectionModeInTSVB,
@ -1215,27 +1219,31 @@ const visualizationSavedObjectTypeMigrations = {
/**
* This creates a migration map that applies search source migrations to legacy visualization SOs
*/
const getVisualizationSearchSourceMigrations = (searchSourceMigrations: MigrateFunctionsObject) =>
const getVisualizationSearchSourceMigrations = (
searchSourceMigrations: MigrateFunctionsObject
): MigrateFunctionsObject =>
mapValues<MigrateFunctionsObject, MigrateFunction>(
searchSourceMigrations,
(migrate: MigrateFunction<SerializedSearchSourceFields>): MigrateFunction =>
(state) => {
const _state = state as unknown as { attributes: VisualizationSavedObjectAttributes };
const _state = state as { attributes: VisualizationSavedObjectAttributes };
const parsedSearchSourceJSON = _state.attributes.kibanaSavedObjectMeta.searchSourceJSON;
if (!parsedSearchSourceJSON) return _state;
return {
..._state,
attributes: {
..._state.attributes,
kibanaSavedObjectMeta: {
..._state.attributes.kibanaSavedObjectMeta,
searchSourceJSON: JSON.stringify(migrate(JSON.parse(parsedSearchSourceJSON))),
const parsedSearchSourceJSON = JSON.parse(
_state.attributes.kibanaSavedObjectMeta.searchSourceJSON
);
if (isSerializedSearchSource(parsedSearchSourceJSON)) {
return {
..._state,
attributes: {
..._state.attributes,
kibanaSavedObjectMeta: {
..._state.attributes.kibanaSavedObjectMeta,
searchSourceJSON: JSON.stringify(migrate(parsedSearchSourceJSON)),
},
},
},
};
};
}
return _state;
}
);
@ -1244,7 +1252,5 @@ export const getAllMigrations = (
): SavedObjectMigrationMap =>
mergeSavedObjectMigrationMaps(
visualizationSavedObjectTypeMigrations,
getVisualizationSearchSourceMigrations(
searchSourceMigrations
) as unknown as SavedObjectMigrationMap
getVisualizationSearchSourceMigrations(searchSourceMigrations) as SavedObjectMigrationMap
);

View file

@ -21,6 +21,7 @@ import { eventLogMock } from '@kbn/event-log-plugin/server/mocks';
import { actionsMock } from '@kbn/actions-plugin/server/mocks';
import { dataPluginMock } from '@kbn/data-plugin/server/mocks';
import { monitoringCollectionMock } from '@kbn/monitoring-collection-plugin/server/mocks';
import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server';
import { spacesMock } from '@kbn/spaces-plugin/server/mocks';
const generateAlertingConfig = (): AlertingConfig => ({
@ -66,6 +67,7 @@ describe('Alerting Plugin', () => {
actions: actionsMock.createSetup(),
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
};
let plugin: AlertingPlugin;
@ -207,6 +209,7 @@ describe('Alerting Plugin', () => {
actions: actionsMock.createSetup(),
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
});
const startContract = plugin.start(coreMock.createStart(), {
@ -246,6 +249,7 @@ describe('Alerting Plugin', () => {
actions: actionsMock.createSetup(),
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
});
const startContract = plugin.start(coreMock.createStart(), {
@ -296,6 +300,7 @@ describe('Alerting Plugin', () => {
actions: actionsMock.createSetup(),
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
});
const startContract = plugin.start(coreMock.createStart(), {

View file

@ -10,6 +10,7 @@ import { BehaviorSubject } from 'rxjs';
import { pick } from 'lodash';
import { UsageCollectionSetup, UsageCounter } from '@kbn/usage-collection-plugin/server';
import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server';
import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server';
import {
EncryptedSavedObjectsPluginSetup,
EncryptedSavedObjectsPluginStart,
@ -140,6 +141,7 @@ export interface AlertingPluginsSetup {
eventLog: IEventLogService;
statusService: StatusServiceSetup;
monitoringCollection: MonitoringCollectionSetup;
data: DataPluginSetup;
}
export interface AlertingPluginsStart {
@ -247,12 +249,16 @@ export class AlertingPlugin {
// Usage counter for telemetry
this.usageCounter = plugins.usageCollection?.createUsageCounter(ALERTS_FEATURE_ID);
const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind(
plugins.data.search.searchSource
);
setupSavedObjects(
core.savedObjects,
plugins.encryptedSavedObjects,
this.ruleTypeRegistry,
this.logger,
plugins.actions.isPreconfiguredConnector
plugins.actions.isPreconfiguredConnector,
getSearchSourceMigrations
);
initializeApiKeyInvalidator(

View file

@ -12,6 +12,7 @@ import type {
SavedObjectsServiceSetup,
} from '@kbn/core/server';
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
import { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common';
import { alertMappings } from './mappings';
import { getMigrations } from './migrations';
import { transformRulesForExport } from './transform_rule_for_export';
@ -51,14 +52,15 @@ export function setupSavedObjects(
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
ruleTypeRegistry: RuleTypeRegistry,
logger: Logger,
isPreconfigured: (connectorId: string) => boolean
isPreconfigured: (connectorId: string) => boolean,
getSearchSourceMigrations: () => MigrateFunctionsObject
) {
savedObjects.registerType({
name: 'alert',
hidden: true,
namespaceType: 'multiple-isolated',
convertToMultiNamespaceTypeVersion: '8.0.0',
migrations: getMigrations(encryptedSavedObjects, isPreconfigured),
migrations: getMigrations(encryptedSavedObjects, getSearchSourceMigrations(), isPreconfigured),
mappings: alertMappings,
management: {
displayName: 'rule',

View file

@ -8,7 +8,7 @@
import uuid from 'uuid';
import { getMigrations, isAnyActionSupportIncidents } from './migrations';
import { RawRule } from '../types';
import { SavedObjectUnsanitizedDoc } from '@kbn/core/server';
import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server';
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
import { migrationMocks } from '@kbn/core/server/mocks';
import { RuleType, ruleTypeMappings } from '@kbn/securitysolution-rules';
@ -25,7 +25,7 @@ describe('successful migrations', () => {
});
describe('7.10.0', () => {
test('marks alerts as legacy', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({});
expect(migration710(alert, migrationContext)).toMatchObject({
...alert,
@ -39,7 +39,7 @@ describe('successful migrations', () => {
});
test('migrates the consumer for metrics', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
consumer: 'metrics',
});
@ -56,7 +56,7 @@ describe('successful migrations', () => {
});
test('migrates the consumer for siem', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
consumer: 'securitySolution',
});
@ -73,7 +73,7 @@ describe('successful migrations', () => {
});
test('migrates the consumer for alerting', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
consumer: 'alerting',
});
@ -90,7 +90,7 @@ describe('successful migrations', () => {
});
test('migrates PagerDuty actions to set a default dedupkey of the AlertId', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
actions: [
{
@ -127,7 +127,7 @@ describe('successful migrations', () => {
});
test('skips PagerDuty actions with a specified dedupkey', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
actions: [
{
@ -165,7 +165,7 @@ describe('successful migrations', () => {
});
test('skips PagerDuty actions with an eventAction of "trigger"', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
actions: [
{
@ -204,7 +204,7 @@ describe('successful migrations', () => {
});
test('creates execution status', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData();
const dateStart = Date.now();
const migratedAlert = migration710(alert, migrationContext);
@ -232,7 +232,7 @@ describe('successful migrations', () => {
describe('7.11.0', () => {
test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => {
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0'];
const alert = getMockData({}, true);
expect(migration711(alert, migrationContext)).toEqual({
...alert,
@ -245,7 +245,7 @@ describe('successful migrations', () => {
});
test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => {
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0'];
const alert = getMockData({});
expect(migration711(alert, migrationContext)).toEqual({
...alert,
@ -258,7 +258,7 @@ describe('successful migrations', () => {
});
test('add notifyWhen=onActiveAlert when throttle is null', () => {
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0'];
const alert = getMockData({});
expect(migration711(alert, migrationContext)).toEqual({
...alert,
@ -271,7 +271,7 @@ describe('successful migrations', () => {
});
test('add notifyWhen=onActiveAlert when throttle is set', () => {
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0'];
const alert = getMockData({ throttle: '5m' });
expect(migration711(alert, migrationContext)).toEqual({
...alert,
@ -286,7 +286,9 @@ describe('successful migrations', () => {
describe('7.11.2', () => {
test('transforms connectors that support incident correctly', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -428,7 +430,9 @@ describe('successful migrations', () => {
});
test('it transforms only subAction=pushToService', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -447,7 +451,9 @@ describe('successful migrations', () => {
});
test('it does not transforms other connectors', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -526,7 +532,9 @@ describe('successful migrations', () => {
});
test('it does not transforms alerts when the right structure connectors is already applied', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -563,7 +571,9 @@ describe('successful migrations', () => {
});
test('if incident attribute is an empty object, copy back the related attributes from subActionParams back to incident', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -625,7 +635,9 @@ describe('successful migrations', () => {
});
test('custom action does not get migrated/loss', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
actions: [
{
@ -654,7 +666,7 @@ describe('successful migrations', () => {
describe('7.13.0', () => {
test('security solution alerts get migrated and remove null values', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -748,7 +760,7 @@ describe('successful migrations', () => {
});
test('non-null values in security solution alerts are not modified', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -815,7 +827,7 @@ describe('successful migrations', () => {
});
test('security solution threshold alert with string in threshold.field is migrated to array', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -846,7 +858,7 @@ describe('successful migrations', () => {
});
test('security solution threshold alert with empty string in threshold.field is migrated to empty array', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -877,7 +889,7 @@ describe('successful migrations', () => {
});
test('security solution threshold alert with array in threshold.field and cardinality is left alone', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -919,7 +931,7 @@ describe('successful migrations', () => {
});
test('security solution ML alert with string in machineLearningJobId is converted to an array', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -945,7 +957,7 @@ describe('successful migrations', () => {
});
test('security solution ML alert with an array in machineLearningJobId is preserved', () => {
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration713 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.13.0'];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -973,7 +985,9 @@ describe('successful migrations', () => {
describe('7.14.1', () => {
test('security solution author field is migrated to array if it is undefined', () => {
const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1'];
const migration7141 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.14.1'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {},
@ -991,7 +1005,9 @@ describe('successful migrations', () => {
});
test('security solution author field does not override existing values if they exist', () => {
const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1'];
const migration7141 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.14.1'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -1015,7 +1031,9 @@ describe('successful migrations', () => {
describe('7.15.0', () => {
test('security solution is migrated to saved object references if it has 1 exceptionsList', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -1044,7 +1062,9 @@ describe('successful migrations', () => {
});
test('security solution is migrated to saved object references if it has 2 exceptionsLists', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -1084,7 +1104,9 @@ describe('successful migrations', () => {
});
test('security solution is migrated to saved object references if it has 3 exceptionsLists', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -1135,7 +1157,9 @@ describe('successful migrations', () => {
});
test('security solution does not change anything if exceptionsList is missing', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = getMockData({
alertTypeId: 'siem.signals',
params: {
@ -1147,7 +1171,9 @@ describe('successful migrations', () => {
});
test('security solution will keep existing references if we do not have an exceptionsList but we do already have references', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1177,7 +1203,9 @@ describe('successful migrations', () => {
});
test('security solution keep any foreign references if they exist but still migrate other references', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1242,7 +1270,9 @@ describe('successful migrations', () => {
});
test('security solution is idempotent and if re-run on the same migrated data will keep the same items', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1282,7 +1312,9 @@ describe('successful migrations', () => {
});
test('security solution will migrate with only missing data if we have partially migrated data', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1331,7 +1363,9 @@ describe('successful migrations', () => {
});
test('security solution will not migrate if exception list if it is invalid data', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1345,7 +1379,9 @@ describe('successful migrations', () => {
});
test('security solution will migrate valid data if it is mixed with invalid data', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1387,7 +1423,9 @@ describe('successful migrations', () => {
});
test('security solution will not migrate if exception list is invalid data but will keep existing references', () => {
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
const migration7150 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.15.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.signals',
@ -1419,7 +1457,7 @@ describe('successful migrations', () => {
describe('7.16.0', () => {
test('add legacyId field to alert - set to SavedObject id attribute', () => {
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const alert = getMockData({}, true);
expect(migration716(alert, migrationContext)).toEqual({
...alert,
@ -1434,7 +1472,7 @@ describe('successful migrations', () => {
isPreconfigured.mockReset();
isPreconfigured.mockReturnValueOnce(true);
isPreconfigured.mockReturnValueOnce(false);
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const rule = {
...getMockData({
actions: [
@ -1510,7 +1548,7 @@ describe('successful migrations', () => {
isPreconfigured.mockReturnValueOnce(true);
isPreconfigured.mockReturnValueOnce(false);
isPreconfigured.mockReturnValueOnce(false);
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const rule = {
...getMockData({
actions: [
@ -1593,7 +1631,7 @@ describe('successful migrations', () => {
test('does nothing to rules with no references', () => {
isPreconfigured.mockReset();
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const rule = {
...getMockData({
actions: [
@ -1629,7 +1667,7 @@ describe('successful migrations', () => {
test('does nothing to rules with no action references', () => {
isPreconfigured.mockReset();
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const rule = {
...getMockData({
actions: [
@ -1671,7 +1709,7 @@ describe('successful migrations', () => {
test('does nothing to rules with references but no actions', () => {
isPreconfigured.mockReset();
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration716 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.16.0'];
const rule = {
...getMockData({
actions: [],
@ -1699,7 +1737,9 @@ describe('successful migrations', () => {
});
test('security solution is migrated to saved object references if it has a "ruleAlertId"', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = getMockData({
alertTypeId: 'siem.notifications',
params: {
@ -1724,7 +1764,9 @@ describe('successful migrations', () => {
});
test('security solution does not migrate anything if its type is not siem.notifications', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = getMockData({
alertTypeId: 'other-type',
params: {
@ -1741,7 +1783,9 @@ describe('successful migrations', () => {
});
});
test('security solution does not change anything if "ruleAlertId" is missing', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = getMockData({
alertTypeId: 'siem.notifications',
params: {},
@ -1757,7 +1801,9 @@ describe('successful migrations', () => {
});
test('security solution will keep existing references if we do not have a "ruleAlertId" but we do already have references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.notifications',
@ -1789,7 +1835,9 @@ describe('successful migrations', () => {
});
test('security solution will keep any foreign references if they exist but still migrate other "ruleAlertId" references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.notifications',
@ -1828,7 +1876,9 @@ describe('successful migrations', () => {
});
test('security solution is idempotent and if re-run on the same migrated data will keep the same items "ruleAlertId" references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.notifications',
@ -1862,7 +1912,9 @@ describe('successful migrations', () => {
});
test('security solution will not migrate "ruleAlertId" if it is invalid data', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.notifications',
@ -1882,7 +1934,9 @@ describe('successful migrations', () => {
});
test('security solution will not migrate "ruleAlertId" if it is invalid data but will keep existing references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: 'siem.notifications',
@ -1916,7 +1970,9 @@ describe('successful migrations', () => {
});
test('geo-containment alert migration extracts boundary and index references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: '.geo-containment',
@ -1944,7 +2000,9 @@ describe('successful migrations', () => {
});
test('geo-containment alert migration should preserve foreign references', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: '.geo-containment',
@ -1984,7 +2042,9 @@ describe('successful migrations', () => {
});
test('geo-containment alert migration ignores other alert-types', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const alert = {
...getMockData({
alertTypeId: '.foo',
@ -2008,13 +2068,13 @@ describe('successful migrations', () => {
describe('8.0.0', () => {
test('no op migration for rules SO', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData({}, true);
expect(migration800(alert, migrationContext)).toEqual(alert);
});
test('add threatIndicatorPath default value to threat match rules if missing', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData(
{ params: { type: 'threat_match' }, alertTypeId: 'siem.signals' },
true
@ -2025,7 +2085,7 @@ describe('successful migrations', () => {
});
test('doesnt change threatIndicatorPath value in threat match rules if value is present', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData(
{
params: { type: 'threat_match', threatIndicatorPath: 'custom.indicator.path' },
@ -2039,7 +2099,7 @@ describe('successful migrations', () => {
});
test('doesnt change threatIndicatorPath value in other rules', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData({ params: { type: 'eql' }, alertTypeId: 'siem.signals' }, true);
expect(migration800(alert, migrationContext).attributes.params.threatIndicatorPath).toEqual(
undefined
@ -2047,7 +2107,7 @@ describe('successful migrations', () => {
});
test('doesnt change threatIndicatorPath value if not a siem.signals rule', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData(
{ params: { type: 'threat_match' }, alertTypeId: 'not.siem.signals' },
true
@ -2058,7 +2118,7 @@ describe('successful migrations', () => {
});
test('doesnt change AAD rule params if not a siem.signals rule', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
const alert = getMockData(
{ params: { outputIndex: 'output-index', type: 'query' }, alertTypeId: 'not.siem.signals' },
true
@ -2073,7 +2133,9 @@ describe('successful migrations', () => {
test.each(Object.keys(ruleTypeMappings) as RuleType[])(
'changes AAD rule params accordingly if rule is a siem.signals %p rule',
(ruleType) => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.0.0'
];
const alert = getMockData(
{ params: { outputIndex: 'output-index', type: ruleType }, alertTypeId: 'siem.signals' },
true
@ -2118,7 +2180,7 @@ describe('successful migrations', () => {
);
test('Does not update rule tags if rule has already been enabled', () => {
const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured);
const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured);
const migration800 = migrations['8.0.0'];
const migration801 = migrations['8.0.1'];
@ -2141,7 +2203,7 @@ describe('successful migrations', () => {
});
test('Does not update rule tags if rule was already disabled before upgrading to 8.0', () => {
const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured);
const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured);
const migration800 = migrations['8.0.0'];
const migration801 = migrations['8.0.1'];
@ -2161,7 +2223,7 @@ describe('successful migrations', () => {
});
test('Updates rule tags if rule was auto-disabled in 8.0 upgrade and not reenabled', () => {
const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured);
const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured);
const migration800 = migrations['8.0.0'];
const migration801 = migrations['8.0.1'];
@ -2181,7 +2243,7 @@ describe('successful migrations', () => {
});
test('Updates rule tags correctly if tags are undefined', () => {
const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured);
const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured);
const migration801 = migrations['8.0.1'];
const alert = {
@ -2204,7 +2266,7 @@ describe('successful migrations', () => {
});
test('Updates rule tags correctly if tags are null', () => {
const migrations = getMigrations(encryptedSavedObjectsSetup, isPreconfigured);
const migrations = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured);
const migration801 = migrations['8.0.1'];
const alert = {
@ -2231,7 +2293,9 @@ describe('successful migrations', () => {
describe('8.2.0', () => {
test('migrates params to mapped_params', () => {
const migration820 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.2.0'];
const migration820 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.2.0'
];
const alert = getMockData(
{
params: {
@ -2254,8 +2318,29 @@ describe('successful migrations', () => {
});
describe('8.3.0', () => {
test('migrates es_query alert params', () => {
const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.3.0'
];
const alert = getMockData(
{
params: { esQuery: '{ "query": "test-query" }' },
alertTypeId: '.es-query',
},
true
);
const migratedAlert820 = migration830(alert, migrationContext);
expect(migratedAlert820.attributes.params).toEqual({
esQuery: '{ "query": "test-query" }',
searchType: 'esQuery',
});
});
test('removes internal tags', () => {
const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0'];
const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.3.0'
];
const alert = getMockData(
{
tags: [
@ -2274,7 +2359,9 @@ describe('successful migrations', () => {
});
test('do not remove internal tags if rule is not Security solution rule', () => {
const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0'];
const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.3.0'
];
const alert = getMockData(
{
tags: ['__internal_immutable:false', 'tag-1'],
@ -2290,7 +2377,9 @@ describe('successful migrations', () => {
describe('Metrics Inventory Threshold rule', () => {
test('Migrates incorrect action group spelling', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.0.0'
];
const actions = [
{
@ -2317,7 +2406,9 @@ describe('successful migrations', () => {
});
test('Works with the correct action group spelling', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'8.0.0'
];
const actions = [
{
@ -2346,6 +2437,72 @@ describe('successful migrations', () => {
});
});
describe('search source migration', () => {
it('should apply migration within es query alert rule', () => {
const esQueryRuleSavedObject = {
attributes: {
params: {
searchConfiguration: {
some: 'prop',
migrated: false,
},
},
},
} as SavedObjectUnsanitizedDoc;
const versionToTest = '9.1.3';
const migrations = getMigrations(
encryptedSavedObjectsSetup,
{
[versionToTest]: (state) => ({ ...state, migrated: true }),
},
isPreconfigured
);
expect(
migrations[versionToTest](esQueryRuleSavedObject, {} as SavedObjectMigrationContext)
).toEqual({
attributes: {
params: {
searchConfiguration: {
some: 'prop',
migrated: true,
},
},
},
});
});
it('should not apply migration within es query alert rule when searchConfiguration not an object', () => {
const esQueryRuleSavedObject = {
attributes: {
params: {
searchConfiguration: 5,
},
},
} as SavedObjectUnsanitizedDoc;
const versionToTest = '9.1.4';
const migrations = getMigrations(
encryptedSavedObjectsSetup,
{
[versionToTest]: (state) => ({ ...state, migrated: true }),
},
isPreconfigured
);
expect(
migrations[versionToTest](esQueryRuleSavedObject, {} as SavedObjectMigrationContext)
).toEqual({
attributes: {
params: {
searchConfiguration: 5,
},
},
});
});
});
describe('handles errors during migrations', () => {
beforeEach(() => {
jest.resetAllMocks();
@ -2355,7 +2512,7 @@ describe('handles errors during migrations', () => {
});
describe('7.10.0 throws if migration fails', () => {
test('should show the proper exception', () => {
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
const migration710 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.10.0'];
const alert = getMockData({
consumer: 'alerting',
});
@ -2380,7 +2537,7 @@ describe('handles errors during migrations', () => {
describe('7.11.0 throws if migration fails', () => {
test('should show the proper exception', () => {
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
const migration711 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['7.11.0'];
const alert = getMockData({
consumer: 'alerting',
});
@ -2405,7 +2562,9 @@ describe('handles errors during migrations', () => {
describe('7.11.2 throws if migration fails', () => {
test('should show the proper exception', () => {
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
const migration7112 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.11.2'
];
const alert = getMockData({
consumer: 'alerting',
});
@ -2430,7 +2589,9 @@ describe('handles errors during migrations', () => {
describe('7.13.0 throws if migration fails', () => {
test('should show the proper exception', () => {
const migration7130 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
const migration7130 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.13.0'
];
const alert = getMockData({
consumer: 'alerting',
});
@ -2455,7 +2616,9 @@ describe('handles errors during migrations', () => {
describe('7.16.0 throws if migration fails', () => {
test('should show the proper exception', () => {
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
const migration7160 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
'7.16.0'
];
const rule = getMockData();
expect(() => {
migration7160(rule, migrationContext);
@ -2475,6 +2638,53 @@ describe('handles errors during migrations', () => {
);
});
});
describe('8.3.0 throws if migration fails', () => {
test('should show the proper exception on search source migration', () => {
encryptedSavedObjectsSetup.createMigration.mockImplementation(({ migration }) => migration);
const mockRule = getMockData();
const rule = {
...mockRule,
attributes: {
...mockRule.attributes,
params: {
searchConfiguration: {
some: 'prop',
migrated: false,
},
},
},
};
const versionToTest = '8.3.0';
const migration830 = getMigrations(
encryptedSavedObjectsSetup,
{
[versionToTest]: () => {
throw new Error(`Can't migrate search source!`);
},
},
isPreconfigured
)[versionToTest];
expect(() => {
migration830(rule, migrationContext);
}).toThrowError(`Can't migrate search source!`);
expect(migrationContext.log.error).toHaveBeenCalledWith(
`encryptedSavedObject ${versionToTest} migration failed for alert ${rule.id} with error: Can't migrate search source!`,
{
migrations: {
alertDocument: {
...rule,
attributes: {
...rule.attributes,
},
},
},
}
);
});
});
});
function getUpdatedAt(): string {

View file

@ -7,6 +7,7 @@
import { isRuleType, ruleTypeMappings } from '@kbn/securitysolution-rules';
import { isString } from 'lodash/fp';
import { gte } from 'semver';
import {
LogMeta,
SavedObjectMigrationMap,
@ -19,12 +20,16 @@ import {
} from '@kbn/core/server';
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
import type { IsMigrationNeededPredicate } from '@kbn/encrypted-saved-objects-plugin/server';
import { RawRule, RawRuleAction, RawRuleExecutionStatus } from '../types';
import { MigrateFunctionsObject, MigrateFunction } from '@kbn/kibana-utils-plugin/common';
import { mergeSavedObjectMigrationMaps } from '@kbn/core/server';
import { isSerializedSearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common';
import { extractRefsFromGeoContainmentAlert } from './geo_containment/migrations';
import { RawRule, RawRuleAction, RawRuleExecutionStatus } from '../types';
import { getMappedParams } from '../rules_client/lib/mapped_params_utils';
const SIEM_APP_ID = 'securitySolution';
const SIEM_SERVER_APP_ID = 'siem';
const MINIMUM_SS_MIGRATION_VERSION = '8.3.0';
export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0';
export const FILEBEAT_7X_INDICATOR_PATH = 'threatintel.indicator';
@ -59,6 +64,9 @@ export const isAnyActionSupportIncidents = (doc: SavedObjectUnsanitizedDoc<RawRu
export const isSiemSignalsRuleType = (doc: SavedObjectUnsanitizedDoc<RawRule>): boolean =>
doc.attributes.alertTypeId === 'siem.signals';
export const isEsQueryRuleType = (doc: SavedObjectUnsanitizedDoc<RawRule>) =>
doc.attributes.alertTypeId === '.es-query';
export const isDetectionEngineAADRuleType = (doc: SavedObjectUnsanitizedDoc<RawRule>): boolean =>
(Object.values(ruleTypeMappings) as string[]).includes(doc.attributes.alertTypeId);
@ -75,6 +83,7 @@ export const isSecuritySolutionLegacyNotification = (
export function getMigrations(
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
searchSourceMigrations: MigrateFunctionsObject,
isPreconfigured: (connectorId: string) => boolean
): SavedObjectMigrationMap {
const migrationWhenRBACWasIntroduced = createEsoMigration(
@ -155,22 +164,25 @@ export function getMigrations(
const migrationRules830 = createEsoMigration(
encryptedSavedObjects,
(doc: SavedObjectUnsanitizedDoc<RawRule>): doc is SavedObjectUnsanitizedDoc<RawRule> => true,
pipeMigrations(removeInternalTags)
pipeMigrations(addSearchType, removeInternalTags)
);
return {
'7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'),
'7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'),
'7.11.2': executeMigrationWithErrorHandling(migrationActions7112, '7.11.2'),
'7.13.0': executeMigrationWithErrorHandling(migrationSecurityRules713, '7.13.0'),
'7.14.1': executeMigrationWithErrorHandling(migrationSecurityRules714, '7.14.1'),
'7.15.0': executeMigrationWithErrorHandling(migrationSecurityRules715, '7.15.0'),
'7.16.0': executeMigrationWithErrorHandling(migrateRules716, '7.16.0'),
'8.0.0': executeMigrationWithErrorHandling(migrationRules800, '8.0.0'),
'8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'),
'8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'),
'8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'),
};
return mergeSavedObjectMigrationMaps(
{
'7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'),
'7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'),
'7.11.2': executeMigrationWithErrorHandling(migrationActions7112, '7.11.2'),
'7.13.0': executeMigrationWithErrorHandling(migrationSecurityRules713, '7.13.0'),
'7.14.1': executeMigrationWithErrorHandling(migrationSecurityRules714, '7.14.1'),
'7.15.0': executeMigrationWithErrorHandling(migrationSecurityRules715, '7.15.0'),
'7.16.0': executeMigrationWithErrorHandling(migrateRules716, '7.16.0'),
'8.0.0': executeMigrationWithErrorHandling(migrationRules800, '8.0.0'),
'8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'),
'8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'),
'8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'),
},
getSearchSourceMigrations(encryptedSavedObjects, searchSourceMigrations)
);
}
function executeMigrationWithErrorHandling(
@ -697,6 +709,23 @@ function addSecuritySolutionAADRuleTypes(
: doc;
}
function addSearchType(doc: SavedObjectUnsanitizedDoc<RawRule>) {
const searchType = doc.attributes.params.searchType;
return isEsQueryRuleType(doc) && !searchType
? {
...doc,
attributes: {
...doc.attributes,
params: {
...doc.attributes.params,
searchType: 'esQuery',
},
},
}
: doc;
}
function addSecuritySolutionAADRuleTypeTags(
doc: SavedObjectUnsanitizedDoc<RawRule>
): SavedObjectUnsanitizedDoc<RawRule> {
@ -902,3 +931,56 @@ function pipeMigrations(...migrations: AlertMigration[]): AlertMigration {
return (doc: SavedObjectUnsanitizedDoc<RawRule>) =>
migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc);
}
function mapSearchSourceMigrationFunc(
migrateSerializedSearchSourceFields: MigrateFunction<SerializedSearchSourceFields>
): MigrateFunction {
return (doc) => {
const _doc = doc as { attributes: RawRule };
const serializedSearchSource = _doc.attributes.params.searchConfiguration;
if (isSerializedSearchSource(serializedSearchSource)) {
return {
..._doc,
attributes: {
..._doc.attributes,
params: {
..._doc.attributes.params,
searchConfiguration: migrateSerializedSearchSourceFields(serializedSearchSource),
},
},
};
}
return _doc;
};
}
/**
* This creates a migration map that applies search source migrations to legacy es query rules.
* It doesn't modify existing migrations. The following migrations will occur at minimum version of 8.3+.
*/
function getSearchSourceMigrations(
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
searchSourceMigrations: MigrateFunctionsObject
) {
const filteredMigrations: SavedObjectMigrationMap = {};
for (const versionKey in searchSourceMigrations) {
if (gte(versionKey, MINIMUM_SS_MIGRATION_VERSION)) {
const migrateSearchSource = mapSearchSourceMigrationFunc(
searchSourceMigrations[versionKey]
) as unknown as AlertMigration;
filteredMigrations[versionKey] = executeMigrationWithErrorHandling(
createEsoMigration(
encryptedSavedObjects,
(doc: SavedObjectUnsanitizedDoc<RawRule>): doc is SavedObjectUnsanitizedDoc<RawRule> =>
isEsQueryRuleType(doc),
pipeMigrations(migrateSearchSource)
),
versionKey
);
}
}
return filteredMigrations;
}

View file

@ -419,6 +419,18 @@ export default function createGetTests({ getService }: FtrProviderContext) {
});
});
it('8.2.0 migrates existing esQuery alerts to contain searchType param', async () => {
const response = await es.get<{ alert: RawRule }>(
{
index: '.kibana',
id: 'alert:776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8',
},
{ meta: true }
);
expect(response.statusCode).to.equal(200);
expect(response.body._source?.alert?.params.searchType).to.eql('esQuery');
});
it('8.3.0 removes internal tags in Security Solution rule', async () => {
const response = await es.get<{ alert: RawRule }>(
{

View file

@ -891,6 +891,57 @@
}
}
{
"type": "doc",
"value": {
"id": "alert:776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8",
"index": ".kibana_1",
"source": {
"alert": {
"name": "123",
"alertTypeId": ".es-query",
"consumer": "alerts",
"params": {
"esQuery": "{\n \"query\":{\n \"match_all\" : {}\n }\n}",
"size": 100,
"timeWindowSize": 5,
"timeWindowUnit": "m",
"threshold": [
1000
],
"thresholdComparator": ">",
"index": [
"kibana_sample_data_ecommerce"
],
"timeField": "order_date"
},
"schedule": {
"interval": "1m"
},
"enabled": true,
"actions": [
],
"throttle": null,
"apiKeyOwner": null,
"createdBy" : "elastic",
"updatedBy" : "elastic",
"createdAt": "2022-03-26T16:04:50.698Z",
"muteAll": false,
"mutedInstanceIds": [],
"scheduledTaskId": "776cb5c0-ad1e-11ec-ab9e-5f5932f4fad8",
"tags": []
},
"type": "alert",
"updated_at": "2022-03-26T16:05:55.957Z",
"migrationVersion": {
"alert": "8.0.1"
},
"references": [
]
}
}
}
{
"type":"doc",
"value":{
@ -989,4 +1040,4 @@
]
}
}
}
}