mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Removes internal tags (#130664)
Addresses: - https://github.com/elastic/kibana/issues/124899 ## Summary - removes usage of internal tags (tags start with `__internal`) in codebase - migrates rules (removes existing internal tags from rules) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
3f20878699
commit
ffe1cdddc7
44 changed files with 252 additions and 446 deletions
|
@ -2253,6 +2253,41 @@ describe('successful migrations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('8.3.0', () => {
|
||||
test('removes internal tags', () => {
|
||||
const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0'];
|
||||
const alert = getMockData(
|
||||
{
|
||||
tags: [
|
||||
'__internal_immutable:false',
|
||||
'__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41',
|
||||
'test-tag',
|
||||
],
|
||||
alertTypeId: 'siem.queryRule',
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
const migratedAlert830 = migration830(alert, migrationContext);
|
||||
|
||||
expect(migratedAlert830.attributes.tags).toEqual(['test-tag']);
|
||||
});
|
||||
|
||||
test('do not remove internal tags if rule is not Security solution rule', () => {
|
||||
const migration830 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.3.0'];
|
||||
const alert = getMockData(
|
||||
{
|
||||
tags: ['__internal_immutable:false', 'tag-1'],
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
const migratedAlert830 = migration830(alert, migrationContext);
|
||||
|
||||
expect(migratedAlert830.attributes.tags).toEqual(['__internal_immutable:false', 'tag-1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Metrics Inventory Threshold rule', () => {
|
||||
test('Migrates incorrect action group spelling', () => {
|
||||
const migration800 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['8.0.0'];
|
||||
|
|
|
@ -152,6 +152,12 @@ export function getMigrations(
|
|||
pipeMigrations(addMappedParams)
|
||||
);
|
||||
|
||||
const migrationRules830 = createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
(doc: SavedObjectUnsanitizedDoc<RawRule>): doc is SavedObjectUnsanitizedDoc<RawRule> => true,
|
||||
pipeMigrations(removeInternalTags)
|
||||
);
|
||||
|
||||
return {
|
||||
'7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'),
|
||||
'7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtAndNotifyWhen, '7.11.0'),
|
||||
|
@ -163,6 +169,7 @@ export function getMigrations(
|
|||
'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'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -864,6 +871,32 @@ function getCorrespondingAction(
|
|||
) as RawRuleAction;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* removes internal tags(starts with '__internal') from Security Solution rules
|
||||
* @param doc rule to be migrated
|
||||
* @returns migrated rule if it's Security Solution rule or unchanged if not
|
||||
*/
|
||||
function removeInternalTags(
|
||||
doc: SavedObjectUnsanitizedDoc<RawRule>
|
||||
): SavedObjectUnsanitizedDoc<RawRule> {
|
||||
if (!isDetectionEngineAADRuleType(doc)) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
const {
|
||||
attributes: { tags },
|
||||
} = doc;
|
||||
|
||||
const filteredTags = (tags ?? []).filter((tag) => !tag.startsWith('__internal_'));
|
||||
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
tags: filteredTags,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function pipeMigrations(...migrations: AlertMigration[]): AlertMigration {
|
||||
return (doc: SavedObjectUnsanitizedDoc<RawRule>) =>
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
"version": "WzE5MjksMV0=",
|
||||
"attributes": {
|
||||
"name": "Test-rule",
|
||||
"tags": [
|
||||
"__internal_rule_id:22308402-5e0e-421b-8d22-a47ddc4b0188",
|
||||
"__internal_immutable:false"
|
||||
],
|
||||
"tags": [],
|
||||
"alertTypeId": "siem.queryRule",
|
||||
"consumer": "siem",
|
||||
"params": {
|
||||
|
|
|
@ -206,15 +206,6 @@ export const IP_REPUTATION_LINKS_SETTING_DEFAULT = `[
|
|||
*/
|
||||
export const LEGACY_NOTIFICATIONS_ID = `siem.notifications` as const;
|
||||
|
||||
/**
|
||||
* Special internal structure for tags for signals. This is used
|
||||
* to filter out tags that have internal structures within them.
|
||||
*/
|
||||
export const INTERNAL_IDENTIFIER = '__internal' as const;
|
||||
export const INTERNAL_RULE_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_id` as const;
|
||||
export const INTERNAL_RULE_ALERT_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_alert_id` as const;
|
||||
export const INTERNAL_IMMUTABLE_KEY = `${INTERNAL_IDENTIFIER}_immutable` as const;
|
||||
|
||||
/**
|
||||
* Internal actions route
|
||||
*/
|
||||
|
|
|
@ -188,12 +188,7 @@ export const alertsMock: AlertSearchResponse<unknown, unknown> = {
|
|||
query: 'host.name : *',
|
||||
references: ['https://google.com'],
|
||||
severity: 'high',
|
||||
tags: [
|
||||
'host.name exists',
|
||||
'for testing',
|
||||
'__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280',
|
||||
'__internal_immutable:false',
|
||||
],
|
||||
tags: ['host.name exists', 'for testing'],
|
||||
type: 'query',
|
||||
to: 'now',
|
||||
enabled: true,
|
||||
|
@ -428,12 +423,7 @@ export const alertsMock: AlertSearchResponse<unknown, unknown> = {
|
|||
query: 'host.name : *',
|
||||
references: ['https://google.com'],
|
||||
severity: 'high',
|
||||
tags: [
|
||||
'host.name exists',
|
||||
'for testing',
|
||||
'__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280',
|
||||
'__internal_immutable:false',
|
||||
],
|
||||
tags: ['host.name exists', 'for testing'],
|
||||
type: 'query',
|
||||
to: 'now',
|
||||
enabled: true,
|
||||
|
@ -634,12 +624,7 @@ export const alertsMock: AlertSearchResponse<unknown, unknown> = {
|
|||
query: 'host.name : *',
|
||||
references: ['https://google.com'],
|
||||
severity: 'high',
|
||||
tags: [
|
||||
'host.name exists',
|
||||
'for testing',
|
||||
'__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280',
|
||||
'__internal_immutable:false',
|
||||
],
|
||||
tags: ['host.name exists', 'for testing'],
|
||||
type: 'query',
|
||||
to: 'now',
|
||||
enabled: true,
|
||||
|
@ -838,12 +823,7 @@ export const alertsMock: AlertSearchResponse<unknown, unknown> = {
|
|||
query: 'host.name : *',
|
||||
references: ['https://google.com'],
|
||||
severity: 'high',
|
||||
tags: [
|
||||
'host.name exists',
|
||||
'for testing',
|
||||
'__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280',
|
||||
'__internal_immutable:false',
|
||||
],
|
||||
tags: ['host.name exists', 'for testing'],
|
||||
type: 'query',
|
||||
to: 'now',
|
||||
enabled: true,
|
||||
|
|
|
@ -200,7 +200,7 @@ describe('Detections Rules API', () => {
|
|||
expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
|
||||
method: 'GET',
|
||||
query: {
|
||||
filter: 'alert.attributes.tags: "__internal_immutable:false"',
|
||||
filter: 'alert.attributes.params.immutable: false',
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
sort_field: 'enabled',
|
||||
|
@ -228,7 +228,7 @@ describe('Detections Rules API', () => {
|
|||
expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
|
||||
method: 'GET',
|
||||
query: {
|
||||
filter: 'alert.attributes.tags: "__internal_immutable:true"',
|
||||
filter: 'alert.attributes.params.immutable: true',
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
sort_field: 'enabled',
|
||||
|
@ -383,7 +383,7 @@ describe('Detections Rules API', () => {
|
|||
method: 'GET',
|
||||
query: {
|
||||
filter:
|
||||
'alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND alert.attributes.tags:("hello" AND "world") AND (alert.attributes.name: "ruleName" OR alert.attributes.params.index: "ruleName" OR alert.attributes.params.threat.tactic.id: "ruleName" OR alert.attributes.params.threat.tactic.name: "ruleName" OR alert.attributes.params.threat.technique.id: "ruleName" OR alert.attributes.params.threat.technique.name: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.id: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.name: "ruleName")',
|
||||
'alert.attributes.tags:("hello" AND "world") AND (alert.attributes.name: "ruleName" OR alert.attributes.params.index: "ruleName" OR alert.attributes.params.threat.tactic.id: "ruleName" OR alert.attributes.params.threat.tactic.name: "ruleName" OR alert.attributes.params.threat.technique.id: "ruleName" OR alert.attributes.params.threat.technique.name: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.id: "ruleName" OR alert.attributes.params.threat.technique.subtechnique.name: "ruleName")',
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
sort_field: 'enabled',
|
||||
|
|
|
@ -154,8 +154,6 @@ describe('useRuleWithFallback', () => {
|
|||
"tags": Array [
|
||||
"host.name exists",
|
||||
"for testing",
|
||||
"__internal_rule_id:82b2b065-a2ee-49fc-9d6d-781a75c3d280",
|
||||
"__internal_immutable:false",
|
||||
],
|
||||
"threat": Array [
|
||||
Object {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INTERNAL_IMMUTABLE_KEY } from '../../../../../common/constants';
|
||||
import { FilterOptions } from './types';
|
||||
import { convertRulesFilterToKQL } from './utils';
|
||||
|
||||
|
@ -42,13 +41,23 @@ describe('convertRulesFilterToKQL', () => {
|
|||
it('handles presence of "showCustomRules" properly', () => {
|
||||
const kql = convertRulesFilterToKQL({ ...filterOptions, showCustomRules: true });
|
||||
|
||||
expect(kql).toBe(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`);
|
||||
expect(kql).toBe(`alert.attributes.params.immutable: false`);
|
||||
});
|
||||
|
||||
it('handles presence of "showElasticRules" properly', () => {
|
||||
const kql = convertRulesFilterToKQL({ ...filterOptions, showElasticRules: true });
|
||||
|
||||
expect(kql).toBe(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`);
|
||||
expect(kql).toBe(`alert.attributes.params.immutable: true`);
|
||||
});
|
||||
|
||||
it('handles presence of "showElasticRules" and "showCustomRules" at the same time properly', () => {
|
||||
const kql = convertRulesFilterToKQL({
|
||||
...filterOptions,
|
||||
showElasticRules: true,
|
||||
showCustomRules: true,
|
||||
});
|
||||
|
||||
expect(kql).toBe('');
|
||||
});
|
||||
|
||||
it('handles presence of "tags" properly', () => {
|
||||
|
@ -66,7 +75,7 @@ describe('convertRulesFilterToKQL', () => {
|
|||
});
|
||||
|
||||
expect(kql).toBe(
|
||||
`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND alert.attributes.tags:("tag1" AND "tag2") AND (alert.attributes.name: "foo" OR alert.attributes.params.index: "foo" OR alert.attributes.params.threat.tactic.id: "foo" OR alert.attributes.params.threat.tactic.name: "foo" OR alert.attributes.params.threat.technique.id: "foo" OR alert.attributes.params.threat.technique.name: "foo" OR alert.attributes.params.threat.technique.subtechnique.id: "foo" OR alert.attributes.params.threat.technique.subtechnique.name: "foo")`
|
||||
`alert.attributes.params.immutable: true AND alert.attributes.tags:("tag1" AND "tag2") AND (alert.attributes.name: "foo" OR alert.attributes.params.index: "foo" OR alert.attributes.params.threat.tactic.id: "foo" OR alert.attributes.params.threat.tactic.name: "foo" OR alert.attributes.params.threat.technique.id: "foo" OR alert.attributes.params.threat.technique.name: "foo" OR alert.attributes.params.threat.technique.subtechnique.id: "foo" OR alert.attributes.params.threat.technique.subtechnique.name: "foo")`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INTERNAL_IMMUTABLE_KEY } from '../../../../../common/constants';
|
||||
import { escapeKuery } from '../../../../common/lib/keury';
|
||||
import { FilterOptions } from './types';
|
||||
|
||||
|
@ -35,12 +34,12 @@ export const convertRulesFilterToKQL = ({
|
|||
}: FilterOptions): string => {
|
||||
const filters: string[] = [];
|
||||
|
||||
if (showCustomRules) {
|
||||
filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`);
|
||||
}
|
||||
|
||||
if (showElasticRules) {
|
||||
filters.push(`alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`);
|
||||
if (showCustomRules && showElasticRules) {
|
||||
// if both showCustomRules && showElasticRules selected we omit filter, as it includes all existing rules
|
||||
} else if (showElasticRules) {
|
||||
filters.push('alert.attributes.params.immutable: true');
|
||||
} else if (showCustomRules) {
|
||||
filters.push('alert.attributes.params.immutable: false');
|
||||
}
|
||||
|
||||
if (tags.length > 0) {
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyAddTags } from './legacy_add_tags';
|
||||
import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants';
|
||||
|
||||
describe('legacyAdd_tags', () => {
|
||||
test('it should add a rule id as an internal structure', () => {
|
||||
const tags = legacyAddTags([], 'rule-1');
|
||||
expect(tags).toEqual([`${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]);
|
||||
});
|
||||
|
||||
test('it should not allow duplicate tags to be created', () => {
|
||||
const tags = legacyAddTags(['tag-1', 'tag-1'], 'rule-1');
|
||||
expect(tags).toEqual(['tag-1', `${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]);
|
||||
});
|
||||
|
||||
test('it should not allow duplicate internal tags to be created when called two times in a row', () => {
|
||||
const tags1 = legacyAddTags(['tag-1'], 'rule-1');
|
||||
const tags2 = legacyAddTags(tags1, 'rule-1');
|
||||
expect(tags2).toEqual(['tag-1', `${INTERNAL_RULE_ALERT_ID_KEY}:rule-1`]);
|
||||
});
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants';
|
||||
|
||||
/**
|
||||
* @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function
|
||||
*/
|
||||
export const legacyAddTags = (tags: string[], ruleAlertId: string): string[] =>
|
||||
Array.from(new Set([...tags, `${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}`]));
|
|
@ -9,8 +9,6 @@ import { SanitizedRule } from '@kbn/alerting-plugin/common';
|
|||
import { SERVER_APP_ID, LEGACY_NOTIFICATIONS_ID } from '../../../../common/constants';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { CreateNotificationParams, LegacyRuleNotificationAlertTypeParams } from './legacy_types';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyAddTags } from './legacy_add_tags';
|
||||
|
||||
/**
|
||||
* @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function
|
||||
|
@ -26,7 +24,7 @@ export const legacyCreateNotifications = async ({
|
|||
rulesClient.create<LegacyRuleNotificationAlertTypeParams>({
|
||||
data: {
|
||||
name,
|
||||
tags: legacyAddTags([], ruleAlertId),
|
||||
tags: [],
|
||||
alertTypeId: LEGACY_NOTIFICATIONS_ID,
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
|
|
|
@ -10,7 +10,6 @@ import { RuleTypeParams, SanitizedRule } from '@kbn/alerting-plugin/common';
|
|||
import { LegacyReadNotificationParams, legacyIsAlertType } from './legacy_types';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyFindNotifications } from './legacy_find_notifications';
|
||||
import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants';
|
||||
|
||||
/**
|
||||
* @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function
|
||||
|
@ -39,7 +38,7 @@ export const legacyReadNotifications = async ({
|
|||
} else if (ruleAlertId != null) {
|
||||
const notificationFromFind = await legacyFindNotifications({
|
||||
rulesClient,
|
||||
filter: `alert.attributes.tags: "${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}"`,
|
||||
filter: `alert.attributes.params.ruleAlertId: "${ruleAlertId}"`,
|
||||
page: 1,
|
||||
});
|
||||
if (
|
||||
|
|
|
@ -17,8 +17,6 @@ import {
|
|||
DETECTION_ENGINE_SIGNALS_STATUS_URL,
|
||||
DETECTION_ENGINE_PRIVILEGES_URL,
|
||||
DETECTION_ENGINE_QUERY_SIGNALS_URL,
|
||||
INTERNAL_RULE_ID_KEY,
|
||||
INTERNAL_IMMUTABLE_KEY,
|
||||
DETECTION_ENGINE_PREPACKAGED_URL,
|
||||
DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL,
|
||||
DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL,
|
||||
|
@ -393,7 +391,7 @@ export const nonRuleAlert = () => ({
|
|||
export const getRuleMock = <T extends RuleParams>(params: T): SanitizedRule<T> => ({
|
||||
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
name: 'Detect Root/Admin Users',
|
||||
tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`],
|
||||
tags: [],
|
||||
alertTypeId: ruleTypeMappings[params.type],
|
||||
consumer: 'siem',
|
||||
params,
|
||||
|
@ -693,7 +691,7 @@ export const getSignalsMigrationStatusRequest = () =>
|
|||
export const legacyGetNotificationResult = (): LegacyRuleNotificationAlertType => ({
|
||||
id: '200dbf2f-b269-4bf9-aa85-11ba32ba73ba',
|
||||
name: 'Notification for Rule Test',
|
||||
tags: ['__internal_rule_alert_id:85b64e8a-2e40-4096-86af-5ac172c10825'],
|
||||
tags: [],
|
||||
alertTypeId: 'siem.notifications',
|
||||
consumer: 'siem',
|
||||
params: {
|
||||
|
|
|
@ -61,7 +61,7 @@ export const getPrepackagedRulesStatusRoute = (
|
|||
page: 1,
|
||||
sortField: 'enabled',
|
||||
sortOrder: 'desc',
|
||||
filter: 'alert.attributes.tags:"__internal_immutable:false"',
|
||||
filter: 'alert.attributes.params.immutable: false',
|
||||
fields: undefined,
|
||||
});
|
||||
const frameworkRequest = await buildFrameworkRequest(context, security, request);
|
||||
|
|
|
@ -16,8 +16,6 @@ import { legacyReadNotifications } from '../../notifications/legacy_read_notific
|
|||
// eslint-disable-next-line no-restricted-imports
|
||||
import { LegacyRuleNotificationAlertTypeParams } from '../../notifications/legacy_types';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyAddTags } from '../../notifications/legacy_add_tags';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyCreateNotifications } from '../../notifications/legacy_create_notifications';
|
||||
|
||||
/**
|
||||
|
@ -72,7 +70,7 @@ export const legacyCreateLegacyNotificationRoute = (
|
|||
await rulesClient.update<LegacyRuleNotificationAlertTypeParams>({
|
||||
id: notification.id,
|
||||
data: {
|
||||
tags: legacyAddTags([], ruleAlertId),
|
||||
tags: [],
|
||||
name,
|
||||
schedule: {
|
||||
interval,
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
getIdError,
|
||||
transformFindAlerts,
|
||||
transform,
|
||||
transformTags,
|
||||
getIdBulkError,
|
||||
transformAlertsToRules,
|
||||
getDuplicates,
|
||||
|
@ -23,7 +22,6 @@ import {
|
|||
migrateLegacyActionsIds,
|
||||
} from './utils';
|
||||
import { getRuleMock } from '../__mocks__/request_responses';
|
||||
import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
|
||||
import { PartialFilter } from '../../types';
|
||||
import { BulkError, createBulkErrorObject } from '../utils';
|
||||
import { getOutputRuleAlertForRest } from '../__mocks__/utils';
|
||||
|
@ -99,9 +97,9 @@ describe('utils', () => {
|
|||
expect(ruleWithEnabledFalse).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should work with tags but filter out any internal tags', () => {
|
||||
test('should work with tags', () => {
|
||||
const fullRule = getRuleMock(getQueryRuleParams());
|
||||
fullRule.tags = ['tag 1', 'tag 2', `${INTERNAL_IDENTIFIER}_some_other_value`];
|
||||
fullRule.tags = ['tag 1', 'tag 2'];
|
||||
const rule = internalRuleToAPIResponse(fullRule);
|
||||
const expected = getOutputRuleAlertForRest();
|
||||
expected.tags = ['tag 1', 'tag 2'];
|
||||
|
@ -382,23 +380,6 @@ describe('utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('transformTags', () => {
|
||||
test('it returns tags that have no internal structures', () => {
|
||||
expect(transformTags(['tag 1', 'tag 2'])).toEqual(['tag 1', 'tag 2']);
|
||||
});
|
||||
|
||||
test('it returns empty tags given empty tags', () => {
|
||||
expect(transformTags([])).toEqual([]);
|
||||
});
|
||||
|
||||
test('it returns tags with internal tags stripped out', () => {
|
||||
expect(transformTags(['tag 1', `${INTERNAL_IDENTIFIER}_some_value`, 'tag 2'])).toEqual([
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIdBulkError', () => {
|
||||
test('outputs message about id and rule_id not being found if both are not null', () => {
|
||||
const error = getIdBulkError({ id: '123', ruleId: '456' });
|
||||
|
|
|
@ -17,7 +17,6 @@ import { RuleExecutionSummary } from '../../../../../common/detection_engine/sch
|
|||
import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema';
|
||||
import { ImportRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/import_rules_schema';
|
||||
import { CreateRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/create_rules_bulk_schema';
|
||||
import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
|
||||
import { RuleAlertType, isAlertType } from '../../rules/types';
|
||||
import { createBulkErrorObject, BulkError, OutputError } from '../utils';
|
||||
import { internalRuleToAPIResponse } from '../../schemas/rule_converters';
|
||||
|
@ -88,10 +87,6 @@ export const getIdBulkError = ({
|
|||
}
|
||||
};
|
||||
|
||||
export const transformTags = (tags: string[]): string[] => {
|
||||
return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER));
|
||||
};
|
||||
|
||||
export const transformAlertsToRules = (
|
||||
rules: RuleAlertType[],
|
||||
legacyRuleActions: Record<string, LegacyRulesActionsSavedObject>
|
||||
|
|
|
@ -78,7 +78,6 @@ import {
|
|||
commonParamsCamelToSnake,
|
||||
typeSpecificCamelToSnake,
|
||||
} from '../../../schemas/rule_converters';
|
||||
import { transformTags } from '../../../routes/rules/utils';
|
||||
import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions';
|
||||
import {
|
||||
AncestorLatest,
|
||||
|
@ -215,7 +214,7 @@ export const buildAlert = (
|
|||
[ALERT_RULE_RULE_ID]: params.ruleId,
|
||||
[ALERT_RULE_RULE_NAME_OVERRIDE]: params.ruleNameOverride,
|
||||
[ALERT_RULE_SEVERITY_MAPPING]: params.severityMapping,
|
||||
[ALERT_RULE_TAGS]: transformTags(tags),
|
||||
[ALERT_RULE_TAGS]: tags,
|
||||
[ALERT_RULE_THREAT]: params.threat,
|
||||
[ALERT_RULE_THROTTLE]: throttle ?? undefined,
|
||||
[ALERT_RULE_TIMELINE_ID]: params.timelineId,
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { addTags } from './add_tags';
|
||||
import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
|
||||
|
||||
describe('add_tags', () => {
|
||||
test('it should add a rule id as an internal structure with immutable true', () => {
|
||||
const tags = addTags([], 'rule-1', true);
|
||||
expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:true`]);
|
||||
});
|
||||
|
||||
test('it should add a rule id as an internal structure with immutable false', () => {
|
||||
const tags = addTags([], 'rule-1', false);
|
||||
expect(tags).toEqual([`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`]);
|
||||
});
|
||||
|
||||
test('it should not allow duplicate tags to be created', () => {
|
||||
const tags = addTags(['tag-1', 'tag-1'], 'rule-1', false);
|
||||
expect(tags).toEqual([
|
||||
'tag-1',
|
||||
`${INTERNAL_RULE_ID_KEY}:rule-1`,
|
||||
`${INTERNAL_IMMUTABLE_KEY}:false`,
|
||||
]);
|
||||
});
|
||||
|
||||
test('it should not allow duplicate internal tags to be created when called two times in a row', () => {
|
||||
const tags1 = addTags(['tag-1'], 'rule-1', false);
|
||||
const tags2 = addTags(tags1, 'rule-1', false);
|
||||
expect(tags2).toEqual([
|
||||
'tag-1',
|
||||
`${INTERNAL_RULE_ID_KEY}:rule-1`,
|
||||
`${INTERNAL_IMMUTABLE_KEY}:false`,
|
||||
]);
|
||||
});
|
||||
|
||||
test('it should overwrite existing immutable tag if it exists', () => {
|
||||
const tags1 = addTags(['tag-1', `${INTERNAL_IMMUTABLE_KEY}:true`], 'rule-1', false);
|
||||
expect(tags1).toEqual([
|
||||
'tag-1',
|
||||
`${INTERNAL_RULE_ID_KEY}:rule-1`,
|
||||
`${INTERNAL_IMMUTABLE_KEY}:false`,
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
|
||||
|
||||
export const addTags = (tags: string[], ruleId: string, immutable: boolean): string[] => {
|
||||
return Array.from(
|
||||
new Set([
|
||||
...tags.filter(
|
||||
(tag) => !(tag.startsWith(INTERNAL_RULE_ID_KEY) || tag.startsWith(INTERNAL_IMMUTABLE_KEY))
|
||||
),
|
||||
`${INTERNAL_RULE_ID_KEY}:${ruleId}`,
|
||||
`${INTERNAL_IMMUTABLE_KEY}:${immutable}`,
|
||||
])
|
||||
);
|
||||
};
|
|
@ -19,7 +19,6 @@ import {
|
|||
SERVER_APP_ID,
|
||||
} from '../../../../common/constants';
|
||||
import { CreateRulesOptions } from './types';
|
||||
import { addTags } from './add_tags';
|
||||
import { PartialFilter } from '../types';
|
||||
import { transformToAlertThrottle, transformToNotifyWhen } from './utils';
|
||||
|
||||
|
@ -83,7 +82,7 @@ export const createRules = async ({
|
|||
},
|
||||
data: {
|
||||
name,
|
||||
tags: addTags(tags, ruleId, immutable),
|
||||
tags,
|
||||
alertTypeId: ruleTypeMappings[type],
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import uuid from 'uuid';
|
||||
import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
|
||||
import { duplicateRule } from './duplicate_rule';
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
|
@ -22,7 +21,7 @@ describe('duplicateRule', () => {
|
|||
id: 'oldTestRuleId',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
name: 'test',
|
||||
tags: ['test', '__internal_rule_id:oldTestRuleId', `${INTERNAL_IMMUTABLE_KEY}:false`],
|
||||
tags: ['test'],
|
||||
alertTypeId: 'siem.signals',
|
||||
consumer: 'siem',
|
||||
params: {
|
||||
|
@ -125,8 +124,6 @@ describe('duplicateRule', () => {
|
|||
},
|
||||
"tags": Array [
|
||||
"test",
|
||||
"__internal_rule_id:newId",
|
||||
"__internal_immutable:false",
|
||||
],
|
||||
"throttle": null,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { ruleTypeMappings } from '@kbn/securitysolution-rules';
|
|||
import { SanitizedRule } from '@kbn/alerting-plugin/common';
|
||||
import { SERVER_APP_ID } from '../../../../common/constants';
|
||||
import { InternalRuleCreate, RuleParams } from '../schemas/rule_schemas';
|
||||
import { addTags } from './add_tags';
|
||||
|
||||
const DUPLICATE_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle',
|
||||
|
@ -26,7 +25,7 @@ export const duplicateRule = (rule: SanitizedRule<RuleParams>): InternalRuleCrea
|
|||
const newRuleId = uuid.v4();
|
||||
return {
|
||||
name: `${rule.name} [${DUPLICATE_TITLE}]`,
|
||||
tags: addTags(rule.tags, newRuleId, false),
|
||||
tags: rule.tags,
|
||||
alertTypeId: ruleTypeMappings[rule.params.type],
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
|
|
|
@ -11,7 +11,6 @@ import { validate } from '@kbn/securitysolution-io-ts-utils';
|
|||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { RuleAlertType } from './types';
|
||||
import { InternalRuleUpdate, internalRuleUpdate } from '../schemas/rule_schemas';
|
||||
import { addTags } from './add_tags';
|
||||
|
||||
class EditRuleError extends Error {
|
||||
public readonly statusCode: number;
|
||||
|
@ -103,10 +102,7 @@ const validateAndSanitizeChanges = (
|
|||
throw new EditRuleError(`Internal rule editing error: can't change "params.version"`, 500);
|
||||
}
|
||||
|
||||
return {
|
||||
...changed,
|
||||
tags: addTags(changed.tags, changed.params.ruleId, changed.params.immutable),
|
||||
};
|
||||
return changed;
|
||||
};
|
||||
|
||||
const isRuleChanged = (originalRule: RuleAlertType, editedRule: RuleAlertType): boolean => {
|
||||
|
|
|
@ -6,12 +6,11 @@
|
|||
*/
|
||||
|
||||
import { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
|
||||
import { RuleAlertType, isAlertTypes } from './types';
|
||||
import { findRules } from './find_rules';
|
||||
|
||||
export const FILTER_NON_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:false"`;
|
||||
export const FILTER_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`;
|
||||
export const FILTER_NON_PREPACKED_RULES = 'alert.attributes.params.immutable: false';
|
||||
export const FILTER_PREPACKED_RULES = 'alert.attributes.params.immutable: true';
|
||||
|
||||
export const getNonPackagedRulesCount = async ({
|
||||
rulesClient,
|
||||
|
|
|
@ -16,7 +16,6 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons
|
|||
import { getExportDetailsNdjson } from './get_export_details_ndjson';
|
||||
|
||||
import { isAlertType } from './types';
|
||||
import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants';
|
||||
import { findRules } from './find_rules';
|
||||
import { getRuleExceptionsForExport } from './get_export_rule_exceptions';
|
||||
|
||||
|
@ -92,10 +91,8 @@ export const getRulesFromObjects = async (
|
|||
const chunkedObjects = chunk(objects, 1024);
|
||||
const filter = chunkedObjects
|
||||
.map((chunkedArray) => {
|
||||
const joinedIds = chunkedArray
|
||||
.map((object) => `"${INTERNAL_RULE_ID_KEY}:${object.rule_id}"`)
|
||||
.join(' OR ');
|
||||
return `alert.attributes.tags: (${joinedIds})`;
|
||||
const joinedIds = chunkedArray.map((object) => object.rule_id).join(' OR ');
|
||||
return `alert.attributes.params.ruleId: (${joinedIds})`;
|
||||
})
|
||||
.join(' OR ');
|
||||
const rules = await findRules({
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
normalizeThresholdObject,
|
||||
} from '../../../../common/detection_engine/utils';
|
||||
import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas';
|
||||
import { addTags } from './add_tags';
|
||||
import { PatchRulesOptions } from './types';
|
||||
import {
|
||||
calculateInterval,
|
||||
|
@ -190,7 +189,7 @@ export const patchRules = async ({
|
|||
);
|
||||
|
||||
const newRule = {
|
||||
tags: addTags(tags ?? rule.tags, rule.params.ruleId, rule.params.immutable),
|
||||
tags: tags ?? rule.tags,
|
||||
name: calculateName({ updatedName: name, originalName: rule.name }),
|
||||
schedule: {
|
||||
interval: calculateInterval(interval, rule.schedule.interval),
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/common';
|
||||
import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants';
|
||||
import { RuleParams } from '../schemas/rule_schemas';
|
||||
import { findRules } from './find_rules';
|
||||
import { isAlertType, ReadRuleOptions } from './types';
|
||||
|
@ -17,7 +16,7 @@ import { isAlertType, ReadRuleOptions } from './types';
|
|||
* and the id will either be found through `rulesClient.get({ id })` or it will not
|
||||
* be returned as a not-found or a thrown error that is not 404.
|
||||
* @param ruleId - This is a close second to being fast as long as it can find the rule_id from
|
||||
* a filter query against the tags using `alert.attributes.tags: "__internal:${ruleId}"]`
|
||||
* a filter query against the ruleId property in params using `alert.attributes.params.ruleId: "${ruleId}"`
|
||||
*/
|
||||
export const readRules = async ({
|
||||
rulesClient,
|
||||
|
@ -49,7 +48,7 @@ export const readRules = async ({
|
|||
} else if (ruleId != null) {
|
||||
const ruleFromFind = await findRules({
|
||||
rulesClient,
|
||||
filter: `alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`,
|
||||
filter: `alert.attributes.params.ruleId: "${ruleId}"`,
|
||||
page: 1,
|
||||
fields: undefined,
|
||||
perPage: undefined,
|
||||
|
|
|
@ -12,7 +12,6 @@ import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants';
|
|||
import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions';
|
||||
|
||||
import { UpdateRulesOptions } from './types';
|
||||
import { addTags } from './add_tags';
|
||||
import { typeSpecificSnakeToCamel } from '../schemas/rule_converters';
|
||||
import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas';
|
||||
import { maybeMute, transformToAlertThrottle, transformToNotifyWhen } from './utils';
|
||||
|
@ -39,7 +38,7 @@ export const updateRules = async ({
|
|||
const enabled = ruleUpdate.enabled ?? true;
|
||||
const newInternalRule = {
|
||||
name: ruleUpdate.name,
|
||||
tags: addTags(ruleUpdate.tags ?? [], existingRule.params.ruleId, existingRule.params.immutable),
|
||||
tags: ruleUpdate.tags ?? [],
|
||||
params: {
|
||||
author: ruleUpdate.author ?? [],
|
||||
buildingBlockType: ruleUpdate.building_block_type,
|
||||
|
|
|
@ -29,10 +29,8 @@ import {
|
|||
ResponseTypeSpecific,
|
||||
} from '../../../../common/detection_engine/schemas/request';
|
||||
import { AppClient } from '../../../types';
|
||||
import { addTags } from '../rules/add_tags';
|
||||
import { DEFAULT_MAX_SIGNALS, SERVER_APP_ID } from '../../../../common/constants';
|
||||
import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions';
|
||||
import { transformTags } from '../routes/rules/utils';
|
||||
import {
|
||||
transformFromAlertThrottle,
|
||||
transformToAlertThrottle,
|
||||
|
@ -133,7 +131,7 @@ export const convertCreateAPIToInternalSchema = (
|
|||
const newRuleId = input.rule_id ?? uuid.v4();
|
||||
return {
|
||||
name: input.name,
|
||||
tags: addTags(input.tags ?? [], newRuleId, false),
|
||||
tags: input.tags ?? [],
|
||||
alertTypeId: ruleTypeMappings[input.type],
|
||||
consumer: SERVER_APP_ID,
|
||||
params: {
|
||||
|
@ -301,7 +299,7 @@ export const internalRuleToAPIResponse = (
|
|||
created_at: rule.createdAt.toISOString(),
|
||||
created_by: rule.createdBy ?? 'elastic',
|
||||
name: rule.name,
|
||||
tags: transformTags(rule.tags),
|
||||
tags: rule.tags,
|
||||
interval: rule.schedule.interval,
|
||||
enabled: rule.enabled,
|
||||
// Security solution shared rule params
|
||||
|
|
|
@ -26,13 +26,13 @@ FILTER=${1:-'alert.attributes.enabled:%20true'}
|
|||
# ./find_rule_by_filter.sh "alert.attributes.tags:tag_1"
|
||||
|
||||
# Example get all pre-packaged rules
|
||||
# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:true%22"
|
||||
# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20true"
|
||||
|
||||
# Example get all non pre-packaged rules
|
||||
# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22"
|
||||
# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20false"
|
||||
|
||||
# Example get all non pre-packaged rules and a tag_1
|
||||
# ./find_rule_by_filter.sh "alert.attributes.tags:%20%22__internal_immutable:false%22%20AND%20alert.attributes.tags:tag_1"
|
||||
# ./find_rule_by_filter.sh "alert.attributes.params.immutable:%20false%20AND%20alert.attributes.tags:tag_1"
|
||||
curl -s -k \
|
||||
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
|
||||
-X GET ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_find?filter=$FILTER | jq .
|
||||
|
|
|
@ -53,10 +53,7 @@ to any newly saved rule:
|
|||
"_source" : {
|
||||
"alert" : {
|
||||
"name" : "kql test rule 1",
|
||||
"tags" : [
|
||||
"__internal_rule_id:4ec223b9-77fa-4895-8539-6b3e586a2858",
|
||||
"__internal_immutable:false"
|
||||
],
|
||||
"tags" : [],
|
||||
"alertTypeId" : "siem.signals",
|
||||
"other data... other data": "other data...other data",
|
||||
"exceptionsList" : [
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
|
||||
import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks';
|
||||
|
||||
import { getRuleMock, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses';
|
||||
import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants';
|
||||
import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags';
|
||||
import { readTags, convertTagsToSet, convertToTags, isTags } from './read_tags';
|
||||
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
|
||||
|
||||
describe('read_tags', () => {
|
||||
|
@ -17,88 +15,6 @@ describe('read_tags', () => {
|
|||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('readRawTags', () => {
|
||||
test('it should return the intersection of tags to where none are repeating', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getRuleMock(getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should return the intersection of tags to where some are repeating values', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3'];
|
||||
|
||||
const result2 = getRuleMock(getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4'];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']);
|
||||
});
|
||||
|
||||
test('it should work with no tags defined between two results', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
||||
const result2 = getRuleMock(getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = [];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has repeating values in it', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2'];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2']);
|
||||
});
|
||||
|
||||
test('it should work with a single tag which has empty tags', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readTags', () => {
|
||||
test('it should return the intersection of tags to where none are repeating', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
|
@ -179,56 +95,6 @@ describe('read_tags', () => {
|
|||
const tags = await readTags({ rulesClient });
|
||||
expect(tags).toEqual([]);
|
||||
});
|
||||
|
||||
test('it should filter out any __internal tags for things such as alert_id', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [
|
||||
`${INTERNAL_IDENTIFIER}_some_value`,
|
||||
`${INTERNAL_RULE_ID_KEY}_some_value`,
|
||||
'tag 1',
|
||||
];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
expect(tags).toEqual(['tag 1']);
|
||||
});
|
||||
|
||||
test('it should filter out any __internal tags with two different results', async () => {
|
||||
const result1 = getRuleMock(getQueryRuleParams());
|
||||
result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result1.params.ruleId = 'rule-1';
|
||||
result1.tags = [
|
||||
`${INTERNAL_IDENTIFIER}_some_value`,
|
||||
`${INTERNAL_RULE_ID_KEY}_some_value`,
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
'tag 3',
|
||||
'tag 4',
|
||||
'tag 5',
|
||||
];
|
||||
|
||||
const result2 = getRuleMock(getQueryRuleParams());
|
||||
result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d';
|
||||
result2.params.ruleId = 'rule-2';
|
||||
result2.tags = [
|
||||
`${INTERNAL_IDENTIFIER}_some_value`,
|
||||
`${INTERNAL_RULE_ID_KEY}_some_value`,
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
'tag 3',
|
||||
'tag 4',
|
||||
];
|
||||
|
||||
const rulesClient = rulesClientMock.create();
|
||||
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] }));
|
||||
|
||||
const tags = await readTags({ rulesClient });
|
||||
expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertTagsToSet', () => {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { has } from 'lodash/fp';
|
||||
import { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { INTERNAL_IDENTIFIER } from '../../../../common/constants';
|
||||
import { findRules } from '../rules/find_rules';
|
||||
|
||||
export interface TagType {
|
||||
|
@ -20,11 +19,11 @@ export const isTags = (obj: object): obj is TagType => {
|
|||
};
|
||||
|
||||
export const convertToTags = (tagObjects: object[]): string[] => {
|
||||
const tags = tagObjects.reduce<string[]>((accum, tagObj) => {
|
||||
const tags = tagObjects.reduce<string[]>((acc, tagObj) => {
|
||||
if (isTags(tagObj)) {
|
||||
return [...accum, ...tagObj.tags];
|
||||
return [...acc, ...tagObj.tags];
|
||||
} else {
|
||||
return accum;
|
||||
return acc;
|
||||
}
|
||||
}, []);
|
||||
return tags;
|
||||
|
@ -41,15 +40,6 @@ export const convertTagsToSet = (tagObjects: object[]): Set<string> => {
|
|||
// Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html
|
||||
export const readTags = async ({
|
||||
rulesClient,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
}): Promise<string[]> => {
|
||||
const tags = await readRawTags({ rulesClient });
|
||||
return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER));
|
||||
};
|
||||
|
||||
export const readRawTags = async ({
|
||||
rulesClient,
|
||||
}: {
|
||||
rulesClient: RulesClient;
|
||||
perPage?: number;
|
||||
|
|
|
@ -131,7 +131,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
esClient.search.mockResponseOnce(getEventLogElasticRules());
|
||||
esClient.search.mockResponseOnce(getElasticLogCustomRules());
|
||||
esClient.search.mockResponseOnce(getMockRuleAlertsResponse(800));
|
||||
savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse('not_immutable'));
|
||||
savedObjectsClient.find.mockResolvedValueOnce(getMockRuleSearchResponse(false));
|
||||
savedObjectsClient.find.mockResolvedValueOnce(getMockAlertCaseCommentsResponse());
|
||||
// Get empty saved object for legacy notification system.
|
||||
savedObjectsClient.find.mockResolvedValueOnce(getEmptySavedObjectResponse());
|
||||
|
|
|
@ -291,7 +291,7 @@ export const getMockMlDatafeedStatsResponse = () => ({
|
|||
});
|
||||
|
||||
export const getMockRuleSearchResponse = (
|
||||
immutableTag: string = '__internal_immutable:true'
|
||||
immutable: boolean = true
|
||||
): SavedObjectsFindResponse<RuleSearchResult, never> =>
|
||||
({
|
||||
page: 1,
|
||||
|
@ -304,16 +304,7 @@ export const getMockRuleSearchResponse = (
|
|||
namespaces: ['default'],
|
||||
attributes: {
|
||||
name: 'Azure Diagnostic Settings Deletion',
|
||||
tags: [
|
||||
'Elastic',
|
||||
'Cloud',
|
||||
'Azure',
|
||||
'Continuous Monitoring',
|
||||
'SecOps',
|
||||
'Monitoring',
|
||||
'__internal_rule_id:5370d4cd-2bb3-4d71-abf5-1e1d0ff5a2de',
|
||||
`${immutableTag}`,
|
||||
],
|
||||
tags: ['Elastic', 'Cloud', 'Azure', 'Continuous Monitoring', 'SecOps', 'Monitoring'],
|
||||
alertTypeId: 'siem.queryRule',
|
||||
consumer: 'siem',
|
||||
params: {
|
||||
|
@ -326,7 +317,7 @@ export const getMockRuleSearchResponse = (
|
|||
'Deletion of diagnostic settings may be done by a system or network administrator. Verify whether the username, hostname, and/or resource name should be making changes in your environment. Diagnostic settings deletion from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule.',
|
||||
],
|
||||
from: 'now-25m',
|
||||
immutable: true,
|
||||
immutable,
|
||||
query:
|
||||
'event.dataset:azure.activitylogs and azure.activitylogs.operation_name:"MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and event.outcome:(Success or success)',
|
||||
language: 'kuery',
|
||||
|
|
|
@ -9,8 +9,6 @@ import type { SavedObjectsFindResult } from '@kbn/core/server';
|
|||
import type { RuleMetric } from '../types';
|
||||
import type { RuleSearchResult } from '../../../types';
|
||||
|
||||
import { isElasticRule } from '../../../queries/utils/is_elastic_rule';
|
||||
|
||||
export interface RuleObjectCorrelationsOptions {
|
||||
ruleResults: Array<SavedObjectsFindResult<RuleSearchResult>>;
|
||||
legacyNotificationRuleIds: Map<
|
||||
|
@ -32,7 +30,6 @@ export const getRuleObjectCorrelations = ({
|
|||
return ruleResults.map((result) => {
|
||||
const ruleId = result.id;
|
||||
const { attributes } = result;
|
||||
const isElastic = isElasticRule(attributes.tags);
|
||||
|
||||
// Even if the legacy notification is set to "no_actions" we still count the rule as having a legacy notification that is not migrated yet.
|
||||
const hasLegacyNotification = legacyNotificationRuleIds.get(ruleId) != null;
|
||||
|
@ -50,7 +47,8 @@ export const getRuleObjectCorrelations = ({
|
|||
rule_type: attributes.params.type,
|
||||
rule_version: attributes.params.version,
|
||||
enabled: attributes.enabled,
|
||||
elastic_rule: isElastic,
|
||||
// if rule immutable, it's Elastic/prebuilt
|
||||
elastic_rule: attributes.params.immutable,
|
||||
created_on: attributes.createdAt,
|
||||
updated_on: attributes.updatedAt,
|
||||
alert_count_daily: alertsCounts.get(ruleId) || 0,
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
|
||||
|
||||
export const isElasticRule = (tags: string[] = []) =>
|
||||
tags.includes(`${INTERNAL_IMMUTABLE_KEY}:true`);
|
|
@ -398,14 +398,9 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
|
||||
// Only the rule that was enabled should be tagged
|
||||
expect(responseEnabledBeforeMigration.body._source?.alert?.tags).to.eql([
|
||||
'__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41',
|
||||
'__internal_immutable:false',
|
||||
'auto_disabled_8.0',
|
||||
]);
|
||||
expect(responseDisabledBeforeMigration.body._source?.alert?.tags).to.eql([
|
||||
'__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41',
|
||||
'__internal_immutable:false',
|
||||
]);
|
||||
expect(responseDisabledBeforeMigration.body._source?.alert?.tags).to.eql([]);
|
||||
});
|
||||
|
||||
it('8.2.0 migrates params to mapped_params for specific params properties', async () => {
|
||||
|
@ -423,5 +418,18 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
severity: '80-critical',
|
||||
});
|
||||
});
|
||||
|
||||
it('8.3.0 removes internal tags in Security Solution rule', async () => {
|
||||
const response = await es.get<{ alert: RawRule }>(
|
||||
{
|
||||
index: '.kibana',
|
||||
id: 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43',
|
||||
},
|
||||
{ meta: true }
|
||||
);
|
||||
|
||||
expect(response.statusCode).to.equal(200);
|
||||
expect(response.body._source?.alert?.tags).to.eql(['test-tag-1', 'foo-tag']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -65,10 +65,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
body: {
|
||||
alert: {
|
||||
name: 'test 7.14',
|
||||
tags: [
|
||||
'__internal_rule_id:82747bb8-bae0-4b59-8119-7f65ac564e14',
|
||||
'__internal_immutable:false',
|
||||
],
|
||||
tags: [],
|
||||
alertTypeId: 'siem.queryRule',
|
||||
consumer: 'siem',
|
||||
params: {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import { INTERNAL_RULE_ID_KEY } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { countDownES } from './count_down_es';
|
||||
|
||||
export const downgradeImmutableRule = async (
|
||||
|
@ -29,7 +28,7 @@ export const downgradeImmutableRule = async (
|
|||
},
|
||||
query: {
|
||||
term: {
|
||||
'alert.tags': `${INTERNAL_RULE_ID_KEY}:${ruleId}`,
|
||||
'alert.params.ruleId': ruleId,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,11 +9,7 @@ import type { ToolingLog } from '@kbn/tooling-log';
|
|||
import type SuperTest from 'supertest';
|
||||
import type { FullResponseSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/request';
|
||||
|
||||
import {
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
INTERNAL_IMMUTABLE_KEY,
|
||||
INTERNAL_RULE_ID_KEY,
|
||||
} from '@kbn/security-solution-plugin/common/constants';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
|
||||
/**
|
||||
* Helper to cut down on the noise in some of the tests. This
|
||||
|
@ -32,7 +28,7 @@ export const findImmutableRuleById = async (
|
|||
}> => {
|
||||
const response = await supertest
|
||||
.get(
|
||||
`${DETECTION_ENGINE_RULES_URL}/_find?filter=alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true" AND alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`
|
||||
`${DETECTION_ENGINE_RULES_URL}/_find?filter=alert.attributes.params.immutable: true AND alert.attributes.params.ruleId: "${ruleId}"`
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send();
|
||||
|
|
|
@ -671,10 +671,7 @@
|
|||
"source":{
|
||||
"alert":{
|
||||
"name":"enabled 7.16.1 query rule",
|
||||
"tags":[
|
||||
"__internal_rule_id:064e3fed-6328-416b-bb85-c08265088f41",
|
||||
"__internal_immutable:false"
|
||||
],
|
||||
"tags":[],
|
||||
"alertTypeId":"siem.signals",
|
||||
"consumer":"siem",
|
||||
"params":{
|
||||
|
@ -767,10 +764,7 @@
|
|||
"source":{
|
||||
"alert":{
|
||||
"name":"disabled 7.16.1 query rule",
|
||||
"tags":[
|
||||
"__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41",
|
||||
"__internal_immutable:false"
|
||||
],
|
||||
"tags":[],
|
||||
"alertTypeId":"siem.signals",
|
||||
"consumer":"siem",
|
||||
"params":{
|
||||
|
@ -895,4 +889,104 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type":"doc",
|
||||
"value":{
|
||||
"id":"alert:8990af61-c09a-11ec-9164-4bfd6fc32c43",
|
||||
"index":".kibana_1",
|
||||
"source":{
|
||||
"alert":{
|
||||
"name":"Test remove internal tags",
|
||||
"alertTypeId":"siem.queryRule",
|
||||
"consumer":"siem",
|
||||
"params":{
|
||||
"immutable":true,
|
||||
"ruleId":"bf9638eb-7d3c-4f40-83d7-8c40a7c80f2e",
|
||||
"author":[
|
||||
|
||||
],
|
||||
"description":"remove interns tags mock rule",
|
||||
"falsePositives":[
|
||||
|
||||
],
|
||||
"from":"now-36000060s",
|
||||
"license":"",
|
||||
"outputIndex":".siem-signals-default",
|
||||
"meta":{
|
||||
"from":"10000h"
|
||||
},
|
||||
"maxSignals":100,
|
||||
"riskScore":21,
|
||||
"riskScoreMapping":[
|
||||
|
||||
],
|
||||
"severity":"low",
|
||||
"severityMapping":[
|
||||
|
||||
],
|
||||
"threat":[
|
||||
|
||||
],
|
||||
"to":"now",
|
||||
"references":[
|
||||
|
||||
],
|
||||
"version":4,
|
||||
"exceptionsList":[
|
||||
],
|
||||
"type":"query",
|
||||
"language":"kuery",
|
||||
"index":[
|
||||
"apm-*-transaction*",
|
||||
"traces-apm*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"logs-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
"test-index-3"
|
||||
],
|
||||
"query":"*:*",
|
||||
"filters":[
|
||||
|
||||
]
|
||||
},
|
||||
"schedule":{
|
||||
"interval":"1m"
|
||||
},
|
||||
"enabled":true,
|
||||
"actions":[
|
||||
|
||||
],
|
||||
"throttle":null,
|
||||
"apiKeyOwner":null,
|
||||
"apiKey":null,
|
||||
"createdBy":"elastic",
|
||||
"updatedBy":"elastic",
|
||||
"createdAt":"2021-07-27T20:42:55.896Z",
|
||||
"muteAll":false,
|
||||
"mutedInstanceIds":[
|
||||
|
||||
],
|
||||
"scheduledTaskId":null,
|
||||
"tags":[
|
||||
"__internal_rule_id:364e3fed-6328-416b-bb85-c08265088f41",
|
||||
"__internal_immutable:false",
|
||||
"test-tag-1",
|
||||
"foo-tag"
|
||||
]
|
||||
},
|
||||
"type":"alert",
|
||||
"migrationVersion":{
|
||||
"alert":"8.2.0"
|
||||
},
|
||||
"updated_at":"2021-08-13T23:00:11.985Z",
|
||||
"references":[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue