mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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:
parent
f7f133d23c
commit
6f97f07cbc
7 changed files with 1002 additions and 3 deletions
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue