[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:
Vitalii Dmyterko 2022-04-26 17:27:09 +01:00 committed by GitHub
parent 3f20878699
commit ffe1cdddc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 252 additions and 446 deletions

View file

@ -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'];

View file

@ -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>) =>

View file

@ -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": {

View file

@ -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
*/

View file

@ -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,

View file

@ -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',

View file

@ -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 {

View file

@ -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")`
);
});
});

View file

@ -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) {

View file

@ -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`]);
});
});

View file

@ -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}`]));

View file

@ -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: {

View file

@ -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 (

View file

@ -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: {

View file

@ -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);

View file

@ -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,

View file

@ -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' });

View file

@ -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>

View file

@ -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,

View file

@ -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`,
]);
});
});

View file

@ -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}`,
])
);
};

View file

@ -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: {

View file

@ -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,
}

View file

@ -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: {

View file

@ -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 => {

View file

@ -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,

View file

@ -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({

View file

@ -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),

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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 .

View file

@ -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" : [

View file

@ -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', () => {

View file

@ -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;

View file

@ -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());

View file

@ -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',

View file

@ -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,

View file

@ -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`);

View file

@ -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']);
});
});
}

View file

@ -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: {

View file

@ -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,
},
},
},

View file

@ -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();

View file

@ -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":[
]
}
}
}