[Security Solution][Detection Engine] adds rule actions PLI (#168510)

## Summary

- implements the first phase of [rule external actions Serverless
PLI](https://docs.google.com/spreadsheets/d/1BR9kjzSR0F6o6huxbJidk5ro4CVYfhmLog9ArEbxA8g/edit#gid=301346322).
Phase 1 is defined by support of PLI capabilities in actions plugins and
described [here](https://github.com/elastic/kibana/issues/163751). It
allows only hiding actions that are not in tier. Upselling messages,
will be introduced in phase 2, according to response ops ticket


### Essentials Tier

Serverless config:
```yaml
xpack.securitySolutionServerless.productTypes:
  [
    { product_line: 'security', product_tier: 'essentials' },
    { product_line: 'endpoint', product_tier: 'complete' },
  ]
```

For Essentials, only 3 rule actions available:

- email
- index
- slack

<img width="2525" alt="Screenshot 2023-10-11 at 11 13 57"
src="2a10d077-3090-494d-953f-2880c2afdccf">


### Complete Tier



Serverless config:
```yaml
xpack.securitySolutionServerless.productTypes:
  [
    { product_line: 'security', product_tier: 'complete' },
    { product_line: 'endpoint', product_tier: 'complete' },
  ]
```

<img width="2530" alt="Screenshot 2023-10-11 at 11 17 10"
src="1e5912a5-a358-409b-8fd6-e526e047fe54">

### Checklist

Delete any items that are not applicable to this PR.

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

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Vitalii Dmyterko 2023-10-13 16:21:43 +01:00 committed by GitHub
parent 3f6872d77b
commit 94284a105a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 213 additions and 3 deletions

View file

@ -48,6 +48,11 @@ export enum AppFeatureSecurityKey {
* Enables managing endpoint exceptions on rules and alerts
*/
endpointExceptions = 'endpointExceptions',
/**
* enables all rule actions
*/
externalRuleActions = 'external_rule_actions',
}
export enum AppFeatureCasesKey {

View file

@ -106,4 +106,6 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
},
[AppFeatureSecurityKey.osqueryAutomatedResponseActions]: {},
[AppFeatureSecurityKey.externalRuleActions]: {},
};

View file

@ -22,6 +22,7 @@ export const PLI_APP_FEATURES: PliAppFeatures = {
AppFeatureKey.investigationGuide,
AppFeatureKey.threatIntelligence,
AppFeatureKey.casesConnectors,
AppFeatureKey.externalRuleActions,
],
},
endpoint: {

View file

@ -19,7 +19,8 @@
"serverless",
"taskManager",
"cloud",
"fleet"
"fleet",
"actions"
],
"optionalPlugins": [
"securitySolutionEss"

View file

@ -32,6 +32,7 @@ import {
endpointMeteringService,
setEndpointPackagePolicyServerlessFlag,
} from './endpoint/services';
import { enableRuleActions } from './rules/enable_rule_actions';
export class SecuritySolutionServerlessPlugin
implements
@ -54,6 +55,7 @@ export class SecuritySolutionServerlessPlugin
public setup(coreSetup: CoreSetup, pluginsSetup: SecuritySolutionServerlessPluginSetupDeps) {
this.config = createConfig(this.initializerContext, pluginsSetup.securitySolution);
const enabledAppFeatures = getProductAppFeatures(this.config.productTypes);
// securitySolutionEss plugin should always be disabled when securitySolutionServerless is enabled.
// This check is an additional layer of security to prevent double registrations when
@ -63,12 +65,14 @@ export class SecuritySolutionServerlessPlugin
const productTypesStr = JSON.stringify(this.config.productTypes, null, 2);
this.logger.info(`Security Solution running with product types:\n${productTypesStr}`);
const appFeaturesConfigurator = getProductAppFeaturesConfigurator(
getProductAppFeatures(this.config.productTypes),
enabledAppFeatures,
this.config
);
pluginsSetup.securitySolution.setAppFeaturesConfigurator(appFeaturesConfigurator);
}
enableRuleActions({ actions: pluginsSetup.actions, appFeatureKeys: enabledAppFeatures });
this.cloudSecurityUsageReportingTask = new SecurityUsageReportingTask({
core: coreSetup,
logFactory: this.initializerContext.logger,

View file

@ -0,0 +1,40 @@
/*
* 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 { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
import {
IndexConnectorTypeId,
SlackWebhookConnectorTypeId,
EmailConnectorTypeId,
} from '@kbn/stack-connectors-plugin/server/connector_types';
import { EnabledActionTypes } from '@kbn/actions-plugin/server/config';
import type { AppFeatureKeys } from '@kbn/security-solution-features/src/types';
import type { PluginSetupContract as ActionsPluginSetupContract } from '@kbn/actions-plugin/server';
const INTERNAL_RULE_ACTIONS = [
IndexConnectorTypeId,
SlackWebhookConnectorTypeId,
EmailConnectorTypeId,
];
/**
* enable rule actions based on AppFeature Config
*/
export const enableRuleActions = ({
actions,
appFeatureKeys,
}: {
actions: ActionsPluginSetupContract;
appFeatureKeys: AppFeatureKeys;
}) => {
if (appFeatureKeys.includes(AppFeatureSecurityKey.externalRuleActions)) {
// enables all rule actions
actions.setEnabledConnectorTypes([EnabledActionTypes.Any]);
} else {
actions.setEnabledConnectorTypes(INTERNAL_RULE_ACTIONS);
}
};

View file

@ -18,6 +18,7 @@ import type {
import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type { SecuritySolutionEssPluginSetup } from '@kbn/security-solution-ess/server';
import type { FleetStartContract } from '@kbn/fleet-plugin/server';
import type { PluginSetupContract as ActionsPluginSetupContract } from '@kbn/actions-plugin/server';
import type { ServerlessPluginSetup } from '@kbn/serverless/server';
import type { ProductTier } from '../common/product';
@ -37,6 +38,7 @@ export interface SecuritySolutionServerlessPluginSetupDeps {
features: PluginSetupContract;
taskManager: TaskManagerSetupContract;
cloud: CloudSetup;
actions: ActionsPluginSetupContract;
}
export interface SecuritySolutionServerlessPluginStartDeps {

View file

@ -44,6 +44,8 @@
"@kbn/usage-collection-plugin",
"@kbn/cloud-defend-plugin",
"@kbn/core-logging-server-mocks",
"@kbn/shared-ux-chrome-navigation"
"@kbn/shared-ux-chrome-navigation",
"@kbn/stack-connectors-plugin",
"@kbn/actions-plugin"
]
}

View file

@ -0,0 +1,72 @@
/*
* 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 { getNewRule } from '../../../objects/rule';
import {
INDEX_SELECTOR,
SLACK_ACTION_BTN,
WEBHOOK_ACTION_BTN,
EMAIL_ACTION_BTN,
ACTION_BTN,
} from '../../../screens/common/rule_actions';
import { createRule } from '../../../tasks/api_calls/rules';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common';
import { goToActionsStepTab } from '../../../tasks/create_new_rule';
import { login } from '../../../tasks/login';
import { editFirstRule } from '../../../tasks/alerts_detection_rules';
import { visit } from '../../../tasks/navigation';
const rule = getNewRule();
describe(
'Rule actions PLI complete product tier',
{
tags: ['@serverless'],
env: {
ftrConfig: {
productTypes: [
{ product_line: 'security', product_tier: 'complete' },
{ product_line: 'endpoint', product_tier: 'complete' },
],
},
},
},
() => {
before(() => {
cleanKibana();
login();
});
beforeEach(() => {
deleteAlertsAndRules();
createRule(rule);
login();
});
it('more than 3 rule actions should be available', () => {
visit(RULES_MANAGEMENT_URL);
editFirstRule();
goToActionsStepTab();
// all actions available
cy.get(ACTION_BTN).should('have.length.greaterThan', 4);
cy.get(INDEX_SELECTOR).should('be.visible');
cy.get(SLACK_ACTION_BTN).should('be.visible');
cy.get(EMAIL_ACTION_BTN).should('be.visible');
cy.get(WEBHOOK_ACTION_BTN).should('be.visible');
});
}
);

View file

@ -0,0 +1,74 @@
/*
* 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 { getNewRule } from '../../../objects/rule';
import {
INDEX_SELECTOR,
SLACK_ACTION_BTN,
WEBHOOK_ACTION_BTN,
EMAIL_ACTION_BTN,
ACTION_BTN,
} from '../../../screens/common/rule_actions';
import { createRule } from '../../../tasks/api_calls/rules';
import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management';
import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common';
import { goToActionsStepTab } from '../../../tasks/create_new_rule';
import { login } from '../../../tasks/login';
import { editFirstRule } from '../../../tasks/alerts_detection_rules';
import { visit } from '../../../tasks/navigation';
const rule = getNewRule();
describe(
'Rule actions PLI essentials product tier',
{
tags: ['@serverless'],
env: {
ftrConfig: {
productTypes: [
{ product_line: 'security', product_tier: 'essentials' },
{ product_line: 'endpoint', product_tier: 'essentials' },
],
},
},
},
() => {
before(() => {
cleanKibana();
login();
});
beforeEach(() => {
deleteAlertsAndRules();
createRule(rule);
login();
});
it('only 3 rule actions should be available', () => {
visit(RULES_MANAGEMENT_URL);
editFirstRule();
goToActionsStepTab();
// only 3 basic actions available
cy.get(ACTION_BTN).should('have.length', 3);
cy.get(INDEX_SELECTOR).should('be.visible');
cy.get(SLACK_ACTION_BTN).should('be.visible');
cy.get(EMAIL_ACTION_BTN).should('be.visible');
// webhook is not available
cy.get(WEBHOOK_ACTION_BTN).should('not.exist');
});
}
);

View file

@ -7,6 +7,13 @@
export const EMAIL_ACTION_BTN = '[data-test-subj=".email-siem-ActionTypeSelectOption"]';
export const WEBHOOK_ACTION_BTN = '[data-test-subj=".webhook-siem-ActionTypeSelectOption"]';
/**
* all rule actions buttons, elements which data-test-subj attribute ends with '-siem-ActionTypeSelectOption'
*/
export const ACTION_BTN = '[data-test-subj$="-siem-ActionTypeSelectOption"]';
export const CREATE_ACTION_CONNECTOR_BTN = '[data-test-subj="createActionConnectorButton-0"]';
export const SAVE_ACTION_CONNECTOR_BTN = '[data-test-subj="saveActionButtonModal"]';