diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts index 36feb9deb560a..131d78394cda5 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts @@ -342,10 +342,6 @@ export const UpdateRuleMigrationData = z.object({ * The migrated elastic rule attributes to update. */ elastic_rule: ElasticRulePartial.optional(), - /** - * The rule translation result. - */ - translation_result: RuleMigrationTranslationResult.optional(), /** * The comments for the migration including a summary from the LLM in markdown. */ diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml index d7b8e8d47f6e5..4443141db85b3 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml @@ -313,9 +313,6 @@ components: elastic_rule: description: The migrated elastic rule attributes to update. $ref: '#/components/schemas/ElasticRulePartial' - translation_result: - description: The rule translation result. - $ref: '#/components/schemas/RuleMigrationTranslationResult' comments: description: The comments for the migration including a summary from the LLM in markdown. $ref: '#/components/schemas/RuleMigrationComments' diff --git a/x-pack/solutions/security/plugins/security_solution/kibana.jsonc b/x-pack/solutions/security/plugins/security_solution/kibana.jsonc index 0763dafd23948..61d71620d59d7 100644 --- a/x-pack/solutions/security/plugins/security_solution/kibana.jsonc +++ b/x-pack/solutions/security/plugins/security_solution/kibana.jsonc @@ -88,7 +88,8 @@ "usageCollection", "lists", "ml", - "unifiedSearch" + "unifiedSearch", + "esql" ], "extraPublicDirs": [ "common" diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/app/translations.ts index 79de4e62f0473..5f62097b0d20c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/translations.ts @@ -104,7 +104,7 @@ export const EXCEPTIONS = i18n.translate('xpack.securitySolution.navigation.exce export const SIEM_MIGRATIONS_RULES = i18n.translate( 'xpack.securitySolution.navigation.siemMigrationsRules', { - defaultMessage: 'SIEM Rules Migrations', + defaultMessage: 'SIEM Rule Migrations', } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/links.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/links.ts index a98f823957acf..4ea47d829d794 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/links.ts @@ -19,7 +19,7 @@ export const siemMigrationsLinks: LinkItem = { id: SecurityPageName.siemMigrationsRules, title: SIEM_MIGRATIONS_RULES, description: i18n.translate('xpack.securitySolution.appLinks.siemMigrationsRulesDescription', { - defaultMessage: 'SIEM Rules Migrations.', + defaultMessage: 'SIEM Rule Migrations.', }), landingIcon: SiemMigrationsIcon, path: SIEM_MIGRATIONS_RULES_PATH, @@ -28,8 +28,9 @@ export const siemMigrationsLinks: LinkItem = { hideTimeline: true, globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.siemMigrationsRules', { - defaultMessage: 'SIEM Rules Migrations', + defaultMessage: 'SIEM Rule Migrations', }), ], experimentalKey: 'siemMigrationsEnabled', + isBeta: true, }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx index 3f255a49f87c2..ab9f8cfc5539f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx @@ -8,7 +8,7 @@ import React, { useMemo } from 'react'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; -import { EuiComboBox, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import * as i18n from './translations'; import type { RuleMigrationStats } from '../../types'; @@ -67,6 +67,10 @@ export const HeaderButtons: React.FC = React.memo( return ( + +
{i18n.SIEM_MIGRATIONS_OPTION_TITLE}
+
+ ), + disabled: ruleMigration.translation_result === RuleTranslationResult.UNTRANSLATABLE, }), - [ruleDetailsToOverview, size, expandedOverviewSections, toggleOverviewSection] + [ + ruleDetailsToOverview, + size, + expandedOverviewSections, + toggleOverviewSection, + ruleMigration.translation_result, + ] ); const summaryTab: EuiTabbedContentTab = useMemo( @@ -255,7 +264,7 @@ export const MigrationRuleDetailsFlyout: React.FC - {i18n.DISMISS_BUTTON_LABEL} + {i18n.CLOSE_BUTTON_LABEL} {ruleActions} diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor.tsx new file mode 100644 index 0000000000000..c5184fdfba5de --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor.tsx @@ -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 React, { useMemo } from 'react'; +import type { FieldValueQueryBar } from '../../../../../../../detection_engine/rule_creation_ui/components/query_bar_field'; +import { UseField } from '../../../../../../../shared_imports'; +import { EsqlEditorField } from './esql_editor_field'; +import type { RuleTranslationSchema } from '../types'; + +interface EsqlEditorFieldProps { + path: string; +} + +export const EsqlEditor: React.FC = React.memo(({ path }) => { + const componentProps = useMemo( + () => ({ + idAria: 'ruleEsqlQueryBar', + dataTestSubj: 'ruleEsqlQueryBar', + }), + [] + ); + + return ( + + path={path} + component={EsqlEditorField} + componentProps={componentProps} + /> + ); +}); +EsqlEditor.displayName = 'EsqlEditor'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor_field.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor_field.tsx new file mode 100644 index 0000000000000..425b9b0d00406 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/esql_editor_field.tsx @@ -0,0 +1,67 @@ +/* + * 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 React, { useCallback } from 'react'; +import deepEqual from 'fast-deep-equal'; +import { EuiFormRow } from '@elastic/eui'; +import { ESQLLangEditor } from '@kbn/esql/public'; +import type { AggregateQuery } from '@kbn/es-query'; +import { convertToQueryType } from '../../../../../../../common/components/query_bar/convert_to_query_type'; +import type { FieldValueQueryBar } from '../../../../../../../detection_engine/rule_creation_ui/components/query_bar_field'; +import type { FieldHook } from '../../../../../../../shared_imports'; + +interface EsqlEditorFieldProps { + field: FieldHook; + idAria?: string; + dataTestSubj: string; +} + +export const EsqlEditorField: React.FC = React.memo( + ({ field, idAria, dataTestSubj }) => { + const { value: fieldValue, setValue: setFieldValue } = field; + + const onQueryChange = useCallback( + (newQuery: AggregateQuery) => { + const { query } = fieldValue; + if (!deepEqual(query, newQuery)) { + const esqlQuery = convertToQueryType(newQuery); + setFieldValue({ ...fieldValue, query: esqlQuery }); + } + }, + [fieldValue, setFieldValue] + ); + + const onQuerySubmit = useCallback( + async (newQuery?: AggregateQuery) => { + if (newQuery) { + onQueryChange(newQuery); + } + }, + [onQueryChange] + ); + + return ( + + + + ); + } +); +EsqlEditorField.displayName = 'EsqlEditorField'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/index.tsx similarity index 58% rename from x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx rename to x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/index.tsx index 3c95eaab8fe90..68e333e7e6277 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/esql_editor/index.tsx @@ -5,8 +5,4 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - -export const PAGE_TITLE = i18n.translate('xpack.securitySolution.siemMigrations.rules.pageTitle', { - defaultMessage: 'Translated rules', -}); +export * from './esql_editor'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx index 9184c48ff75b3..8ad5c9af9a62e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/index.tsx @@ -42,15 +42,36 @@ export const TranslationTab: React.FC = React.memo( const isInstalled = !!ruleMigration.elastic_rule?.id; const canEdit = !matchedPrebuiltRule && !isInstalled; - const ruleName = matchedPrebuiltRule?.name ?? ruleMigration.elastic_rule?.title; - const originalQuery = ruleMigration.original_rule.query; - const elasticQuery = useMemo(() => { - let query = ruleMigration.elastic_rule?.query; + const originalRuleQueryComponent = useMemo(() => { + return ( + + ); + }, [ruleMigration]); + + const translatedRuleQueryComponent = useMemo(() => { + let translatedQuery = ruleMigration.elastic_rule?.query ?? ''; + let translatedQueryLanguage = ruleMigration.elastic_rule?.query_language ?? ''; if (matchedPrebuiltRule && matchedPrebuiltRule.type !== 'machine_learning') { - query = matchedPrebuiltRule.query; + translatedQuery = matchedPrebuiltRule.query ?? ''; + translatedQueryLanguage = matchedPrebuiltRule.language; } - return query ?? ''; - }, [matchedPrebuiltRule, ruleMigration.elastic_rule?.query]); + return ( + + ); + }, [canEdit, matchedPrebuiltRule, onTranslationUpdate, ruleMigration]); return ( <> @@ -96,14 +117,7 @@ export const TranslationTab: React.FC = React.memo( - - - + {originalRuleQueryComponent} = React.memo( border-right: ${euiTheme.border.thin}; `} /> - - - + {translatedRuleQueryComponent} diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/migration_rule_query.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/migration_rule_query.tsx index 51a1907da6541..9b41d43d1c0ec 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/migration_rule_query.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/migration_rule_query.tsx @@ -8,10 +8,10 @@ import React, { useCallback, useMemo, useState } from 'react'; import { EuiButtonEmpty, + EuiCodeBlock, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, - EuiMarkdownFormat, EuiSpacer, EuiTitle, useEuiTheme, @@ -19,11 +19,11 @@ import { import { css } from '@emotion/react'; import { VALIDATION_WARNING_CODES } from '../../../../../../detection_engine/rule_creation/constants/validation_warning_codes'; import { useFormWithWarnings } from '../../../../../../common/hooks/use_form_with_warnings'; -import { EsqlQueryEdit } from '../../../../../../detection_engine/rule_creation/components/esql_query_edit'; import { Field, Form, getUseField } from '../../../../../../shared_imports'; import type { RuleTranslationSchema } from './types'; import { schema } from './schema'; import * as i18n from './translations'; +import { EsqlEditor } from './esql_editor'; const CommonUseField = getUseField({ component: Field }); @@ -31,12 +31,13 @@ interface MigrationRuleQueryProps { title: string; ruleName?: string; query: string; + queryLanguage: string; canEdit?: boolean; onTranslationUpdate?: (ruleName: string, ruleQuery: string) => Promise; } export const MigrationRuleQuery: React.FC = React.memo( - ({ title, ruleName, query, canEdit, onTranslationUpdate }) => { + ({ title, ruleName, query, canEdit, queryLanguage, onTranslationUpdate }) => { const { euiTheme } = useEuiTheme(); const formDefaultValue: RuleTranslationSchema = useMemo(() => { @@ -70,6 +71,13 @@ export const MigrationRuleQuery: React.FC = React.memo( } }, [form, onTranslationUpdate]); + const codeBlockLanguage = useMemo(() => { + if (queryLanguage === 'spl') { + return 'splunk-spl'; + } + return 'sql'; + }, [queryLanguage]); + const headerComponent = useMemo(() => { return ( = React.memo(

{ruleName}

- {query} + + {query} + ); - }, [canEdit, editMode, onEdit, query, ruleName]); + }, [editMode, canEdit, onEdit, ruleName, codeBlockLanguage, query]); const editQueryComponent = useMemo(() => { if (!editMode) { @@ -146,14 +156,7 @@ export const MigrationRuleQuery: React.FC = React.memo( }, }} /> - - + ); }, [editMode, form, onCancel, onSave]); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/schema.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/schema.tsx index 4307f919cbde1..5f8bd4d9d0a0e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/schema.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/schema.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { queryRequiredValidatorFactory } from '../../../../../../detection_engine/rule_creation_ui/validators/query_required_validator_factory'; import { FIELD_TYPES, fieldValidators, type FormSchema } from '../../../../../../shared_imports'; import type { RuleTranslationSchema } from './types'; import * as i18n from './translations'; @@ -20,14 +19,4 @@ export const schema: FormSchema = { }, ], }, - queryBar: { - fieldsToValidateOnChange: ['queryBar'], - validations: [ - { - validator: (...args) => { - return queryRequiredValidatorFactory('esql')(...args); - }, - }, - ], - }, }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/translations.ts index 70325c76b9325..468e8304c62bb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/tabs/translation/translations.ts @@ -88,8 +88,7 @@ export const CALLOUT_TRANSLATED_RULE_TITLE = i18n.translate( export const CALLOUT_PARTIALLY_TRANSLATED_RULE_TITLE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutTitle', { - defaultMessage: - 'Parts of the query couldn’t be translated, please complete to Install the rule and finish migrating.', + defaultMessage: 'Part of the query could not be translated.', } ); @@ -97,15 +96,14 @@ export const CALLOUT_PARTIALLY_TRANSLATED_RULE_DESCRIPTION = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutDescription', { defaultMessage: - 'In order to save this SIEM Rule to Elastic, you’ll need to finish the query and define the rule severity below. Complete the required fields and finalize the query to save as Rule. Or if you need help, please reach out to Elastic support.', + 'To save this rule, finish the query and define its severity. If you need help, please contact Elastic support.', } ); export const CALLOUT_NOT_TRANSLATED_RULE_TITLE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.notTranslatedRuleCalloutTitle', { - defaultMessage: - 'This query couldn’t be translated, please complete to Install the rule and finish migrating.', + defaultMessage: 'This query couldn’t be translated.', } ); @@ -113,6 +111,6 @@ export const CALLOUT_NOT_TRANSLATED_RULE_DESCRIPTION = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.notTranslatedRuleCalloutDescription', { defaultMessage: - 'When a query cannot be partially translated, there could be a misalignment in features between the SIEM product.', + 'This might be caused by feature differences between SIEM products. If possible, update the rule manually.', } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translations.ts index fe6543f683e66..fe52d358b162a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translations.ts @@ -35,9 +35,9 @@ export const TRANSLATION_TAB_LABEL = i18n.translate( } ); -export const DISMISS_BUTTON_LABEL = i18n.translate( +export const CLOSE_BUTTON_LABEL = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.dismissButtonLabel', { - defaultMessage: 'Dismiss', + defaultMessage: 'Close', } ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx index 82d6c6a2f10ce..94aa5283f29fe 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx @@ -19,6 +19,7 @@ import { import React, { useCallback, useMemo, useState } from 'react'; import type { RelatedIntegration, RuleResponse } from '../../../../../common/api/detection_engine'; +import { isMigrationPrebuiltRule } from '../../../../../common/siem_migrations/rules/utils'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import { EmptyMigration } from './empty_migration'; @@ -235,19 +236,21 @@ export const MigrationRulesTable: React.FC = React.mem {i18n.INSTALL_WITHOUT_ENABLING_BUTTON_LABEL}
- - { - installSingleRule(ruleMigration, true); - closeRulePreview(); - }} - fill - data-test-subj="installAndEnableMigrationRuleFromFlyoutButton" - > - {i18n.INSTALL_AND_ENABLE_BUTTON_LABEL} - - + {isMigrationPrebuiltRule(ruleMigration.elastic_rule) && ( + + { + installSingleRule(ruleMigration, true); + closeRulePreview(); + }} + fill + data-test-subj="installAndEnableMigrationRuleFromFlyoutButton" + > + {i18n.INSTALL_AND_ENABLE_BUTTON_LABEL} + + + )}
); }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/actions.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/actions.tsx index a8175cbffc4db..a5c67403f4458 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/actions.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/actions.tsx @@ -7,17 +7,14 @@ import React from 'react'; import { EuiLink } from '@elastic/eui'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; import { RuleTranslationResult, SiemMigrationStatus, } from '../../../../../common/siem_migrations/constants'; import { getRuleDetailsUrl } from '../../../../common/components/link_to'; -import { useKibana } from '../../../../common/lib/kibana'; -import { APP_UI_ID, SecurityPageName } from '../../../../../common'; -import { - RuleMigrationStatusEnum, - type RuleMigration, -} from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import { SecurityPageName } from '../../../../../common'; +import { type RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import * as i18n from './translations'; import { type TableColumn } from './constants'; @@ -34,33 +31,25 @@ const ActionName = ({ openMigrationRuleDetails, installMigrationRule, }: ActionNameProps) => { - const { navigateToApp } = useKibana().services.application; + // Failed + if (migrationRule.status === SiemMigrationStatus.FAILED) { + return null; + } + + // Installed if (migrationRule.elastic_rule?.id) { - const ruleId = migrationRule.elastic_rule.id; return ( - { - navigateToApp(APP_UI_ID, { - deepLinkId: SecurityPageName.rules, - path: getRuleDetailsUrl(ruleId), - }); - }} + {i18n.ACTIONS_VIEW_LABEL} - - ); - } - - if (migrationRule.status === SiemMigrationStatus.FAILED) { - return ( - {}} data-test-subj="restartRule"> - {i18n.ACTIONS_RESTART_LABEL} - + ); } + // Installable if (migrationRule.translation_result === RuleTranslationResult.FULL) { return ( { - return rule.status === RuleMigrationStatusEnum.failed ? null : ( + return ( ; @@ -91,6 +91,8 @@ export const MigrationRulesPage: React.FC = React.memo( refreshStats, ]); + const pageTitle = useMemo(() => , []); + const content = useMemo(() => { const migrationStats = ruleMigrationsStats.find((stats) => stats.id === migrationId); if (!migrationId || !migrationStats) { @@ -126,7 +128,7 @@ export const MigrationRulesPage: React.FC = React.memo( - + { + const { euiTheme } = useEuiTheme(); + + return ( + + + +

{i18n.PAGE_TITLE}

+
+
+ + + + +
+ ); +}); +PageTitle.displayName = 'PageTitle'; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts new file mode 100644 index 0000000000000..fead59a60e2a0 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/pages/page_title/translations.ts @@ -0,0 +1,27 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.securitySolution.siemMigrations.rules.pageTitle', { + defaultMessage: 'Translated rules', +}); + +export const BETA_LABEL = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.pageTitle.betaBadge', + { + defaultMessage: 'Technical preview', + } +); + +export const BETA_TOOLTIP = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.pageTitle.betaTooltip', + { + defaultMessage: + 'This functionality is in technical preview and is subject to change. Please use SIEM Migrations with caution in production environments.', + } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts index f0f38cfc61481..03f76cb833818 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/utils/translation_results/translations.ts @@ -24,7 +24,7 @@ export const SIEM_TRANSLATION_RESULT_PARTIAL_LABEL = i18n.translate( export const SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationResult.untranslatable', { - defaultMessage: 'Needs manual translation', + defaultMessage: 'Not translated', } ); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/update.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/update.ts index a41ba32d2dd34..c2c52eb3bb844 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/update.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/update.ts @@ -14,6 +14,7 @@ import { import { SIEM_RULE_MIGRATIONS_PATH } from '../../../../../common/siem_migrations/constants'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { withLicense } from './util/with_license'; +import { transformToInternalUpdateRuleMigrationData } from './util/update_rules'; export const registerSiemRuleMigrationsUpdateRoute = ( router: SecuritySolutionPluginRouter, @@ -39,7 +40,10 @@ export const registerSiemRuleMigrationsUpdateRoute = ( const ctx = await context.resolve(['securitySolution']); const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient(); - await ruleMigrationsClient.data.rules.update(rulesToUpdate); + const transformedRuleToUpdate = rulesToUpdate.map( + transformToInternalUpdateRuleMigrationData + ); + await ruleMigrationsClient.data.rules.update(transformedRuleToUpdate); return res.ok({ body: { updated: true } }); } catch (err) { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/utils.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/update_rules.ts similarity index 64% rename from x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/utils.ts rename to x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/update_rules.ts index ca547da00e8c9..b0b7af8fe3af2 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/utils.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/update_rules.ts @@ -6,10 +6,12 @@ */ import { parseEsqlQuery } from '@kbn/securitysolution-utils'; +import type { UpdateRuleMigrationData } from '../../../../../../common/siem_migrations/model/rule_migration.gen'; import { RuleMigrationTranslationResultEnum, type RuleMigrationTranslationResult, -} from '../../../../../common/siem_migrations/model/rule_migration.gen'; +} from '../../../../../../common/siem_migrations/model/rule_migration.gen'; +import type { InternalUpdateRuleMigrationData } from '../../types'; export const isValidEsqlQuery = (esqlQuery: string) => { const { isEsqlQueryAggregating, hasMetadataOperator, errors } = parseEsqlQuery(esqlQuery); @@ -28,11 +30,8 @@ export const isValidEsqlQuery = (esqlQuery: string) => { }; export const convertEsqlQueryToTranslationResult = ( - esqlQuery?: string + esqlQuery: string ): RuleMigrationTranslationResult | undefined => { - if (esqlQuery === undefined) { - return undefined; - } if (esqlQuery === '') { return RuleMigrationTranslationResultEnum.untranslatable; } @@ -40,3 +39,15 @@ export const convertEsqlQueryToTranslationResult = ( ? RuleMigrationTranslationResultEnum.full : RuleMigrationTranslationResultEnum.partial; }; + +export const transformToInternalUpdateRuleMigrationData = ( + ruleMigration: UpdateRuleMigrationData +): InternalUpdateRuleMigrationData => { + if (ruleMigration.elastic_rule?.query == null) { + return ruleMigration; + } + return { + ...ruleMigration, + translation_result: convertEsqlQueryToTranslationResult(ruleMigration.elastic_rule.query), + }; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts index 7279138596795..85a78cf14b849 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts @@ -15,7 +15,7 @@ import type { QueryDslQueryContainer, Duration, } from '@elastic/elasticsearch/lib/api/types'; -import type { StoredRuleMigration } from '../types'; +import type { InternalUpdateRuleMigrationData, StoredRuleMigration } from '../types'; import { SiemMigrationStatus, RuleTranslationResult, @@ -24,12 +24,10 @@ import { type RuleMigration, type RuleMigrationTaskStats, type RuleMigrationTranslationStats, - type UpdateRuleMigrationData, } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import { RuleMigrationsDataBaseClient } from './rule_migrations_data_base_client'; import { getSortingOptions, type RuleMigrationSort } from './sort'; import { conditions as searchConditions } from './search'; -import { convertEsqlQueryToTranslationResult } from './utils'; export type CreateRuleMigrationInput = Omit< RuleMigration, @@ -96,31 +94,23 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient } /** Updates an array of rule migrations to be processed */ - async update(ruleMigrations: UpdateRuleMigrationData[]): Promise { + async update(ruleMigrations: InternalUpdateRuleMigrationData[]): Promise { const index = await this.getIndexName(); const profileId = await this.getProfileUid(); - let ruleMigrationsSlice: UpdateRuleMigrationData[]; + let ruleMigrationsSlice: InternalUpdateRuleMigrationData[]; const updatedAt = new Date().toISOString(); while ((ruleMigrationsSlice = ruleMigrations.splice(0, BULK_MAX_SIZE)).length) { await this.esClient .bulk({ refresh: 'wait_for', operations: ruleMigrationsSlice.flatMap((ruleMigration) => { - const { - id, - translation_result: translationResult, - elastic_rule: elasticRule, - ...rest - } = ruleMigration; + const { id, ...rest } = ruleMigration; return [ { update: { _index: index, _id: id } }, { doc: { ...rest, - elastic_rule: elasticRule, - translation_result: - translationResult ?? convertEsqlQueryToTranslationResult(elasticRule?.query), updated_by: profileId, updated_at: updatedAt, }, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/types.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/types.ts index d1df32ebbcb61..03811c506be35 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/types.ts @@ -6,8 +6,12 @@ */ import type { - RuleMigration, - RuleMigrationResource, + UpdateRuleMigrationData, + RuleMigrationTranslationResult, +} from '../../../../common/siem_migrations/model/rule_migration.gen'; +import { + type RuleMigration, + type RuleMigrationResource, } from '../../../../common/siem_migrations/model/rule_migration.gen'; export type Stored = T & { id: string }; @@ -31,3 +35,7 @@ export interface RuleMigrationPrebuiltRule { elser_embedding: string; mitre_attack_ids?: string[]; } + +export type InternalUpdateRuleMigrationData = UpdateRuleMigrationData & { + translation_result?: RuleMigrationTranslationResult; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/tsconfig.json b/x-pack/solutions/security/plugins/security_solution/tsconfig.json index ba59f383ea6f6..14185c37b4a35 100644 --- a/x-pack/solutions/security/plugins/security_solution/tsconfig.json +++ b/x-pack/solutions/security/plugins/security_solution/tsconfig.json @@ -188,6 +188,7 @@ "@kbn/elastic-assistant-common", "@kbn/core-elasticsearch-server-mocks", "@kbn/lens-embeddable-utils", + "@kbn/esql", "@kbn/esql-utils", "@kbn/core-test-helpers-kbn-server", "@kbn/core-ui-settings-server",