Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Alerts] Alert suppression per rule execution #142686

Merged
merged 89 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
41144fa
Initial commit
madirey Jul 27, 2022
781cf26
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Jul 27, 2022
8e6aa9e
No value list exceptions for now
madirey Jul 28, 2022
3c9a1b5
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Jul 28, 2022
de903ec
Gather alerts from groups for indexing
madirey Jul 28, 2022
00a817a
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Jul 28, 2022
aa6dec8
Refine algorithm
madirey Jul 28, 2022
8eea5a8
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 1, 2022
2e01a48
Fix algorithm
madirey Aug 2, 2022
903fa61
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 2, 2022
a3bf7af
Clean up prototype
madirey Aug 3, 2022
7e10630
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 3, 2022
d2f356c
add comments
madirey Aug 4, 2022
20c4745
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 4, 2022
8b764b9
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 10, 2022
20e5c12
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 10, 2022
9206c6c
Refactor
madirey Aug 10, 2022
05fedc3
Refactor
madirey Aug 10, 2022
04b3a27
More refactoring
madirey Aug 10, 2022
95a96be
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 11, 2022
674776c
Some tests
madirey Aug 11, 2022
85c8cfc
Integration test
madirey Aug 11, 2022
bf17d52
Fixes
madirey Aug 11, 2022
e793e93
Add create/patch params
madirey Aug 11, 2022
dadc419
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 15, 2022
71f26c3
Type fixes
madirey Aug 15, 2022
efbe457
Integration test
madirey Aug 15, 2022
32bc5bc
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 15, 2022
81fb0e5
Initial commit - UI
madirey Aug 15, 2022
131a9fa
type fixes
madirey Aug 16, 2022
ab4bd0e
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 16, 2022
efc015b
UI fixes
madirey Aug 18, 2022
141d7e4
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 18, 2022
90d95c9
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 21, 2022
98c54be
cleanup
madirey Aug 22, 2022
a309d05
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 22, 2022
2fc1d10
cleanup
madirey Aug 22, 2022
6fb6416
Default
madirey Aug 23, 2022
73f5ff3
Feature flag
madirey Aug 24, 2022
cba60bf
Merge branch 'main' of github.com:elastic/kibana into alert-per-entit…
madirey Aug 24, 2022
105f364
comments
madirey Aug 24, 2022
7933ba4
Do more things
madirey Aug 24, 2022
1d852bc
WIP
marshallmain Sep 26, 2022
ff176f2
Merge branch 'main' of github.com:elastic/kibana into alert-throttling
marshallmain Sep 26, 2022
6cf5285
WIP
marshallmain Sep 27, 2022
a2c1629
First implementation of backend
marshallmain Sep 28, 2022
39a94b6
Investigate throttled alert in timeline, add field to UI
marshallmain Sep 30, 2022
f1c3ff6
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Oct 4, 2022
6fa3d29
Update AAD throttling field names
marshallmain Oct 6, 2022
fc1caa6
Merge branch 'main' of github.com:elastic/kibana into alert-throttling
marshallmain Oct 24, 2022
241487d
Move alertGrouping schema to correct file
marshallmain Oct 24, 2022
438dfcc
Merge branch 'main' of github.com:elastic/kibana into alert-throttling
marshallmain Oct 24, 2022
02f3e53
Add some tests
marshallmain Oct 27, 2022
9f265e8
Add test snapshot
marshallmain Oct 27, 2022
669920b
Fix saved query rule type
marshallmain Oct 27, 2022
8a1275e
Add alert_grouping in remaining places
marshallmain Oct 27, 2022
c18b67b
Remove old grouping integration test
marshallmain Oct 27, 2022
395df7a
Fix another type
marshallmain Oct 27, 2022
f7e6817
Misc fixes
marshallmain Oct 29, 2022
f315467
Fix state returning from query executor
marshallmain Oct 29, 2022
75551bc
Merge branch 'main' into alert-throttling
marshallmain Oct 29, 2022
7f87e7b
Fix state return, alertTimestampOverride merge conflicts
marshallmain Oct 31, 2022
665142e
Merge branch 'main' into alert-throttling
marshallmain Nov 1, 2022
4350c77
Combine query and saved query files
marshallmain Nov 1, 2022
16bfd4f
Move alert throttle fields to rule registry
marshallmain Nov 1, 2022
d714131
Fix imports
marshallmain Nov 1, 2022
82c1bae
Fix more imports
marshallmain Nov 1, 2022
79e77e3
Update test
marshallmain Nov 1, 2022
93566e8
Merge branch 'main' into alert-throttling
marshallmain Nov 1, 2022
7452fd7
Change throttling references to suppression
marshallmain Nov 2, 2022
d4e4e6c
Add jsdoc comment
marshallmain Nov 2, 2022
dad1f69
Rename throttlign to suppression in more places
marshallmain Nov 7, 2022
0650f11
Update snapshot
marshallmain Nov 7, 2022
079e934
Fix rule suppression bucket state and add grouping fields limit
marshallmain Nov 8, 2022
1b45b4b
Add server side license check for alert suppression
marshallmain Nov 9, 2022
310146b
Merge branch 'main' into alert-throttling
marshallmain Nov 9, 2022
b84b679
Renaming suppression.count to suppression.docs_count
marshallmain Nov 9, 2022
9929a13
Subtract 1 from bucket count
marshallmain Nov 9, 2022
a0e2619
Merge branch 'main' into alert-throttling
marshallmain Nov 9, 2022
66fda9d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 9, 2022
d757582
Add runtime license check for suppression
marshallmain Nov 10, 2022
2df10e9
Fix test snapshot
marshallmain Nov 14, 2022
65cc647
Rule create/edit/details licensing UX
marshallmain Nov 14, 2022
454d672
Revert "Add server side license check for alert suppression"
marshallmain Nov 14, 2022
3823357
Merge branch 'main' into alert-throttling
marshallmain Nov 14, 2022
f8b4250
Fix description step tests
marshallmain Nov 14, 2022
135944e
Limit group by fields to aggregatable fields
marshallmain Nov 14, 2022
534c896
Add layers icon in rule name, fix details
marshallmain Nov 15, 2022
5d88e35
Fix incorrect i18n names
marshallmain Nov 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/kbn-rule-data-utils/src/technical_field_names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ const ALERT_UUID = `${ALERT_NAMESPACE}.uuid` as const;
const ALERT_WORKFLOW_REASON = `${ALERT_NAMESPACE}.workflow_reason` as const;
const ALERT_WORKFLOW_STATUS = `${ALERT_NAMESPACE}.workflow_status` as const;
const ALERT_WORKFLOW_USER = `${ALERT_NAMESPACE}.workflow_user` as const;
const ALERT_SUPPRESSION_META = `${ALERT_NAMESPACE}.suppression` as const;
const ALERT_SUPPRESSION_TERMS = `${ALERT_SUPPRESSION_META}.terms` as const;
const ALERT_SUPPRESSION_FIELD = `${ALERT_SUPPRESSION_TERMS}.field` as const;
const ALERT_SUPPRESSION_VALUE = `${ALERT_SUPPRESSION_TERMS}.value` as const;
const ALERT_SUPPRESSION_START = `${ALERT_SUPPRESSION_META}.start` as const;
const ALERT_SUPPRESSION_END = `${ALERT_SUPPRESSION_META}.end` as const;
const ALERT_SUPPRESSION_DOCS_COUNT = `${ALERT_SUPPRESSION_META}.docs_count` as const;

// Fields pertaining to the rule associated with the alert
const ALERT_RULE_AUTHOR = `${ALERT_RULE_NAMESPACE}.author` as const;
Expand Down Expand Up @@ -167,6 +174,12 @@ const fields = {
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_ID,
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_NAME,
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_REFERENCE,
ALERT_SUPPRESSION_TERMS,
ALERT_SUPPRESSION_FIELD,
ALERT_SUPPRESSION_VALUE,
ALERT_SUPPRESSION_START,
ALERT_SUPPRESSION_END,
ALERT_SUPPRESSION_DOCS_COUNT,
SPACE_IDS,
VERSION,
};
Expand Down Expand Up @@ -236,6 +249,12 @@ export {
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_ID,
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_NAME,
ALERT_THREAT_TECHNIQUE_SUBTECHNIQUE_REFERENCE,
ALERT_SUPPRESSION_TERMS,
ALERT_SUPPRESSION_FIELD,
ALERT_SUPPRESSION_VALUE,
ALERT_SUPPRESSION_START,
ALERT_SUPPRESSION_END,
ALERT_SUPPRESSION_DOCS_COUNT,
TAGS,
TIMESTAMP,
SPACE_IDS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,31 @@ it('matches snapshot', () => {
"required": true,
"type": "keyword",
},
"kibana.alert.suppression.docs_count": Object {
"array": false,
"required": false,
"type": "long",
},
"kibana.alert.suppression.end": Object {
"array": false,
"required": false,
"type": "date",
},
"kibana.alert.suppression.start": Object {
"array": false,
"required": false,
"type": "date",
},
"kibana.alert.suppression.terms.field": Object {
"array": true,
"required": false,
"type": "keyword",
},
"kibana.alert.suppression.terms.value": Object {
"array": true,
"required": false,
"type": "keyword",
},
"kibana.alert.system_status": Object {
"array": false,
"required": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,31 @@ export const technicalRuleFieldMap = {
array: false,
required: false,
},
[Fields.ALERT_SUPPRESSION_FIELD]: {
type: 'keyword',
array: true,
required: false,
},
[Fields.ALERT_SUPPRESSION_VALUE]: {
type: 'keyword',
array: true,
required: false,
},
[Fields.ALERT_SUPPRESSION_START]: {
type: 'date',
array: false,
required: false,
},
[Fields.ALERT_SUPPRESSION_END]: {
type: 'date',
array: false,
required: false,
},
[Fields.ALERT_SUPPRESSION_DOCS_COUNT]: {
type: 'long',
array: false,
required: false,
},
} as const;

export type TechnicalRuleFieldMap = typeof technicalRuleFieldMap;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './model/common_attributes/timeline_template';

export * from './model/specific_attributes/eql_attributes';
export * from './model/specific_attributes/new_terms_attributes';
export * from './model/specific_attributes/query_attributes';
export * from './model/specific_attributes/threshold_attributes';

export * from './model/rule_schemas';
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): QueryRule
filters: undefined,
saved_id: undefined,
response_actions: undefined,
alert_suppression: undefined,
});

export const getSavedQuerySchemaMock = (anchorDate: string = ANCHOR_DATE): SavedQueryRule => ({
Expand All @@ -87,6 +88,7 @@ export const getSavedQuerySchemaMock = (anchorDate: string = ANCHOR_DATE): Saved
data_view_id: undefined,
filters: undefined,
response_actions: undefined,
alert_suppression: undefined,
});

export const getRulesMlSchemaMock = (anchorDate: string = ANCHOR_DATE): MachineLearningRule => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import {
} from './specific_attributes/eql_attributes';
import { Threshold } from './specific_attributes/threshold_attributes';
import { HistoryWindowStart, NewTermsFields } from './specific_attributes/new_terms_attributes';
import { AlertSuppression } from './specific_attributes/query_attributes';

import { buildRuleSchemas } from './build_rule_schemas';

Expand Down Expand Up @@ -302,6 +303,7 @@ const querySchema = buildRuleSchemas({
filters: RuleFilterArray,
saved_id,
response_actions: ResponseActionArray,
alert_suppression: AlertSuppression,
},
defaultable: {
query: RuleQuery,
Expand Down Expand Up @@ -340,6 +342,7 @@ const savedQuerySchema = buildRuleSchemas({
query: RuleQuery,
filters: RuleFilterArray,
response_actions: ResponseActionArray,
alert_suppression: AlertSuppression,
},
defaultable: {
language: t.keyof({ kuery: null, lucene: null }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 * as t from 'io-ts';
import { LimitedSizeArray } from '@kbn/securitysolution-io-ts-types';

export const AlertSuppressionGroupBy = LimitedSizeArray({
codec: t.string,
minSize: 1,
maxSize: 3,
});

/**
* Schema for fields relating to alert suppression, which enables limiting the number of alerts per entity.
* e.g. group_by: ['host.name'] would create only one alert per value of host.name. The created alert
* contains metadata about how many other candidate alerts with the same host.name value were suppressed.
*/
export type AlertSuppression = t.TypeOf<typeof AlertSuppression>;
export const AlertSuppression = t.exact(
t.type({
group_by: AlertSuppressionGroupBy,
})
);

export const minimumLicenseForSuppression = 'platinum';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see check for license is only present in UI.
What about having it on API level of Security and Platform? Is there a consideration to have it in 8.6?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched from validating the params on create/edit of a rule to checking the license at runtime in the executor to avoid edge cases around license expiration/degradation. E.g. if the license expires, rules would be exportable but then would fail to import, patch would allow editing a rule while leaving suppression params untouched but update would not, etc.

The behavior now is that if a license is insufficient and suppression is enabled then rules will continue to work, but the suppression configuration will be ignored. If the platinum license is restored, then suppression will start to work again on those rules automatically. This way we can keep the functionality behind the license appropriately but not cause issues with rule management if the license level does drop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining, that makes sense 👍

I switched from validating the params on create/edit of a rule to checking the license at runtime in the executor to avoid edge cases around license expiration/degradation

Btw, can we use there minimumLicenseForSuppression instead of hardcoded platinum value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yeah that should be minimumLicenseForSuppression as well. I'll add it to the follow up list.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 type { AlertWithCommonFields800 } from '@kbn/rule-registry-plugin/common/schemas/8.0.0';
import type {
ALERT_SUPPRESSION_TERMS,
ALERT_SUPPRESSION_START,
ALERT_SUPPRESSION_END,
ALERT_SUPPRESSION_DOCS_COUNT,
} from '@kbn/rule-data-utils';

import type { BaseFields840, DetectionAlert840 } from '../8.4.0';

/* DO NOT MODIFY THIS SCHEMA TO ADD NEW FIELDS. These types represent the alerts that shipped in 8.6.0.
Any changes to these types should be bug fixes so the types more accurately represent the alerts from 8.6.0.
If you are adding new fields for a new release of Kibana, create a new sibling folder to this one
for the version to be released and add the field(s) to the schema in that folder.
Then, update `../index.ts` to import from the new folder that has the latest schemas, add the
new schemas to the union of all alert schemas, and re-export the new schemas as the `*Latest` schemas.
*/

export interface SuppressionFields860 extends BaseFields840 {
[ALERT_SUPPRESSION_TERMS]: Array<{ field: string; value: string | number | null }>;
[ALERT_SUPPRESSION_START]: Date;
[ALERT_SUPPRESSION_END]: Date;
[ALERT_SUPPRESSION_DOCS_COUNT]: number;
}

export type SuppressionAlert860 = AlertWithCommonFields800<SuppressionFields860>;

export type DetectionAlert860 = DetectionAlert840 | SuppressionAlert860;
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ import type {
NewTermsFields840,
} from './8.4.0';

import type { DetectionAlert860, SuppressionFields860 } from './8.6.0';

// When new Alert schemas are created for new Kibana versions, add the DetectionAlert type from the new version
// here, e.g. `export type DetectionAlert = DetectionAlert800 | DetectionAlert820` if a new schema is created in 8.2.0
export type DetectionAlert = DetectionAlert800 | DetectionAlert840;
export type DetectionAlert = DetectionAlert800 | DetectionAlert840 | DetectionAlert860;

export type {
Ancestor840 as AncestorLatest,
BaseFields840 as BaseFieldsLatest,
DetectionAlert840 as DetectionAlertLatest,
DetectionAlert860 as DetectionAlertLatest,
WrappedFields840 as WrappedFieldsLatest,
EqlBuildingBlockFields840 as EqlBuildingBlockFieldsLatest,
EqlShellFields840 as EqlShellFieldsLatest,
NewTermsFields840 as NewTermsFieldsLatest,
SuppressionFields860 as SuppressionFieldsLatest,
};
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep
history_window_start: `now-${ruleFields.historyWindowSize}`,
}
: {
...(ruleFields.groupByFields.length > 0
? { alert_suppression: { group_by: ruleFields.groupByFields } }
: {}),
index: ruleFields.index,
filters: ruleFields.queryBar?.filters,
language: ruleFields.queryBar?.query?.language,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types';

import { RuleExecutionSummary } from '../../../../common/detection_engine/rule_monitoring';
import {
AlertSuppression,
AlertsIndex,
BuildingBlockType,
DataViewId,
Expand Down Expand Up @@ -191,6 +192,7 @@ export const RuleSchema = t.intersection([
uuid: t.string,
version: RuleVersion,
execution_summary: RuleExecutionSummary,
alert_suppression: AlertSuppression,
}),
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ export const mockRuleWithEverything = (id: string): Rule => ({
timestamp_override_fallback_disabled: false,
note: '# this is some markdown documentation',
version: 1,
alert_suppression: {
group_by: ['host.name'],
},
new_terms_fields: ['host.name'],
history_window_start: 'now-7d',
});
Expand Down Expand Up @@ -226,6 +229,7 @@ export const mockDefineStepRule = (): DefineStepRule => ({
newTermsFields: ['host.ip'],
historyWindowSize: '7d',
shouldLoadQueryDynamically: false,
groupByFields: [],
});

export const mockScheduleStepRule = (): ScheduleStepRule => ({
Expand Down
Loading