[Security Solution] [Platform] Migrate pre-8.0 action connector ids in rule params on import to post-8.0 _id (#120975) (#122302)

Security Solution import route needed to find actions where the action SO id was the old, pre-8.0 _id so we find references to the _id and the originId to make sure the rule we are importing will not error out.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Devin W. Hurley <devin.hurley@elastic.co>
This commit is contained in:
Kibana Machine 2022-01-04 19:55:06 -05:00 committed by GitHub
parent f7f133d23c
commit 6f97f07cbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1002 additions and 3 deletions

View file

@ -29,6 +29,8 @@ export const action = t.exact(
})
);
export type Action = t.TypeOf<typeof action>;
export const actions = t.array(action);
export type Actions = t.TypeOf<typeof actions>;

View file

@ -31,7 +31,11 @@ import {
buildSiemResponse,
} from '../utils';
import { getTupleDuplicateErrorsAndUniqueRules, getInvalidConnectors } from './utils';
import {
getTupleDuplicateErrorsAndUniqueRules,
getInvalidConnectors,
migrateLegacyActionsIds,
} from './utils';
import { createRulesAndExceptionsStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
import { HapiReadableStream } from '../../rules/types';
@ -74,6 +78,9 @@ export const importRulesRoute = (
const rulesClient = context.alerting.getRulesClient();
const actionsClient = context.actions.getActionsClient();
const esClient = context.core.elasticsearch.client;
const actionSOClient = context.core.savedObjects.getClient({
includedHiddenTypes: ['action'],
});
const savedObjectsClient = context.core.savedObjects.client;
const siemClient = context.securitySolution.getAppClient();
const exceptionsClient = context.lists?.getExceptionListClient();
@ -127,8 +134,13 @@ export const importRulesRoute = (
const [duplicateIdErrors, parsedObjectsWithoutDuplicateErrors] =
getTupleDuplicateErrorsAndUniqueRules(rules, request.query.overwrite);
const [nonExistentActionErrors, uniqueParsedObjects] = await getInvalidConnectors(
const migratedParsedObjectsWithoutDuplicateErrors = await migrateLegacyActionsIds(
parsedObjectsWithoutDuplicateErrors,
actionSOClient
);
const [nonExistentActionErrors, uniqueParsedObjects] = await getInvalidConnectors(
migratedParsedObjectsWithoutDuplicateErrors,
actionsClient
);

View file

@ -7,6 +7,7 @@
import { Readable } from 'stream';
import { createPromiseFromStreams } from '@kbn/utils';
import { Action, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import {
transformAlertToRule,
@ -19,6 +20,8 @@ import {
getDuplicates,
getTupleDuplicateErrorsAndUniqueRules,
getInvalidConnectors,
swapActionIds,
migrateLegacyActionsIds,
} from './utils';
import { getAlertMock } from '../__mocks__/request_responses';
import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
@ -30,7 +33,6 @@ import { createRulesAndExceptionsStreamFromNdJson } from '../../rules/create_rul
import { RuleAlertType } from '../../rules/types';
import { ImportRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/import_rules_schema';
import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock';
import { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import { CreateRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request';
import {
getMlRuleParams,
@ -652,6 +654,208 @@ describe.each([
});
});
describe('swapActionIds', () => {
const mockAction: Action = {
group: 'group string',
id: 'some-7.x-id',
action_type_id: '.slack',
params: {},
};
const soClient = clients.core.savedObjects.getClient();
beforeEach(() => {
soClient.find.mockReset();
soClient.find.mockClear();
});
test('returns original action if Elasticsearch query fails', async () => {
clients.core.savedObjects
.getClient()
.find.mockRejectedValueOnce(new Error('failed to query'));
const result = await swapActionIds(mockAction, soClient);
expect(result).toEqual(mockAction);
});
test('returns original action if Elasticsearch query returns no hits', async () => {
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [],
}));
const result = await swapActionIds(mockAction, soClient);
expect(result).toEqual(mockAction);
});
test('returns error if conflicting action connectors are found -> two hits found with same originId', async () => {
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'fake id 1', type: 'action', attributes: {}, references: [] },
{ score: 0, id: 'fake id 2', type: 'action', attributes: {}, references: [] },
],
}));
const result = await swapActionIds(mockAction, soClient);
expect(result instanceof Error).toBeTruthy();
expect((result as unknown as Error).message).toEqual(
'Found two action connectors with originId or _id: some-7.x-id The upload cannot be completed unless the _id or the originId of the action connector is changed. See https://www.elastic.co/guide/en/kibana/current/sharing-saved-objects.html for more details'
);
});
test('returns action with new migrated _id if a single hit is found when querying by action connector originId', async () => {
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
],
}));
const result = await swapActionIds(mockAction, soClient);
expect(result).toEqual({ ...mockAction, id: 'new-post-8.0-id' });
});
});
describe('migrateLegacyActionsIds', () => {
const mockAction: Action = {
group: 'group string',
id: 'some-7.x-id',
action_type_id: '.slack',
params: {},
};
const soClient = clients.core.savedObjects.getClient();
beforeEach(() => {
soClient.find.mockReset();
soClient.find.mockClear();
});
test('returns import rules schemas + migrated action', async () => {
const rule: ReturnType<typeof getCreateRulesSchemaMock> = {
...getCreateRulesSchemaMock('rule-1'),
actions: [mockAction],
};
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
],
}));
const res = await migrateLegacyActionsIds(
// @ts-expect-error
[rule],
soClient
);
expect(res).toEqual([{ ...rule, actions: [{ ...mockAction, id: 'new-post-8.0-id' }] }]);
});
test('returns import rules schemas + multiple migrated action', async () => {
const rule: ReturnType<typeof getCreateRulesSchemaMock> = {
...getCreateRulesSchemaMock('rule-1'),
actions: [mockAction, { ...mockAction, id: 'different-id' }],
};
soClient.find.mockImplementation(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
],
}));
const res = await migrateLegacyActionsIds(
// @ts-expect-error
[rule],
soClient
);
expect(res).toEqual([
{
...rule,
actions: [
{ ...mockAction, id: 'new-post-8.0-id' },
{ ...mockAction, id: 'new-post-8.0-id' },
],
},
]);
});
test('returns import rules schemas + migrated action resulting in error', async () => {
const rule: ReturnType<typeof getCreateRulesSchemaMock> = {
...getCreateRulesSchemaMock('rule-1'),
actions: [mockAction],
};
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
{ score: 0, id: 'new-post-8.0-id-2', type: 'action', attributes: {}, references: [] },
],
}));
const res = await migrateLegacyActionsIds(
// @ts-expect-error
[rule],
soClient
);
expect(res[0] instanceof Error).toBeTruthy();
expect((res[0] as unknown as Error).message).toEqual(
JSON.stringify({
rule_id: 'rule-1',
error: {
status_code: 409,
message:
'Found two action connectors with originId or _id: some-7.x-id The upload cannot be completed unless the _id or the originId of the action connector is changed. See https://www.elastic.co/guide/en/kibana/current/sharing-saved-objects.html for more details',
},
})
);
});
test('returns import multiple rules schemas + migrated action, one success and one error', async () => {
const rule: ReturnType<typeof getCreateRulesSchemaMock> = {
...getCreateRulesSchemaMock('rule-1'),
actions: [mockAction],
};
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
],
}));
soClient.find.mockImplementationOnce(async () => ({
total: 0,
per_page: 0,
page: 1,
saved_objects: [
{ score: 0, id: 'new-post-8.0-id', type: 'action', attributes: {}, references: [] },
{ score: 0, id: 'new-post-8.0-id-2', type: 'action', attributes: {}, references: [] },
],
}));
const res = await migrateLegacyActionsIds(
// @ts-expect-error
[rule, rule],
soClient
);
expect(res[0]).toEqual({ ...rule, actions: [{ ...mockAction, id: 'new-post-8.0-id' }] });
expect(res[1] instanceof Error).toBeTruthy();
expect((res[1] as unknown as Error).message).toEqual(
JSON.stringify({
rule_id: 'rule-1',
error: {
status_code: 409,
message:
'Found two action connectors with originId or _id: some-7.x-id The upload cannot be completed unless the _id or the originId of the action connector is changed. See https://www.elastic.co/guide/en/kibana/current/sharing-saved-objects.html for more details',
},
})
);
});
});
describe('getInvalidConnectors', () => {
beforeEach(() => {
clients.actionsClient.getAll.mockReset();

View file

@ -7,6 +7,9 @@
import { countBy } from 'lodash/fp';
import uuid from 'uuid';
import { Action } from '@kbn/securitysolution-io-ts-alerting-types';
import { SavedObjectsClientContract } from 'kibana/server';
import pMap from 'p-map';
import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema';
import { ImportRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/import_rules_schema';
@ -28,6 +31,7 @@ import { SanitizedAlert } from '../../../../../../alerting/common';
import { LegacyRulesActionsSavedObject } from '../../rule_actions/legacy_get_rule_actions_saved_object';
type PromiseFromStreams = ImportRulesSchemaDecoded | Error;
const MAX_CONCURRENT_SEARCHES = 10;
export const getIdError = ({
id,
@ -193,6 +197,122 @@ export const getTupleDuplicateErrorsAndUniqueRules = (
return [Array.from(errors.values()), Array.from(rulesAcc.values())];
};
// functions copied from here
// https://github.com/elastic/kibana/blob/4584a8b570402aa07832cf3e5b520e5d2cfa7166/src/core/server/saved_objects/import/lib/check_origin_conflicts.ts#L55-L57
const createQueryTerm = (input: string) => input.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
const createQuery = (type: string, id: string) =>
`"${createQueryTerm(`${type}:${id}`)}" | "${createQueryTerm(id)}"`;
/**
* Query for a saved object with a given origin id and replace the
* id in the provided action with the _id from the query result
* @param action
* @param esClient
* @returns
*/
export const swapActionIds = async (
action: Action,
savedObjectsClient: SavedObjectsClientContract
): Promise<Action | Error> => {
try {
const search = createQuery('action', action.id);
const foundAction = await savedObjectsClient.find<Action>({
type: 'action',
search,
rootSearchFields: ['_id', 'originId'],
});
if (foundAction.saved_objects.length === 1) {
return { ...action, id: foundAction.saved_objects[0].id };
} else if (foundAction.saved_objects.length > 1) {
return new Error(
`Found two action connectors with originId or _id: ${action.id} The upload cannot be completed unless the _id or the originId of the action connector is changed. See https://www.elastic.co/guide/en/kibana/current/sharing-saved-objects.html for more details`
);
}
} catch (exc) {
return action;
}
return action;
};
/**
* In 8.0 all saved objects made in a non-default space will have their
* _id's regenerated. Security Solution rules have references to the
* actions SO id inside the 'actions' param.
* When users import these rules, we need to ensure any rule with
* an action that has an old, pre-8.0 id will need to be updated
* to point to the new _id for that action (alias_target_id)
*
* ex:
* import rule.ndjson:
* {
* rule_id: 'myrule_id'
* name: 'my favorite rule'
* ...
* actions:[{id: '1111-2222-3333-4444', group...}]
* }
*
* In 8.0 the 'id' field of this action is no longer a reference
* to the _id of the action (connector). Querying against the connector
* endpoint for this id will yield 0 results / 404.
*
* The solution: If we query the .kibana index for '1111-2222-3333-4444' as an originId,
* we should get the original connector back
* (with the new, migrated 8.0 _id of 'xxxx-yyyy-zzzz-0000') and can then replace
* '1111-2222-3333-4444' in the example above with 'xxxx-yyyy-zzzz-0000'
* And the rule will then import successfully.
* @param rules
* @param savedObjectsClient SO client exposing hidden 'actions' SO type
* @returns
*/
export const migrateLegacyActionsIds = async (
rules: PromiseFromStreams[],
savedObjectsClient: SavedObjectsClientContract
): Promise<PromiseFromStreams[]> => {
const isImportRule = (r: unknown): r is ImportRulesSchemaDecoded => !(r instanceof Error);
return pMap(
rules,
async (rule) => {
if (isImportRule(rule)) {
// can we swap the pre 8.0 action connector(s) id with the new,
// post-8.0 action id (swap the originId for the new _id?)
const newActions: Array<Action | Error> = await pMap(
rule.actions,
(action: Action) => swapActionIds(action, savedObjectsClient),
{ concurrency: MAX_CONCURRENT_SEARCHES }
);
// were there any errors discovered while trying to migrate and swap the action connector ids?
const actionMigrationErrors = newActions.filter(
(action): action is Error => action instanceof Error
);
const newlyMigratedActions: Action[] = newActions.filter(
(action): action is Action => !(action instanceof Error)
);
if (actionMigrationErrors == null || actionMigrationErrors.length === 0) {
return { ...rule, actions: newlyMigratedActions };
}
// return an Error object with the rule_id and the error messages
// for the actions associated with that rule.
return new Error(
JSON.stringify(
createBulkErrorObject({
ruleId: rule.rule_id,
statusCode: 409,
message: `${actionMigrationErrors.map((error: Error) => error.message).join(',')}`,
})
)
);
}
return rule;
},
{ concurrency: MAX_CONCURRENT_SEARCHES }
);
};
/**
* Given a set of rules and an actions client this will return connectors that are invalid
* such as missing connectors and filter out the rules that have invalid connectors.

View file

@ -29,10 +29,72 @@ import {
} from '../../../../plugins/lists/common/schemas/request/import_exceptions_schema.mock';
import { deleteAllExceptions } from '../../../lists_api_integration/utils';
const getImportRuleBuffer = (connectorId: string) => {
const rule1 = {
id: '53aad690-544e-11ec-a349-11361cc441c4',
updated_at: '2021-12-03T15:33:13.271Z',
updated_by: 'elastic',
created_at: '2021-12-03T15:33:13.271Z',
created_by: 'elastic',
name: '7.16 test with action',
tags: [],
interval: '5m',
enabled: true,
description: 'test',
risk_score: 21,
severity: 'low',
license: '',
output_index: '.siem-signals-devin-hurley-7',
meta: { from: '1m', kibana_siem_app_url: 'http://0.0.0.0:5601/s/7/app/security' },
author: [],
false_positives: [],
from: 'now-360s',
rule_id: 'aa525d7c-8948-439f-b32d-27e00c750246',
max_signals: 100,
risk_score_mapping: [],
severity_mapping: [],
threat: [],
to: 'now',
references: [],
version: 1,
exceptions_list: [],
immutable: false,
type: 'query',
language: 'kuery',
index: [
'apm-*-transaction*',
'traces-apm*',
'auditbeat-*',
'endgame-*',
'filebeat-*',
'logs-*',
'packetbeat-*',
'winlogbeat-*',
],
query: '*:*',
filters: [],
throttle: '1h',
actions: [
{
group: 'default',
id: connectorId,
params: {
message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
action_type_id: '.slack',
},
],
};
const rule1String = JSON.stringify(rule1);
const buffer = Buffer.from(`${rule1String}\n`);
return buffer;
};
// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext): void => {
const supertest = getService('supertest');
const log = getService('log');
const esArchiver = getService('esArchiver');
describe('import_rules', () => {
describe('importing rules with an index', () => {
@ -529,6 +591,112 @@ export default ({ getService }: FtrProviderContext): void => {
});
});
describe('migrate pre-8.0 action connector ids', () => {
const defaultSpaceActionConnectorId = '61b17790-544e-11ec-a349-11361cc441c4';
const space714ActionConnectorId = '51b17790-544e-11ec-a349-11361cc441c4';
beforeEach(async () => {
await esArchiver.load(
'x-pack/test/functional/es_archives/security_solution/import_rule_connector'
);
});
afterEach(async () => {
await esArchiver.unload(
'x-pack/test/functional/es_archives/security_solution/import_rule_connector'
);
});
it('importing a non-default-space 7.16 rule with a connector made in the non-default space should result in a 200', async () => {
const spaceId = '714-space';
// connectorId is from the 7.x connector here
// x-pack/test/functional/es_archives/security_solution/import_rule_connector
const buffer = getImportRuleBuffer(space714ActionConnectorId);
const { body } = await supertest
.post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`)
.set('kbn-xsrf', 'true')
.attach('file', buffer, 'rules.ndjson')
.expect(200);
expect(body.success).to.eql(true);
expect(body.success_count).to.eql(1);
expect(body.errors.length).to.eql(0);
});
// When objects become share-capable we will either add / update this test
it('importing a non-default-space 7.16 rule with a connector made in the non-default space into the default space should result in a 404', async () => {
// connectorId is from the 7.x connector here
// x-pack/test/functional/es_archives/security_solution/import_rule_connector
const buffer = getImportRuleBuffer(space714ActionConnectorId);
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_import`)
.set('kbn-xsrf', 'true')
.attach('file', buffer, 'rules.ndjson')
.expect(200);
expect(body.success).to.equal(false);
expect(body.errors[0].error.status_code).to.equal(404);
expect(body.errors[0].error.message).to.equal(
`1 connector is missing. Connector id missing is: ${space714ActionConnectorId}`
);
});
// When objects become share-capable we will either add / update this test
it('importing a non-default-space 7.16 rule with a connector made in the non-default space into a different non-default space should result in a 404', async () => {
const spaceId = '4567-space';
// connectorId is from the 7.x connector here
// x-pack/test/functional/es_archives/security_solution/import_rule_connector
// it
const buffer = getImportRuleBuffer(space714ActionConnectorId);
const { body } = await supertest
.post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`)
.set('kbn-xsrf', 'true')
.attach('file', buffer, 'rules.ndjson')
.expect(200);
expect(body.success).to.equal(false);
expect(body.errors[0].error.status_code).to.equal(404);
expect(body.errors[0].error.message).to.equal(
`1 connector is missing. Connector id missing is: ${space714ActionConnectorId}`
);
});
it('importing a default-space 7.16 rule with a connector made in the default space into the default space should result in a 200', async () => {
// connectorId is from the 7.x connector here
// x-pack/test/functional/es_archives/security_solution/import_rule_connector
// it
const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId);
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_import`)
.set('kbn-xsrf', 'true')
.attach('file', buffer, 'rules.ndjson')
.expect(200);
expect(body.success).to.equal(true);
expect(body.success_count).to.eql(1);
expect(body.errors.length).to.eql(0);
});
it('importing a default-space 7.16 rule with a connector made in the default space into a non-default space should result in a 404', async () => {
await esArchiver.load(
'x-pack/test/functional/es_archives/security_solution/import_rule_connector'
);
const spaceId = '4567-space';
// connectorId is from the 7.x connector here
// x-pack/test/functional/es_archives/security_solution/import_rule_connector
// it
const buffer = getImportRuleBuffer(defaultSpaceActionConnectorId);
const { body } = await supertest
.post(`/s/${spaceId}${DETECTION_ENGINE_RULES_URL}/_import`)
.set('kbn-xsrf', 'true')
.attach('file', buffer, 'rules.ndjson')
.expect(200);
expect(body.success).to.equal(false);
expect(body.errors[0].error.status_code).to.equal(404);
expect(body.errors[0].error.message).to.equal(
`1 connector is missing. Connector id missing is: ${defaultSpaceActionConnectorId}`
);
});
});
describe('importing with exceptions', () => {
beforeEach(async () => {
await deleteAllExceptions(supertest, log);

View file

@ -0,0 +1,93 @@
{
"type" : "doc",
"value": {
"index" : ".kibana_1",
"id" : "space:714-space",
"source" : {
"space" : {
"name" : "714-space",
"initials" : "t",
"color" : "#B9A888",
"disabledFeatures" : [ ],
"imageUrl" : ""
},
"type" : "space",
"references" : [ ],
"migrationVersion" : {
"space" : "6.6.0"
},
"updated_at" : "2021-10-11T14:49:07.012Z"
}
}
}
{
"type" : "doc",
"value": {
"index" : ".kibana_1",
"id" : "space:4567-space",
"source" : {
"space" : {
"name" : "4567-space",
"initials" : "t",
"color" : "#B9A888",
"disabledFeatures" : [ ],
"imageUrl" : ""
},
"type" : "space",
"references" : [ ],
"migrationVersion" : {
"space" : "6.6.0"
},
"updated_at" : "2021-10-11T14:49:07.012Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_1",
"id": "714-space:action:51b17790-544e-11ec-a349-11361cc441c4",
"source": {
"action": {
"actionTypeId": ".slack",
"name": "7.16 test connector",
"isMissingSecrets": false,
"config": {},
"secrets": "fEO5Lk2AxFWtXNpTyg2DZKaZCjeCfMh/DGch02neTGJ/Hzu+w4DXigUVuUtgynNyRbhe7TbnTzi44jVg39WB3VR3yoWSFtvV/W7NHa3B1Kr3za1S3V4XCIu/CMIk0k8vnQMiNGiMuolwws6UjvQk8fiVJygjznhEGc66TMuAmKdz7fM="
},
"type": "action",
"references": [],
"namespace": "714-space",
"migrationVersion": {
"action": "7.14.0"
},
"updated_at": "2021-12-03T15:33:09.651Z"
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana_1",
"id": "action:61b17790-544e-11ec-a349-11361cc441c4",
"source": {
"action": {
"actionTypeId": ".slack",
"name": "7.16 test connector",
"isMissingSecrets": false,
"config": {},
"secrets": "fEO5Lk2AxFWtXNpTyg2DZKaZCjeCfMh/DGch02neTGJ/Hzu+w4DXigUVuUtgynNyRbhe7TbnTzi44jVg39WB3VR3yoWSFtvV/W7NHa3B1Kr3za1S3V4XCIu/CMIk0k8vnQMiNGiMuolwws6UjvQk8fiVJygjznhEGc66TMuAmKdz7fM="
},
"type": "action",
"references": [],
"migrationVersion": {
"action": "7.14.0"
},
"updated_at": "2021-12-03T15:33:09.651Z"
}
}
}

View file

@ -0,0 +1,400 @@
{
"type": "index",
"value": {
"aliases": {
".kibana": {}
},
"index": ".kibana_1",
"mappings": {
"_meta": {
"migrationMappingPropertyHashes": {
"action": "6e96ac5e648f57523879661ea72525b7",
"action_task_params": "a9d49f184ee89641044be0ca2950fa3a",
"alert": "7b44fba6773e37c806ce290ea9b7024e",
"apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd",
"apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2",
"application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1",
"application_usage_transactional": "965839e75f809fefe04f92dc4d99722a",
"canvas-element": "7390014e1091044523666d97247392fc",
"canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231",
"cases": "32aa96a6d3855ddda53010ae2048ac22",
"cases-comments": "c2061fb929f585df57425102fa928b4b",
"cases-configure": "42711cbb311976c0687853f4c1354572",
"cases-user-actions": "32277330ec6b721abe3b846cfd939a71",
"config": "ae24d22d5986d04124cc6568f771066f",
"dashboard": "d00f614b29a80360e1190193fd333bab",
"file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e",
"graph-workspace": "cd7ba1330e6682e9cc00b78850874be1",
"index-pattern": "66eccb05066c5a89924f48a9e9736499",
"infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c",
"inventory-view": "88fc7e12fd1b45b6f0787323ce4f18d2",
"kql-telemetry": "d12a98a6f19a2d273696597547e064ee",
"lens": "d33c68a69ff1e78c9888dedd2164ac22",
"lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327",
"map": "23d7aa4a720d4938ccde3983f87bd58d",
"maps-telemetry": "bfd39d88aadadb4be597ea984d433dbe",
"metrics-explorer-view": "428e319af3e822c80a84cf87123ca35c",
"migrationVersion": "4a1746014a75ade3a714e1db5763276f",
"ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9",
"namespace": "2f4316de49999235636386fe51dc06c1",
"namespaces": "2f4316de49999235636386fe51dc06c1",
"query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9",
"references": "7997cf5a56cc02bdc9c93361bde732b0",
"sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4",
"search": "181661168bbadd1eff5902361e2a0d5c",
"space": "c5ca8acafa0beaa4d08d014a97b6bc6b",
"telemetry": "36a616f7026dfa617d6655df850fe16d",
"todo": "082a2cc96a590268344d5cd74c159ac4",
"tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215",
"type": "2f4316de49999235636386fe51dc06c1",
"ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3",
"updated_at": "00da57df13e94e9d98437d13ace4bfe0",
"upgrade-assistant-reindex-operation": "296a89039fc4260292be36b1b005d8f2",
"upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b",
"uptime-dynamic-settings": "fcdb453a30092f022f2642db29523d80",
"url": "b675c3be8d76ecf029294d51dc7ec65d",
"visualization": "52d7a13ad68a150c4525b292d23e12cc"
}
},
"dynamic": "strict",
"properties": {
"action": {
"properties": {
"actionTypeId": {
"type": "keyword"
},
"isMissingSecrets": {
"type": "boolean"
},
"config": {
"enabled": false,
"type": "object"
},
"name": {
"fields": {
"keyword": {
"type": "keyword"
}
},
"type": "text"
},
"secrets": {
"type": "binary"
}
}
},
"action_task_params": {
"properties": {
"actionId": {
"type": "keyword"
},
"apiKey": {
"type": "binary"
},
"params": {
"enabled": false,
"type": "object"
}
}
},
"alert": {
"properties": {
"actions": {
"properties": {
"actionRef": {
"type": "keyword"
},
"actionTypeId": {
"type": "keyword"
},
"group": {
"type": "keyword"
},
"params": {
"enabled": false,
"type": "object"
}
},
"type": "nested"
},
"alertTypeId": {
"type": "keyword"
},
"apiKey": {
"type": "binary"
},
"apiKeyOwner": {
"type": "keyword"
},
"consumer": {
"type": "keyword"
},
"createdAt": {
"type": "date"
},
"legacyId": {
"type": "keyword"
},
"createdBy": {
"type": "keyword"
},
"updatedAt": {
"type": "date"
},
"executionStatus": {
"properties": {
"error": {
"properties": {
"message": {
"type": "keyword"
},
"reason": {
"type": "keyword"
}
}
},
"lastExecutionDate": {
"type": "date"
},
"status": {
"type": "keyword"
}
}
},
"enabled": {
"type": "boolean"
},
"muteAll": {
"type": "boolean"
},
"mutedInstanceIds": {
"type": "keyword"
},
"name": {
"fields": {
"keyword": {
"type": "keyword"
}
},
"type": "text"
},
"params": {
"enabled": false,
"type": "object"
},
"schedule": {
"properties": {
"interval": {
"type": "keyword"
}
}
},
"scheduledTaskId": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"throttle": {
"type": "keyword"
},
"updatedBy": {
"type": "keyword"
}
}
},
"legacy-url-alias": {
"properties": {
"sourceId": {
"type": "text"
},
"targetNamespace": {
"type": "keyword"
},
"targetType": {
"type": "keyword"
},
"targetId": {
"type": "keyword"
},
"resolveCounter": {
"type": "integer"
},
"lastResolved": {
"type": "date"
}
}
},
"config": {
"dynamic": "true",
"properties": {
"buildNum": {
"type": "keyword"
}
}
},
"migrationVersion": {
"dynamic": "true",
"properties": {
"alert": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"config": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"space": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
}
}
},
"namespace": {
"type": "keyword"
},
"namespaces": {
"type": "keyword"
},
"originId": {
"type": "keyword"
},
"coreMigrationVersion": {
"type": "keyword"
},
"query": {
"properties": {
"description": {
"type": "text"
},
"filters": {
"enabled": false,
"type": "object"
},
"query": {
"properties": {
"language": {
"type": "keyword"
},
"query": {
"index": false,
"type": "keyword"
}
}
},
"timefilter": {
"enabled": false,
"type": "object"
},
"title": {
"type": "text"
}
}
},
"references": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"type": {
"type": "keyword"
}
},
"type": "nested"
},
"search": {
"properties": {
"columns": {
"type": "keyword"
},
"description": {
"type": "text"
},
"hits": {
"type": "integer"
},
"kibanaSavedObjectMeta": {
"properties": {
"searchSourceJSON": {
"type": "text"
}
}
},
"sort": {
"type": "keyword"
},
"title": {
"type": "text"
},
"version": {
"type": "integer"
}
}
},
"space": {
"properties": {
"_reserved": {
"type": "boolean"
},
"color": {
"type": "keyword"
},
"description": {
"type": "text"
},
"disabledFeatures": {
"type": "keyword"
},
"imageUrl": {
"index": false,
"type": "text"
},
"initials": {
"type": "keyword"
},
"name": {
"fields": {
"keyword": {
"ignore_above": 2048,
"type": "keyword"
}
},
"type": "text"
}
}
},
"type": {
"type": "keyword"
},
"updated_at": {
"type": "date"
}
}
},
"settings": {
"index": {
"auto_expand_replicas": "0-1",
"number_of_replicas": "1",
"number_of_shards": "1"
}
}
}
}