diff --git a/x-pack/packages/security-solution/features/src/app_features_keys.ts b/x-pack/packages/security-solution/features/src/app_features_keys.ts index ea3939e2b9f28..ab54c64cf8992 100644 --- a/x-pack/packages/security-solution/features/src/app_features_keys.ts +++ b/x-pack/packages/security-solution/features/src/app_features_keys.ts @@ -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 { diff --git a/x-pack/packages/security-solution/features/src/security/app_feature_config.ts b/x-pack/packages/security-solution/features/src/security/app_feature_config.ts index a27dccd6c5bf6..66bbfb4e5ddcd 100644 --- a/x-pack/packages/security-solution/features/src/security/app_feature_config.ts +++ b/x-pack/packages/security-solution/features/src/security/app_feature_config.ts @@ -106,4 +106,6 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig }, [AppFeatureSecurityKey.osqueryAutomatedResponseActions]: {}, + + [AppFeatureSecurityKey.externalRuleActions]: {}, }; diff --git a/x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts b/x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts index 62acff6857a8e..3dcb800dc7a74 100644 --- a/x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts +++ b/x-pack/plugins/security_solution_serverless/common/pli/pli_config.ts @@ -22,6 +22,7 @@ export const PLI_APP_FEATURES: PliAppFeatures = { AppFeatureKey.investigationGuide, AppFeatureKey.threatIntelligence, AppFeatureKey.casesConnectors, + AppFeatureKey.externalRuleActions, ], }, endpoint: { diff --git a/x-pack/plugins/security_solution_serverless/kibana.jsonc b/x-pack/plugins/security_solution_serverless/kibana.jsonc index 3756c1114c009..975aaebe45af8 100644 --- a/x-pack/plugins/security_solution_serverless/kibana.jsonc +++ b/x-pack/plugins/security_solution_serverless/kibana.jsonc @@ -19,7 +19,8 @@ "serverless", "taskManager", "cloud", - "fleet" + "fleet", + "actions" ], "optionalPlugins": [ "securitySolutionEss" diff --git a/x-pack/plugins/security_solution_serverless/server/plugin.ts b/x-pack/plugins/security_solution_serverless/server/plugin.ts index fc77d2829d9f4..99f9fbf4a1e7b 100644 --- a/x-pack/plugins/security_solution_serverless/server/plugin.ts +++ b/x-pack/plugins/security_solution_serverless/server/plugin.ts @@ -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, diff --git a/x-pack/plugins/security_solution_serverless/server/rules/enable_rule_actions.ts b/x-pack/plugins/security_solution_serverless/server/rules/enable_rule_actions.ts new file mode 100644 index 0000000000000..45e851540b286 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/server/rules/enable_rule_actions.ts @@ -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); + } +}; diff --git a/x-pack/plugins/security_solution_serverless/server/types.ts b/x-pack/plugins/security_solution_serverless/server/types.ts index 6f9c87dd92b18..44a86f534ebdf 100644 --- a/x-pack/plugins/security_solution_serverless/server/types.ts +++ b/x-pack/plugins/security_solution_serverless/server/types.ts @@ -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 { diff --git a/x-pack/plugins/security_solution_serverless/tsconfig.json b/x-pack/plugins/security_solution_serverless/tsconfig.json index 77481caa489fb..ef6c4009f345e 100644 --- a/x-pack/plugins/security_solution_serverless/tsconfig.json +++ b/x-pack/plugins/security_solution_serverless/tsconfig.json @@ -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" ] } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts new file mode 100644 index 0000000000000..6d3491a7e3acc --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_complete.cy.ts @@ -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'); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts new file mode 100644 index 0000000000000..1555b1788f6ce --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_actions/rule_actions_pli_essentials.cy.ts @@ -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'); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/common/rule_actions.ts b/x-pack/test/security_solution_cypress/cypress/screens/common/rule_actions.ts index 76975fbe760f1..e6d44f0918c6d 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/common/rule_actions.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/common/rule_actions.ts @@ -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"]';