[8.x] [Security Solution] Move rule actions field and its validation into the common folder (#215494) (#215949)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution] Move rule actions field and its validation into
the common folder
(#215494)](https://github.com/elastic/kibana/pull/215494)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"ievgen.sorokopud@elastic.co"},"sourceCommit":{"committedDate":"2025-03-25T19:50:08Z","message":"[Security
Solution] Move rule actions field and its validation into the common
folder (#215494)\n\n## Summary\n\nWith this PR we make
`RuleActionsField` component and relevant\nvalidations reusable outside
of and not bound to the rules management.\n\nAs part of the Attack
Discovery
Scheduling\n[feature](https://github.com/elastic/security-team/issues/10142)
we\nwould like to be able to setup schedules (similar to detection
rules,\njust named differently within the feature space) and be able to
add\nactions (email, slack, webhook etc.).\n\nCurrently
`RuleActionsField` lives inside
the\n`x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/`\nfolder.
We could just reference it from within the Attack Discovery\nfolder, but
for better code structure it will be good to put it into a\ncommon
place.\n\n### Checklist\n\nCheck the PR satisfies following conditions.
\n\nReviewers should verify this PR satisfies this list as well.\n\n-
[x] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests
changed\n-\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/8075\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"760b0cfca324eabaf544f23fdff819bab441576c","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:
SecuritySolution","Team:Detection Rule Management","Team:Detection
Engine","Team:Security Generative
AI","backport:version","v9.1.0","v8.19.0"],"title":"[Security Solution]
Move rule actions field and its validation into the common
folder","number":215494,"url":"https://github.com/elastic/kibana/pull/215494","mergeCommit":{"message":"[Security
Solution] Move rule actions field and its validation into the common
folder (#215494)\n\n## Summary\n\nWith this PR we make
`RuleActionsField` component and relevant\nvalidations reusable outside
of and not bound to the rules management.\n\nAs part of the Attack
Discovery
Scheduling\n[feature](https://github.com/elastic/security-team/issues/10142)
we\nwould like to be able to setup schedules (similar to detection
rules,\njust named differently within the feature space) and be able to
add\nactions (email, slack, webhook etc.).\n\nCurrently
`RuleActionsField` lives inside
the\n`x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/`\nfolder.
We could just reference it from within the Attack Discovery\nfolder, but
for better code structure it will be good to put it into a\ncommon
place.\n\n### Checklist\n\nCheck the PR satisfies following conditions.
\n\nReviewers should verify this PR satisfies this list as well.\n\n-
[x] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests
changed\n-\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/8075\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"760b0cfca324eabaf544f23fdff819bab441576c"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/215494","number":215494,"mergeCommit":{"message":"[Security
Solution] Move rule actions field and its validation into the common
folder (#215494)\n\n## Summary\n\nWith this PR we make
`RuleActionsField` component and relevant\nvalidations reusable outside
of and not bound to the rules management.\n\nAs part of the Attack
Discovery
Scheduling\n[feature](https://github.com/elastic/security-team/issues/10142)
we\nwould like to be able to setup schedules (similar to detection
rules,\njust named differently within the feature space) and be able to
add\nactions (email, slack, webhook etc.).\n\nCurrently
`RuleActionsField` lives inside
the\n`x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/`\nfolder.
We could just reference it from within the Attack Discovery\nfolder, but
for better code structure it will be good to put it into a\ncommon
place.\n\n### Checklist\n\nCheck the PR satisfies following conditions.
\n\nReviewers should verify this PR satisfies this list as well.\n\n-
[x] [Flaky
Test\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\nused on any tests
changed\n-\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/8075\n\n---------\n\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"760b0cfca324eabaf544f23fdff819bab441576c"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Ievgen Sorokopud 2025-03-25 22:53:05 +01:00 committed by GitHub
parent 0b3e6bf6a5
commit baa0362f78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 28 additions and 29 deletions

View file

@ -245,6 +245,7 @@ module.exports = {
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]panel[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]popover_items[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]progress_inline[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]rule_actions_field[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]selectable_text[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]subtitle[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]components[\/\\]text_field_value[\/\\]index.stories.tsx/,
@ -272,7 +273,6 @@ module.exports = {
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]eql_query_edit[\/\\]footer.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]ml_job_link[\/\\]ml_job_link.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]ml_jobs_description[\/\\]ml_job_item.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]rule_actions_field[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]schedule_item_field[\/\\]schedule_item_field.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]schedule_item_form[\/\\]index.tsx/,
/x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]step_about_rule_details[\/\\]index.test.tsx/,

View file

@ -39161,7 +39161,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchQueryBar.requiredError": "Une requête d'index indicative est requise.",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "Tous les résultats",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.docsLinkText": "En savoir plus",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage": "{key} n'est pas un modèle de moustache valide",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "Impossible de créer des actions de règle. Vous ne disposez pas des autorisations \"Lire\" pour le plug-in \"Actions\".",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozeDescription": "Choisissez quand effectuer des actions ou les mettre en sommeil. Les notifications ne sont pas créées pour des actions répétées. {docs}.",
"xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "Créer et activer la règle",

View file

@ -39131,7 +39131,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchQueryBar.requiredError": "インジケーターインデックスクエリが必要です。",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "すべての結果",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.docsLinkText": "詳細",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage": "{key}は有効なmustacheテンプレートではありません",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "ルールアクションを作成できません。「Actions」プラグインの「読み取り」アクセス権がありません。",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozeDescription": "いつアクションを実行するか、いつアクションをスヌーズするかを選択します。スヌーズされたアクションに対しては通知が作成されません。{docs}。",
"xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "ルールを作成して有効にする",

View file

@ -39201,7 +39201,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchQueryBar.requiredError": "指标索引查询必填。",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "所有结果",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.docsLinkText": "了解详情",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage": "{key} 不是有效的 Mustache 模板",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "无法创建规则操作。您对“操作”插件没有“读”权限。",
"xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozeDescription": "选择执行操作的时间,或暂停这些操作。没有为已暂停操作创建通知。{docs}。",
"xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "创建并启用规则",

View file

@ -9,10 +9,10 @@ import React from 'react';
import { shallow } from 'enzyme';
import { RuleActionsField } from '.';
import { useForm, Form } from '../../../../shared_imports';
import { useKibana } from '../../../../common/lib/kibana';
import { useFormFieldMock } from '../../../../common/mock';
jest.mock('../../../../common/lib/kibana');
import { useForm, Form } from '../../../shared_imports';
import { useKibana } from '../../lib/kibana';
import { useFormFieldMock } from '../../mock';
jest.mock('../../lib/kibana');
describe('RuleActionsField', () => {
it('should not render ActionForm if no actions are supported', () => {

View file

@ -10,6 +10,7 @@ import { EuiSpacer, EuiCallOut, EuiText } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import deepMerge from 'deepmerge';
import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
import type {
@ -24,10 +25,10 @@ import type {
import { SecurityConnectorFeatureId } from '@kbn/actions-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { NOTIFICATION_DEFAULT_FREQUENCY } from '../../../../../common/constants';
import type { FieldHook } from '../../../../shared_imports';
import { useFormContext } from '../../../../shared_imports';
import { useKibana } from '../../../../common/lib/kibana';
import { NOTIFICATION_DEFAULT_FREQUENCY } from '../../../../common/constants';
import type { FieldHook } from '../../../shared_imports';
import { useFormContext } from '../../../shared_imports';
import { useKibana } from '../../lib/kibana';
import {
FORM_CUSTOM_FREQUENCY_OPTION,
FORM_ERRORS_TITLE,

View file

@ -10,7 +10,7 @@ import { startCase } from 'lodash/fp';
export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage',
'xpack.securitySolution.ruleActions.validation.invalidMustacheTemplateErrorMessage',
{
defaultMessage: '{key} is not valid mustache template',
values: {

View file

@ -8,7 +8,7 @@
import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/action_type_registry.mock';
import { getActionTypeName, validateMustache, validateActionParams } from './utils';
describe('stepRuleActions utils', () => {
describe('RuleActions utils', () => {
describe('getActionTypeName', () => {
it('should return capitalized action type name', () => {
expect(getActionTypeName('.slack')).toEqual('Slack');

View file

@ -8,7 +8,7 @@
import { validateSingleAction, validateRuleActionsField } from './validate_rule_actions_field';
import { getActionTypeName, validateMustache, validateActionParams } from './utils';
import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/action_type_registry.mock';
import type { FormHook } from '../../../../../shared_imports';
import type { FormHook } from '../../../../shared_imports';
jest.mock('./utils');
describe('validate_rule_actions_field', () => {

View file

@ -13,14 +13,13 @@ import type {
ValidationResponsePromise,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import type {
ActionTypeRegistryContract,
RuleAction,
ActionTypeRegistryContract,
} from '@kbn/triggers-actions-ui-plugin/public';
import { validateActionFilterQuery } from '@kbn/triggers-actions-ui-plugin/public';
import type { RuleActionsFormData } from '../../../../../detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form';
import type { ActionsStepRule } from '../../../../../detection_engine/common/types';
import type { ERROR_CODE, ValidationFunc } from '../../../../../shared_imports';
import { getActionTypeName, validateActionParams, validateMustache } from './utils';
import type { FormData, ValidationFunc, ERROR_CODE } from '../../../../shared_imports';
import { getActionTypeName, validateMustache, validateActionParams } from './utils';
export const DEFAULT_VALIDATION_TIMEOUT = 100;
@ -37,8 +36,10 @@ export const validateSingleAction = async (
export const validateRuleActionsField =
(actionTypeRegistry: ActionTypeRegistryContract) =>
async (...data: Parameters<ValidationFunc>): ValidationResponsePromise<ERROR_CODE> => {
const [{ value, path }] = data as [{ value: RuleAction[]; path: string }];
async (
...data: Parameters<ValidationFunc<FormData, string, RuleAction[]>>
): ValidationResponsePromise<ERROR_CODE> => {
const [{ value, path }] = data;
const errors = [];
for (const actionItem of value) {
@ -75,11 +76,11 @@ export const validateRuleActionsField =
* @param defaultValidationTimeout
*/
export const debouncedValidateRuleActionsField =
(
<I extends FormData>(
actionTypeRegistry: ActionTypeRegistryContract,
defaultValidationTimeout = DEFAULT_VALIDATION_TIMEOUT
) =>
(data: ValidationFuncArg<ActionsStepRule | RuleActionsFormData>): ValidationResponsePromise => {
(data: ValidationFuncArg<I, RuleAction[]>): ValidationResponsePromise => {
let isCanceled = false;
const promise: ValidationCancelablePromise = new Promise((resolve) => {
setTimeout(() => {

View file

@ -6,7 +6,7 @@
*/
import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public';
import { debouncedValidateRuleActionsField } from '../../../../detections/containers/detection_engine/rules/validate_rule_actions_field';
import { debouncedValidateRuleActionsField } from '../../../../common/containers/rule_actions/validate_rule_actions_field';
import type { FormSchema } from '../../../../shared_imports';
import type { ActionsStepRule } from '../../../common/types';

View file

@ -15,13 +15,13 @@ import type {
ActionVariables,
} from '@kbn/triggers-actions-ui-plugin/public';
import { UseArray } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { RuleActionsField } from '../../../../common/components/rule_actions_field';
import type { RuleObjectId } from '../../../../../common/api/detection_engine/model/rule_schema';
import { ResponseActionsForm } from '../../../rule_response_actions/response_actions_form';
import type { ActionsStepRule, RuleStepProps } from '../../../common/types';
import type { FormHook } from '../../../../shared_imports';
import { Form, UseField } from '../../../../shared_imports';
import { StepContentWrapper } from '../step_content_wrapper';
import { RuleActionsField } from '../rule_actions_field';
import { useKibana } from '../../../../common/lib/kibana';
import { useFetchConnectorsQuery } from '../../../rule_management/api/hooks/use_fetch_connectors_query';
import { useFetchConnectorTypesQuery } from '../../../rule_management/api/hooks/use_fetch_connector_types_query';

View file

@ -13,6 +13,7 @@ import type {
ActionTypeRegistryContract,
} from '@kbn/triggers-actions-ui-plugin/public';
import { RuleActionsField } from '../../../../../../common/components/rule_actions_field';
import { transformAlertToNormalizedRuleAction } from '../../../../../../../common/detection_engine/transform_actions';
import type { FormSchema } from '../../../../../../shared_imports';
import {
@ -33,8 +34,7 @@ import { useKibana } from '../../../../../../common/lib/kibana';
import { getAllActionMessageParams } from '../../../../../common/helpers';
import { RuleActionsField } from '../../../../../rule_creation/components/rule_actions_field';
import { debouncedValidateRuleActionsField } from '../../../../../../detections/containers/detection_engine/rules/validate_rule_actions_field';
import { debouncedValidateRuleActionsField } from '../../../../../../common/containers/rule_actions/validate_rule_actions_field';
const CommonUseField = getUseField({ component: Field });

View file

@ -10,11 +10,11 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { map, reduce } from 'lodash';
import ReactMarkdown from 'react-markdown';
import { ResponseActionsWrapper } from './response_actions_wrapper';
import { FORM_ERRORS_TITLE } from '../rule_creation/components/rule_actions_field/translations';
import { ResponseActionsHeader } from './response_actions_header';
import type { ArrayItem, FormHook } from '../../shared_imports';
import { useSupportedResponseActionTypes } from './use_supported_response_action_types';
import { getActionDetails } from './constants';
import { FORM_ERRORS_TITLE } from '../../common/components/rule_actions_field/translations';
interface ResponseActionsFormProps {
items: ArrayItem[];