Skip to content

Commit

Permalink
[RAM][SECURITYSOLUTION][ALERTS] - Integrate Alert summary inside of s…
Browse files Browse the repository at this point in the history
…ecurity solution rule page

* [RAM][SECURITYSOLUTION][ALERTS] - Integrate per-action frequency field in security solution APIs elastic#154532
* [RAM][SECURITYSOLUTION][ALERTS] - Integrate per-action frequency UI in security solution elastic#154534
  • Loading branch information
e40pud committed Apr 15, 2023
1 parent a2ad8f0 commit a093ad8
Show file tree
Hide file tree
Showing 62 changed files with 508 additions and 441 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './src/default_severity_mapping_array';
export * from './src/default_threat_array';
export * from './src/default_to_string';
export * from './src/default_uuid';
export * from './src/frequency';
export * from './src/language';
export * from './src/machine_learning_job_id';
export * from './src/max_signals';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NonEmptyString } from '@kbn/securitysolution-io-ts-types';

import * as t from 'io-ts';
import { saved_object_attributes } from '../saved_object_attributes';
import { RuleActionFrequency } from '../frequency';

export type RuleActionGroup = t.TypeOf<typeof RuleActionGroup>;
export const RuleActionGroup = t.string;
Expand Down Expand Up @@ -71,7 +72,11 @@ export const RuleAction = t.exact(
action_type_id: RuleActionTypeId,
params: RuleActionParams,
}),
t.partial({ uuid: RuleActionUuid, alerts_filter: RuleActionAlertsFilter }),
t.partial({
uuid: RuleActionUuid,
alerts_filter: RuleActionAlertsFilter,
frequency: RuleActionFrequency,
}),
])
);

Expand All @@ -87,7 +92,11 @@ export const RuleActionCamel = t.exact(
actionTypeId: RuleActionTypeId,
params: RuleActionParams,
}),
t.partial({ uuid: RuleActionUuid, alertsFilter: RuleActionAlertsFilter }),
t.partial({
uuid: RuleActionUuid,
alertsFilter: RuleActionAlertsFilter,
frequency: RuleActionFrequency,
}),
])
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';

import { RuleActionThrottle } from '../throttle';

/**
* Action summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert
*/
export type RuleActionSummary = t.TypeOf<typeof RuleActionSummary>;
export const RuleActionSummary = t.boolean;

export type RuleActionNotifyWhen = t.TypeOf<typeof RuleActionNotifyWhen>;
export const RuleActionNotifyWhen = t.union([
t.literal('onActionGroupChange'),
t.literal('onActiveAlert'),
t.literal('onThrottleInterval'),
]);

export type RuleActionFrequency = t.TypeOf<typeof RuleActionFrequency>;
export const RuleActionFrequency = t.type({
summary: RuleActionSummary,
notifyWhen: RuleActionNotifyWhen,
throttle: t.union([RuleActionThrottle, t.null]),
});
11 changes: 11 additions & 0 deletions x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ const ruleActionSchema = schema.object({
id: schema.string(),
params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
uuid: schema.maybe(schema.string()),
frequency: schema.maybe(
schema.object({
summary: schema.boolean(),
throttle: schema.nullable(schema.string()),
notifyWhen: schema.oneOf([
schema.literal('onActionGroupChange'),
schema.literal('onActiveAlert'),
schema.literal('onThrottleInterval'),
]),
})
),
});

const operationsSchema = schema.arrayOf(
Expand Down
13 changes: 1 addition & 12 deletions x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pMap from 'p-map';
import Boom from '@hapi/boom';
import { cloneDeep, omit } from 'lodash';
import { cloneDeep } from 'lodash';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { KueryNode, nodeBuilder } from '@kbn/es-query';
import {
Expand Down Expand Up @@ -623,17 +623,6 @@ async function getUpdatedAttributesFromOperations(
isAttributesUpdateSkipped = false;
}

// TODO https://github.com/elastic/kibana/issues/148414
// If any action-level frequencies get pushed into a SIEM rule, strip their frequencies
const firstFrequency = updatedOperation.value[0]?.frequency;
if (rule.attributes.consumer === AlertConsumers.SIEM && firstFrequency) {
ruleActions.actions = ruleActions.actions.map((action) => omit(action, 'frequency'));
if (!attributes.notifyWhen) {
attributes.notifyWhen = firstFrequency.notifyWhen;
attributes.throttle = firstFrequency.throttle;
}
}

break;
}
case 'snoozeSchedule': {
Expand Down
13 changes: 0 additions & 13 deletions x-pack/plugins/alerting/server/rules_client/methods/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/
import Semver from 'semver';
import Boom from '@hapi/boom';
import { omit } from 'lodash';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { SavedObjectsUtils } from '@kbn/core/server';
import { withSpan } from '@kbn/apm-utils';
import { parseDuration } from '../../../common/parse_duration';
Expand Down Expand Up @@ -111,17 +109,6 @@ export async function create<Params extends RuleTypeParams = never>(
throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`);
}

// TODO https://github.com/elastic/kibana/issues/148414
// If any action-level frequencies get pushed into a SIEM rule, strip their frequencies
const firstFrequency = data.actions[0]?.frequency;
if (data.consumer === AlertConsumers.SIEM && firstFrequency) {
data.actions = data.actions.map((action) => omit(action, 'frequency'));
if (!data.notifyWhen) {
data.notifyWhen = firstFrequency.notifyWhen;
data.throttle = firstFrequency.throttle;
}
}

await withSpan({ name: 'validateActions', type: 'rules' }, () =>
validateActions(context, ruleType, data, allowMissingConnectorSecrets)
);
Expand Down
14 changes: 1 addition & 13 deletions x-pack/plugins/alerting/server/rules_client/methods/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
*/

import Boom from '@hapi/boom';
import { isEqual, omit } from 'lodash';
import { isEqual } from 'lodash';
import { SavedObject } from '@kbn/core/server';
import { AlertConsumers } from '@kbn/rule-data-utils';
import type { ShouldIncrementRevision } from './bulk_edit';
import {
PartialRule,
Expand Down Expand Up @@ -171,17 +170,6 @@ async function updateAlert<Params extends RuleTypeParams>(

const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId);

// TODO https://github.com/elastic/kibana/issues/148414
// If any action-level frequencies get pushed into a SIEM rule, strip their frequencies
const firstFrequency = data.actions[0]?.frequency;
if (attributes.consumer === AlertConsumers.SIEM && firstFrequency) {
data.actions = data.actions.map((action) => omit(action, 'frequency'));
if (!attributes.notifyWhen) {
attributes.notifyWhen = firstFrequency.notifyWhen;
attributes.throttle = firstFrequency.throttle;
}
}

// Validate
const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate?.params);
await validateActions(context, ruleType, data, allowMissingConnectorSecrets);
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import { RuleNotifyWhen } from '@kbn/alerting-plugin/common';

/**
* as const
*
Expand Down Expand Up @@ -377,9 +379,18 @@ export const ML_GROUP_ID = 'security' as const;
export const LEGACY_ML_GROUP_ID = 'siem' as const;
export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID] as const;

/**
* Rule Actions
*/
export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions' as const;
export const NOTIFICATION_THROTTLE_RULE = 'rule' as const;

export const NOTIFICATION_DEFAULT_FREQUENCY = {
notifyWhen: RuleNotifyWhen.ACTIVE,
throttle: null,
summary: true,
};

export const showAllOthersBucket: string[] = [
'destination.ip',
'event.action',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,28 +512,6 @@ describe('Perform bulk action request schema', () => {
expect(message.schema).toEqual({});
});

test('invalid request: missing throttle in payload', () => {
const payload = {
query: 'name: test',
action: BulkActionType.edit,
[BulkActionType.edit]: [
{
type: BulkActionEditType.add_rule_actions,
value: {
actions: [],
},
},
],
};

const message = retrieveValidationMessage(payload);

expect(getPaths(left(message.errors))).toEqual(
expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,throttle"'])
);
expect(message.schema).toEqual({});
});

test('invalid request: missing actions in payload', () => {
const payload = {
query: 'name: test',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as t from 'io-ts';

import { NonEmptyArray, TimeDuration } from '@kbn/securitysolution-io-ts-types';
import {
RuleActionFrequency,
RuleActionGroup,
RuleActionId,
RuleActionParams,
Expand Down Expand Up @@ -96,11 +97,14 @@ const BulkActionEditPayloadTimeline = t.type({
*/
type NormalizedRuleAction = t.TypeOf<typeof NormalizedRuleAction>;
const NormalizedRuleAction = t.exact(
t.type({
group: RuleActionGroup,
id: RuleActionId,
params: RuleActionParams,
})
t.intersection([
t.type({
group: RuleActionGroup,
id: RuleActionId,
params: RuleActionParams,
}),
t.partial({ frequency: RuleActionFrequency }),
])
);

export type BulkActionEditPayloadRuleActions = t.TypeOf<typeof BulkActionEditPayloadRuleActions>;
Expand All @@ -109,10 +113,12 @@ export const BulkActionEditPayloadRuleActions = t.type({
t.literal(BulkActionEditType.add_rule_actions),
t.literal(BulkActionEditType.set_rule_actions),
]),
value: t.type({
throttle: ThrottleForBulkActions,
actions: t.array(NormalizedRuleAction),
}),
value: t.intersection([
t.partial({ throttle: ThrottleForBulkActions }),
t.type({
actions: t.array(NormalizedRuleAction),
}),
]),
});

type BulkActionEditPayloadSchedule = t.TypeOf<typeof BulkActionEditPayloadSchedule>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const transformRuleToAlertAction = ({
action_type_id: actionTypeId,
params,
uuid,
frequency,
alerts_filter: alertsFilter,
}: RuleAlertAction): RuleAction => ({
group,
Expand All @@ -24,6 +25,7 @@ export const transformRuleToAlertAction = ({
actionTypeId,
...(alertsFilter && { alertsFilter }),
...(uuid && { uuid }),
...(frequency && { frequency }),
});

export const transformAlertToRuleAction = ({
Expand All @@ -32,6 +34,7 @@ export const transformAlertToRuleAction = ({
actionTypeId,
params,
uuid,
frequency,
alertsFilter,
}: RuleAction): RuleAlertAction => ({
group,
Expand All @@ -40,6 +43,7 @@ export const transformAlertToRuleAction = ({
action_type_id: actionTypeId,
...(alertsFilter && { alerts_filter: alertsFilter }),
...(uuid && { uuid }),
...(frequency && { frequency }),
});

export const transformRuleToAlertResponseAction = ({
Expand Down
Loading

0 comments on commit a093ad8

Please sign in to comment.