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

[EDR Workflows] Automated Actions in more rule types #191874

Merged
merged 50 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5fb4a70
response actions in eql and esql rules
tomsonpl Aug 30, 2024
96c92ed
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 2, 2024
2b342da
limit to esql and eql
tomsonpl Sep 2, 2024
0db366d
add type
tomsonpl Sep 2, 2024
e9b272c
fix types
tomsonpl Sep 2, 2024
45c6f9a
fix cy test
tomsonpl Sep 2, 2024
a07a932
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 2, 2024
90990df
[CI] Auto-commit changed files from 'yarn openapi:bundle'
kibanamachine Sep 2, 2024
5c3701b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Sep 2, 2024
3a983f9
snapshots
tomsonpl Sep 2, 2024
ac7010c
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 2, 2024
62f3045
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 3, 2024
e3cef1a
fix
tomsonpl Sep 3, 2024
28a93d2
rename type
tomsonpl Sep 3, 2024
f566043
roll back ThresholdRule position
maximpn Sep 3, 2024
e878208
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 9, 2024
4e3be88
pass response actions to more rules
tomsonpl Sep 9, 2024
4bdb90c
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 9, 2024
9453127
[CI] Auto-commit changed files from 'yarn openapi:bundle'
kibanamachine Sep 9, 2024
a73360b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Sep 9, 2024
8cd270d
fix tests, remove TypeSpecificFields filter
tomsonpl Sep 9, 2024
f989c37
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 9, 2024
64dffe1
fix e2e
tomsonpl Sep 10, 2024
3e46918
fix header
tomsonpl Sep 10, 2024
3977af5
temporary hide other rule types - intermediate release
tomsonpl Sep 11, 2024
eac4b45
intermediate release - comment out e2e test
tomsonpl Sep 11, 2024
cab1f84
Update form.cy.ts
tomsonpl Sep 11, 2024
618982b
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 11, 2024
3fc2bcf
feature flag
tomsonpl Sep 12, 2024
708905a
fix
tomsonpl Sep 12, 2024
28e37f2
fixes
tomsonpl Sep 12, 2024
a27a0dd
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 12, 2024
07a573c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Sep 12, 2024
16a534c
fix
tomsonpl Sep 12, 2024
2f79a3d
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 13, 2024
6f0e36e
use TypeSpecificCreatePropsInternal instead of TypeSpecificFields
tomsonpl Sep 13, 2024
c1536b9
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 13, 2024
81fc160
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 13, 2024
6a39a14
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 16, 2024
ebf458d
Merge branch 'main' into automated-in-all-rules
tomsonpl Sep 18, 2024
ee68d55
move back response_actions to typespecific rules
tomsonpl Sep 18, 2024
1dc5cf4
move condition logic to scheduleNotificationResponseActionsService
tomsonpl Sep 18, 2024
412e456
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 18, 2024
69d9cd0
tests
tomsonpl Sep 18, 2024
d787d0a
[CI] Auto-commit changed files from 'yarn openapi:bundle'
kibanamachine Sep 18, 2024
e2680e9
fix
tomsonpl Sep 18, 2024
6215ded
Merge remote-tracking branch 'origin/automated-in-all-rules' into aut…
tomsonpl Sep 18, 2024
87c0c8b
fix tests
tomsonpl Sep 18, 2024
2b7c7f4
fix license header again
tomsonpl Sep 18, 2024
ade46fd
remove redundant spaces
tomsonpl Sep 18, 2024
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
Next Next commit
response actions in eql and esql rules
  • Loading branch information
tomsonpl committed Aug 30, 2024
commit 5fb4a702089a51726dc6e7a724f3cb76bf62f02b
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const BaseOptionalFields = z.object({
meta: RuleMetadata.optional(),
investigation_fields: InvestigationFields.optional(),
throttle: RuleActionThrottle.optional(),
response_actions: z.array(ResponseAction).optional(),
});

export type BaseDefaultableFields = z.infer<typeof BaseDefaultableFields>;
Expand Down Expand Up @@ -261,7 +262,6 @@ export const QueryRuleOptionalFields = z.object({
data_view_id: DataViewId.optional(),
filters: RuleFilterArray.optional(),
saved_id: SavedQueryId.optional(),
response_actions: z.array(ResponseAction).optional(),
alert_suppression: AlertSuppression.optional(),
});

Expand Down Expand Up @@ -312,7 +312,6 @@ export const SavedQueryRuleOptionalFields = z.object({
index: IndexPatternArray.optional(),
data_view_id: DataViewId.optional(),
filters: RuleFilterArray.optional(),
response_actions: z.array(ResponseAction).optional(),
alert_suppression: AlertSuppression.optional(),
query: RuleQuery.optional(),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ openapi: 3.0.0
info:
title: Security Rule Schema
version: 'not applicable'
paths: {}
paths: { }
components:
x-codegen-enabled: true
schemas:
Expand Down Expand Up @@ -70,6 +70,10 @@ components:
investigation_fields:
$ref: './common_attributes.schema.yaml#/components/schemas/InvestigationFields'

response_actions:
type: array
items:
$ref: '../rule_response_actions/response_actions.schema.yaml#/components/schemas/ResponseAction'
# Throttle
throttle:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleActionThrottle'
Expand Down Expand Up @@ -262,7 +266,7 @@ components:
properties:
type:
type: string
enum: [eql]
enum: [ eql ]
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: @tomsonpl Adding spaces is unnecessary. It seems your eslint YAML rules are slightly different and it leads to bigger diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I will revert 👍

description: Rule type
query:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleQuery'
Expand Down Expand Up @@ -338,7 +342,7 @@ components:
properties:
type:
type: string
enum: [query]
enum: [ query ]
description: Rule type
required:
- type
Expand All @@ -354,10 +358,6 @@ components:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleFilterArray'
saved_id:
$ref: './common_attributes.schema.yaml#/components/schemas/SavedQueryId'
response_actions:
type: array
items:
$ref: '../rule_response_actions/response_actions.schema.yaml#/components/schemas/ResponseAction'
alert_suppression:
$ref: './common_attributes.schema.yaml#/components/schemas/AlertSuppression'

Expand Down Expand Up @@ -418,7 +418,7 @@ components:
properties:
type:
type: string
enum: [saved_query]
enum: [ saved_query ]
description: Rule type
saved_id:
$ref: './common_attributes.schema.yaml#/components/schemas/SavedQueryId'
Expand All @@ -435,10 +435,6 @@ components:
$ref: './common_attributes.schema.yaml#/components/schemas/DataViewId'
filters:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleFilterArray'
response_actions:
type: array
items:
$ref: '../rule_response_actions/response_actions.schema.yaml#/components/schemas/ResponseAction'
alert_suppression:
$ref: './common_attributes.schema.yaml#/components/schemas/AlertSuppression'
query:
Expand Down Expand Up @@ -499,7 +495,7 @@ components:
properties:
type:
type: string
enum: [threshold]
enum: [ threshold ]
description: Rule type
query:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleQuery'
Expand Down Expand Up @@ -579,7 +575,7 @@ components:
properties:
type:
type: string
enum: [threat_match]
enum: [ threat_match ]
description: Rule type
query:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleQuery'
Expand Down Expand Up @@ -675,7 +671,7 @@ components:
properties:
type:
type: string
enum: [machine_learning]
enum: [ machine_learning ]
description: Rule type
anomaly_threshold:
$ref: './specific_attributes/ml_attributes.schema.yaml#/components/schemas/AnomalyThreshold'
Expand Down Expand Up @@ -737,7 +733,7 @@ components:
properties:
type:
type: string
enum: [new_terms]
enum: [ new_terms ]
description: Rule type
query:
$ref: './common_attributes.schema.yaml#/components/schemas/RuleQuery'
Expand Down Expand Up @@ -823,7 +819,7 @@ components:
properties:
type:
type: string
enum: [esql]
enum: [ esql ]
description: Rule type
language:
$ref: '#/components/schemas/EsqlQueryLanguage'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {
import { UseArray } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import type { RuleObjectId } from '../../../../../common/api/detection_engine/model/rule_schema';
import { isQueryRule } from '../../../../../common/detection_engine/utils';
import { isQueryRule, isEsqlRule, isEqlRule } from '../../../../../common/detection_engine/utils';
import { ResponseActionsForm } from '../../../rule_response_actions/response_actions_form';
import type {
RuleStepProps,
Expand Down Expand Up @@ -101,7 +101,7 @@ const StepRuleActionsComponent: FC<StepRuleActionsProps> = ({
[actionMessageParams, summaryActionMessageParams]
);
const displayResponseActionsOptions = useMemo(() => {
if (isQueryRule(ruleType)) {
if (isQueryRule(ruleType) || isEsqlRule(ruleType) || isEqlRule(ruleType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The information about which rule type supports response actions lays down here. It might be hard to get why these rule types support response actions while the others don't. As an intermediate simple improvement is to create helper function like shouldShowResponseActions() where rule type is implementation detail with a comment.

The better option is to add response actions to all rule types. Is there something blocking from that approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Maxim, good point! I am afraid we cannot call response actions against all the rules types because in order to do so we need to know what agent the alert is referring to. Do you know if any other alerts (from other rules than Query, E(S)QL contain agent.id of any kind?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now I'll adjust to use shouldShowResponseActions , but I'll drop the checks if you say we can support more types.

return (
<UseArray path="responseActions" initialNumberOfItems={0}>
{ResponseActionsForm}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*
* 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.
* 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 {
Expand All @@ -12,6 +13,7 @@ import {
tryAddingDisabledResponseAction,
validateAvailableCommands,
visitRuleActions,
fillUpNewEsqlRule,
} from '../../tasks/response_actions';
import { cleanupRule, generateRandomStringName, loadRule } from '../../tasks/api_fixtures';
import { ResponseActionTypesEnum } from '../../../../../common/api/detection_engine';
Expand Down Expand Up @@ -202,6 +204,31 @@ describe(
});
});

describe('User should be able to add response action to ESQL rule', () => {
let ruleId: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like ruleId isn't used in this test and can be removed.

const [ruleName, ruleDescription] = generateRandomStringName(2);

beforeEach(() => {
login(ROLE.soc_manager);
});
afterEach(() => {
if (ruleId) {
cleanupRule(ruleId);
}
});

it('create and save endpoint response action inside of a rule', () => {
fillUpNewEsqlRule(ruleName, ruleDescription);
addEndpointResponseAction();
focusAndOpenCommandDropdown();
validateAvailableCommands();
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could keep it procedural by extracting commands below to functions?

cy.getByTestSubj(`command-type-isolate`).click();

cy.getByTestSubj('create-enabled-false').click();
cy.contains(`${ruleName} was created`);
});
});

describe('User should not see endpoint action when no rbac', () => {
const [ruleName, ruleDescription] = generateRandomStringName(2);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*
* 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.
* 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 { inputConsoleCommand, submitCommand } from './response_console';
Expand Down Expand Up @@ -69,6 +70,28 @@ export const fillUpNewRule = (name = 'Test', description = 'Test') => {
cy.getByTestSubj('about-continue').click();
cy.getByTestSubj('schedule-continue').click();
};

export const fillUpNewEsqlRule = (name = 'Test', description = 'Test') => {
loadPage('app/security/rules/management');
cy.getByTestSubj('create-new-rule').click();
cy.getByTestSubj('stepDefineRule').within(() => {
cy.getByTestSubj('esqlRuleType').click();
cy.getByTestSubj('globalQueryBar').first().click();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we locate it in any other manner than going for the .first?

cy.getByTestSubj('kibanaCodeEditor').type(
'FROM logs-* METADATA _index, _id {backspace}{enter}'
);
});
cy.getByTestSubj('define-continue').click();
cy.getByTestSubj('detectionEngineStepAboutRuleName').within(() => {
cy.getByTestSubj('input').type(name);
});
cy.getByTestSubj('detectionEngineStepAboutRuleDescription').within(() => {
cy.getByTestSubj('input').type(description);
});
cy.getByTestSubj('about-continue').click();
cy.getByTestSubj('schedule-continue').click();
};

export const visitRuleActions = (ruleId: string) => {
loadPage(`app/security/rules/id/${ruleId}/edit`);
cy.getByTestSubj('edit-rule-actions-tab').should('exist');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe('Create rule route', () => {
});
const defaultAction = getResponseAction();

test('is successful', async () => {
test('is successful in query rule', async () => {
const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
Expand All @@ -196,6 +196,33 @@ describe('Create rule route', () => {
expect(response.status).toEqual(200);
});

test('is successful in esql rule', async () => {
const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
body: {
...getCreateEsqlRulesSchemaMock('rule-2'),
response_actions: [defaultAction],
},
});

const response = await server.inject(request, requestContextMock.convertContext(context));
expect(response.status).toEqual(200);
});
test('is successful in eql rule', async () => {
const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
body: {
...getCreateEqlRuleSchemaMock('rule-3'),
response_actions: [defaultAction],
},
});

const response = await server.inject(request, requestContextMock.convertContext(context));
expect(response.status).toEqual(200);
});

test('fails when isolate rbac is set to false', async () => {
(context.securitySolution.getEndpointAuthz as jest.Mock).mockReturnValue(() => ({
canIsolateHost: jest.fn().mockReturnValue(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe('Update rule route', () => {
});
const defaultAction = getResponseAction();

test('is successful', async () => {
test('is successful for query rule', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

It could be transformed into test.each() with rule params generated via getCreateRulesSchemaMock (), getCreateEsqlRulesSchemaMock() etc and provided as a parameter.

const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
Expand All @@ -203,6 +203,34 @@ describe('Update rule route', () => {
expect(response.status).toEqual(200);
});

test('is successful for esql rule', async () => {
const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
body: {
...getCreateEsqlRulesSchemaMock('rule-2'),
response_actions: [defaultAction],
},
});

const response = await server.inject(request, requestContextMock.convertContext(context));
expect(response.status).toEqual(200);
});

test('is successful for eql rule', async () => {
const request = requestMock.create({
method: 'post',
path: DETECTION_ENGINE_RULES_URL,
body: {
...getCreateEqlRuleSchemaMock('rule-3'),
response_actions: [defaultAction],
},
});

const response = await server.inject(request, requestContextMock.convertContext(context));
expect(response.status).toEqual(200);
});

test('fails when isolate rbac is set to false', async () => {
(context.securitySolution.getEndpointAuthz as jest.Mock).mockReturnValue(() => ({
canIsolateHost: jest.fn().mockReturnValue(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { transformAlertToRuleResponseAction } from '../../../../../../../common/detection_engine/transform_actions';
import { convertObjectKeysToSnakeCase } from '../../../../../../utils/object_case_converters';
import type { BaseRuleParams } from '../../../../rule_schema';
import { migrateLegacyInvestigationFields } from '../../../utils/utils';
Expand Down Expand Up @@ -43,5 +44,6 @@ export const commonParamsCamelToSnake = (params: BaseRuleParams) => {
related_integrations: params.relatedIntegrations ?? [],
required_fields: params.requiredFields ?? [],
setup: params.setup ?? '',
response_actions: params.responseActions?.map(transformAlertToRuleResponseAction),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ export const convertRuleResponseToAlertingRule = (
note: rule.note,
version: rule.version,
exceptionsList: rule.exceptions_list,
responseActions: params.response_actions?.map((rule) =>
transformRuleToAlertResponseAction(rule)
),
...typeSpecificParams,
},
schedule: { interval: rule.interval },
Expand Down Expand Up @@ -157,9 +160,7 @@ const typeSpecificSnakeToCamel = (params: TypeSpecificCreateProps): TypeSpecific
query: params.query ?? '',
filters: params.filters,
savedId: params.saved_id,
responseActions: params.response_actions?.map((rule) =>
transformRuleToAlertResponseAction(rule)
),

alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression),
};
}
Expand All @@ -172,9 +173,6 @@ const typeSpecificSnakeToCamel = (params: TypeSpecificCreateProps): TypeSpecific
filters: params.filters,
savedId: params.saved_id,
dataViewId: params.data_view_id,
responseActions: params.response_actions?.map((rule) =>
transformRuleToAlertResponseAction(rule)
),
alertSuppression: convertObjectKeysToCamelCase(params.alert_suppression),
};
}
Expand Down
Loading