From b3d6d914b347bb1abc8822131d20de9f20f38ec0 Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Sun, 8 Dec 2024 23:04:39 +0100 Subject: [PATCH] [Rules migration] Add functionality to display matched prebuilt rules details (#11360) (#203035) ## Summary [Internal link](https://github.com/elastic/security-team/issues/10820) to the feature details These changes add functionality that allows to display matched prebuilt rules details. ### New route There is a new route `/internal/siem_migrations/rules/{migration_id}/prebuilt_rules` that will return all prebuilt rules matched by translated rules within a specific migration. ### UI changes The rule migration details flyout was updated to display matched prebuilt rule data in both `Translation` and `Overview` tabs. https://github.com/user-attachments/assets/3da49653-e0ab-4d8b-892e-dd05cf73743b ### Other changes Also, as part of this PR, batching of a rule installation/creation was added. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Sergi Massaneda --- .../common/api/quickstart_client.gen.ts | 23 +++ .../common/siem_migrations/constants.ts | 2 + .../model/api/rules/rule_migration.gen.ts | 19 ++ .../api/rules/rule_migration.schema.yaml | 27 +++ .../model/rule_migration.gen.ts | 16 ++ .../model/rule_migration.schema.yaml | 13 ++ .../common/siem_migrations/rules/utils.ts | 36 ++++ .../public/siem_migrations/rules/api/index.ts | 19 ++ .../components/rule_details_flyout/index.tsx | 70 ++++---- .../translation_tab/index.tsx | 165 ++++++++++-------- .../translation_tab/translations.ts | 7 + .../rules/components/rules_table/index.tsx | 9 +- .../use_migration_rule_preview_flyout.tsx | 29 ++- .../rules/logic/translations.ts | 7 + .../logic/use_get_migration_prebuilt_rules.ts | 39 +++++ .../siem_migrations/rules/api/constants.ts | 10 ++ .../rules/api/get_prebuilt_rules.ts | 73 ++++++++ .../lib/siem_migrations/rules/api/index.ts | 2 + .../rules/api/util/installation.ts | 156 ++++++++--------- .../rules/api/util/prebuilt_rules.ts | 84 +++++++++ .../data/rule_migrations_data_rules_client.ts | 7 +- .../services/security_solution_api.gen.ts | 25 +++ 22 files changed, 638 insertions(+), 200 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/siem_migrations/rules/utils.ts create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/logic/use_get_migration_prebuilt_rules.ts create mode 100644 x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/constants.ts create mode 100644 x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts create mode 100644 x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index 0ef67f164409b..b5d72fc1ef207 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -363,6 +363,8 @@ import type { GetRuleMigrationRequestQueryInput, GetRuleMigrationRequestParamsInput, GetRuleMigrationResponse, + GetRuleMigrationPrebuiltRulesRequestParamsInput, + GetRuleMigrationPrebuiltRulesResponse, GetRuleMigrationResourcesRequestQueryInput, GetRuleMigrationResourcesRequestParamsInput, GetRuleMigrationResourcesResponse, @@ -1431,6 +1433,24 @@ finalize it. }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Retrieves all available prebuilt rules (installed and installable) + */ + async getRuleMigrationPrebuiltRules(props: GetRuleMigrationPrebuiltRulesProps) { + this.log.info(`${new Date().toISOString()} Calling API GetRuleMigrationPrebuiltRules`); + return this.kbnClient + .request({ + path: replaceParams( + '/internal/siem_migrations/rules/{migration_id}/prebuilt_rules', + props.params + ), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '1', + }, + method: 'GET', + }) + .catch(catchAxiosErrorFormatAndThrow); + } /** * Retrieves resources for an existing SIEM rules migration */ @@ -2396,6 +2416,9 @@ export interface GetRuleMigrationProps { query: GetRuleMigrationRequestQueryInput; params: GetRuleMigrationRequestParamsInput; } +export interface GetRuleMigrationPrebuiltRulesProps { + params: GetRuleMigrationPrebuiltRulesRequestParamsInput; +} export interface GetRuleMigrationResourcesProps { query: GetRuleMigrationResourcesRequestQueryInput; params: GetRuleMigrationResourcesRequestParamsInput; diff --git a/x-pack/plugins/security_solution/common/siem_migrations/constants.ts b/x-pack/plugins/security_solution/common/siem_migrations/constants.ts index e04130e7f44d7..e947dda4bbcc2 100644 --- a/x-pack/plugins/security_solution/common/siem_migrations/constants.ts +++ b/x-pack/plugins/security_solution/common/siem_migrations/constants.ts @@ -23,6 +23,8 @@ export const SIEM_RULE_MIGRATION_STOP_PATH = `${SIEM_RULE_MIGRATION_PATH}/stop` export const SIEM_RULE_MIGRATION_INSTALL_PATH = `${SIEM_RULE_MIGRATION_PATH}/install` as const; export const SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH = `${SIEM_RULE_MIGRATION_PATH}/install_translated` as const; +export const SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH = + `${SIEM_RULE_MIGRATION_PATH}/prebuilt_rules` as const; export const SIEM_RULE_MIGRATION_RESOURCES_PATH = `${SIEM_RULE_MIGRATION_PATH}/resources` as const; diff --git a/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts b/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts index 8a549e8e11817..58944ff7f2f95 100644 --- a/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts +++ b/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts @@ -26,6 +26,7 @@ import { OriginalRule, RuleMigration, RuleMigrationTranslationStats, + PrebuiltRuleVersion, RuleMigrationResourceData, RuleMigrationResourceType, RuleMigrationResource, @@ -76,6 +77,24 @@ export const GetRuleMigrationResponse = z.object({ total: z.number(), data: z.array(RuleMigration), }); + +export type GetRuleMigrationPrebuiltRulesRequestParams = z.infer< + typeof GetRuleMigrationPrebuiltRulesRequestParams +>; +export const GetRuleMigrationPrebuiltRulesRequestParams = z.object({ + migration_id: NonEmptyString, +}); +export type GetRuleMigrationPrebuiltRulesRequestParamsInput = z.input< + typeof GetRuleMigrationPrebuiltRulesRequestParams +>; + +/** + * The map of prebuilt rules, with the rules id as a key + */ +export type GetRuleMigrationPrebuiltRulesResponse = z.infer< + typeof GetRuleMigrationPrebuiltRulesResponse +>; +export const GetRuleMigrationPrebuiltRulesResponse = z.object({}).catchall(PrebuiltRuleVersion); export type GetRuleMigrationResourcesRequestQuery = z.infer< typeof GetRuleMigrationResourcesRequestQuery >; diff --git a/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml b/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml index 8b9d264cf4104..dff6089b2b48f 100644 --- a/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml +++ b/x-pack/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml @@ -355,6 +355,33 @@ paths: 204: description: Indicates the migration id was not found running. + /internal/siem_migrations/rules/{migration_id}/prebuilt_rules: + get: + summary: Retrieves all prebuilt rules for a specific migration + operationId: GetRuleMigrationPrebuiltRules + x-codegen-enabled: true + x-internal: true + description: Retrieves all available prebuilt rules (installed and installable) + tags: + - SIEM Rule Migrations + parameters: + - name: migration_id + in: path + required: true + schema: + description: The migration id to retrieve prebuilt rules for + $ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString' + responses: + 200: + description: Indicates prebuilt rules have been retrieved correctly. + content: + application/json: + schema: + type: object + description: The map of prebuilt rules, with the rules id as a key + additionalProperties: + $ref: '../../rule_migration.schema.yaml#/components/schemas/PrebuiltRuleVersion' + # Rule migration resources APIs /internal/siem_migrations/rules/{migration_id}/resources: diff --git a/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts b/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts index b52cdb1c91f19..60220bf054a12 100644 --- a/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts +++ b/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.gen.ts @@ -17,6 +17,7 @@ import { z } from '@kbn/zod'; import { NonEmptyString } from '../../api/model/primitives.gen'; +import { RuleResponse } from '../../api/detection_engine/model/rule_schema/rule_schemas.gen'; /** * The original rule vendor identifier. @@ -117,6 +118,21 @@ export const ElasticRule = z.object({ export type ElasticRulePartial = z.infer; export const ElasticRulePartial = ElasticRule.partial(); +/** + * The prebuilt rule version. + */ +export type PrebuiltRuleVersion = z.infer; +export const PrebuiltRuleVersion = z.object({ + /** + * The latest available version of prebuilt rule. + */ + target: RuleResponse, + /** + * The currently installed version of prebuilt rule. + */ + current: RuleResponse.optional(), +}); + /** * The rule translation result. */ diff --git a/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml b/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml index 4c88c66fc604d..e13e9b1d0ed75 100644 --- a/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml +++ b/x-pack/plugins/security_solution/common/siem_migrations/model/rule_migration.schema.yaml @@ -97,6 +97,19 @@ components: $ref: '#/components/schemas/ElasticRule' x-modify: partial + PrebuiltRuleVersion: + type: object + description: The prebuilt rule version. + required: + - target + properties: + target: + description: The latest available version of prebuilt rule. + $ref: '../../../common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse' + current: + description: The currently installed version of prebuilt rule. + $ref: '../../../common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse' + RuleMigration: description: The rule migration document object. allOf: diff --git a/x-pack/plugins/security_solution/common/siem_migrations/rules/utils.ts b/x-pack/plugins/security_solution/common/siem_migrations/rules/utils.ts new file mode 100644 index 0000000000000..a9b8666b19981 --- /dev/null +++ b/x-pack/plugins/security_solution/common/siem_migrations/rules/utils.ts @@ -0,0 +1,36 @@ +/* + * 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 type { Severity } from '../../api/detection_engine'; +import { DEFAULT_TRANSLATION_FIELDS, DEFAULT_TRANSLATION_SEVERITY } from '../constants'; +import type { ElasticRule, ElasticRulePartial } from '../model/rule_migration.gen'; + +export type MigrationPrebuiltRule = ElasticRulePartial & + Required>; + +export type MigrationCustomRule = ElasticRulePartial & + Required>; + +export const isMigrationPrebuiltRule = (rule?: ElasticRule): rule is MigrationPrebuiltRule => + !!(rule?.title && rule?.description && rule?.prebuilt_rule_id); + +export const isMigrationCustomRule = (rule?: ElasticRule): rule is MigrationCustomRule => + !isMigrationPrebuiltRule(rule) && + !!(rule?.title && rule?.description && rule?.query && rule?.query_language); + +export const convertMigrationCustomRuleToSecurityRulePayload = (rule: MigrationCustomRule) => { + return { + type: rule.query_language, + language: rule.query_language, + query: rule.query, + name: rule.title, + description: rule.description, + + ...DEFAULT_TRANSLATION_FIELDS, + severity: (rule.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY, + }; +}; diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/index.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/index.ts index db6f0117d4a77..ac9e49ff861fc 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/index.ts +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/index.ts @@ -19,6 +19,7 @@ import { SIEM_RULE_MIGRATION_START_PATH, SIEM_RULE_MIGRATION_STATS_PATH, SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH, + SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, } from '../../../../common/siem_migrations/constants'; import type { CreateRuleMigrationRequestBody, @@ -30,6 +31,7 @@ import type { InstallMigrationRulesResponse, StartRuleMigrationRequestBody, GetRuleMigrationStatsResponse, + GetRuleMigrationPrebuiltRulesResponse, } from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; export interface GetRuleMigrationStatsParams { @@ -192,3 +194,20 @@ export const installTranslatedMigrationRules = async ({ { version: '1', signal } ); }; + +export interface GetRuleMigrationsPrebuiltRulesParams { + /** `id` of the migration to install rules for */ + migrationId: string; + /** Optional AbortSignal for cancelling request */ + signal?: AbortSignal; +} +/** Retrieves all prebuilt rules matched within a specific migration. */ +export const getRuleMigrationsPrebuiltRules = async ({ + migrationId, + signal, +}: GetRuleMigrationsPrebuiltRulesParams): Promise => { + return KibanaServices.get().http.get( + replaceParams(SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, { migration_id: migrationId }), + { version: '1', signal } + ); +}; diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/index.tsx index 9353d0063b8ab..8fea17b51cb0e 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/index.tsx @@ -24,19 +24,12 @@ import { } from '@elastic/eui'; import type { EuiTabbedContentTab, EuiTabbedContentProps, EuiFlyoutProps } from '@elastic/eui'; -import { - DEFAULT_TRANSLATION_SEVERITY, - DEFAULT_TRANSLATION_FIELDS, -} from '../../../../../common/siem_migrations/constants'; import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import { RuleOverviewTab, useOverviewTabSections, } from '../../../../detection_engine/rule_management/components/rule_details/rule_overview_tab'; -import { - type RuleResponse, - type Severity, -} from '../../../../../common/api/detection_engine/model/rule_schema'; +import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import * as i18n from './translations'; import { @@ -44,6 +37,10 @@ import { LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS, } from './constants'; import { TranslationTab } from './translation_tab'; +import { + convertMigrationCustomRuleToSecurityRulePayload, + isMigrationCustomRule, +} from '../../../../../common/siem_migrations/rules/utils'; /* * Fixes tabs to the top and allows the content to scroll. @@ -67,6 +64,7 @@ export const TabContentPadding: FC> = ({ children }) interface MigrationRuleDetailsFlyoutProps { ruleActions?: React.ReactNode; ruleMigration: RuleMigration; + matchedPrebuiltRule?: RuleResponse; size?: EuiFlyoutProps['size']; extraTabs?: EuiTabbedContentTab[]; closeFlyout: () => void; @@ -76,26 +74,21 @@ export const MigrationRuleDetailsFlyout: React.FC { const { expandedOverviewSections, toggleOverviewSection } = useOverviewTabSections(); - const rule: RuleResponse = useMemo(() => { - const esqlLanguage = ruleMigration.elastic_rule?.query_language ?? 'esql'; - return { - type: esqlLanguage, - language: esqlLanguage, - name: ruleMigration.elastic_rule?.title, - description: ruleMigration.elastic_rule?.description, - query: ruleMigration.elastic_rule?.query, - - ...DEFAULT_TRANSLATION_FIELDS, - severity: - (ruleMigration.elastic_rule?.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY, - } as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter - }, [ruleMigration]); + const rule = useMemo(() => { + if (isMigrationCustomRule(ruleMigration.elastic_rule)) { + return convertMigrationCustomRuleToSecurityRulePayload( + ruleMigration.elastic_rule + ) as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter; + } + return matchedPrebuiltRule; + }, [matchedPrebuiltRule, ruleMigration]); const translationTab: EuiTabbedContentTab = useMemo( () => ({ @@ -103,11 +96,14 @@ export const MigrationRuleDetailsFlyout: React.FC - + ), }), - [ruleMigration] + [matchedPrebuiltRule, ruleMigration] ); const overviewTab: EuiTabbedContentTab = useMemo( @@ -116,16 +112,18 @@ export const MigrationRuleDetailsFlyout: React.FC - + {rule && ( + + )} ), }), @@ -166,7 +164,9 @@ export const MigrationRuleDetailsFlyout: React.FC -

{rule.name}

+

+ {rule?.name ?? ruleMigration.original_rule.title} +

diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/index.tsx index a2e590b85ac09..a80480b8837bb 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/index.tsx +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiAccordion, EuiBadge, @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { css } from '@emotion/css'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { RuleResponse } from '../../../../../../common/api/detection_engine'; import type { RuleMigration } from '../../../../../../common/siem_migrations/model/rule_migration.gen'; import { TranslationTabHeader } from './header'; import { MigrationRuleQuery } from './migration_rule_query'; @@ -31,82 +32,98 @@ import { interface TranslationTabProps { ruleMigration: RuleMigration; + matchedPrebuiltRule?: RuleResponse; } -export const TranslationTab: React.FC = React.memo(({ ruleMigration }) => { - const { euiTheme } = useEuiTheme(); +export const TranslationTab: React.FC = React.memo( + ({ ruleMigration, matchedPrebuiltRule }) => { + const { euiTheme } = useEuiTheme(); - const name = ruleMigration.elastic_rule?.title ?? ruleMigration.original_rule.title; - const originalQuery = ruleMigration.original_rule.query; - const elasticQuery = ruleMigration.elastic_rule?.query ?? 'Prebuilt rule query'; + const name = useMemo( + () => ruleMigration.elastic_rule?.title ?? ruleMigration.original_rule.title, + [ruleMigration.elastic_rule?.title, ruleMigration.original_rule.title] + ); + const originalQuery = ruleMigration.original_rule.query; + const elasticQuery = useMemo(() => { + let query = ruleMigration.elastic_rule?.query; + if (matchedPrebuiltRule && matchedPrebuiltRule.type !== 'machine_learning') { + query = matchedPrebuiltRule.query; + } + return query ?? ''; + }, [matchedPrebuiltRule, ruleMigration.elastic_rule?.query]); - return ( - <> - - - - - - } - initialIsOpen={true} - > - - - - - - - -

- -

-
-
- - {}} - onClickAriaLabel={'Click to update translation status'} - > - {convertTranslationResultIntoText(ruleMigration.translation_result)} - - -
-
- - - - + + + + + + } + initialIsOpen={true} + > + + + + + + + +

+ +

+
+
+ + {}} + onClickAriaLabel={'Click to update translation status'} + > + {convertTranslationResultIntoText(ruleMigration.translation_result)} + + +
+
+ + + + + + - - - - - - - -
-
-
- - ); -}); + + + +
+
+
+
+
+ + ); + } +); TranslationTab.displayName = 'TranslationTab'; diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/translations.ts index e7532a5a8b2e3..1592a80d32478 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/translations.ts +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rule_details_flyout/translation_tab/translations.ts @@ -28,6 +28,13 @@ export const SPLUNK_QUERY_TITLE = i18n.translate( } ); +export const PREBUILT_RULE_QUERY_TITLE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.prebuiltRuleQueryTitle', + { + defaultMessage: 'Prebuilt rule query', + } +); + export const ESQL_TRANSLATION_TITLE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.esqlTranslationTitle', { diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx index e7af1af93e2ba..13b451c2918d8 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx @@ -26,6 +26,7 @@ import { useInstallMigrationRules } from '../../logic/use_install_migration_rule import { useGetMigrationRules } from '../../logic/use_get_migration_rules'; import { useInstallTranslatedMigrationRules } from '../../logic/use_install_translated_migration_rules'; import { useGetMigrationTranslationStats } from '../../logic/use_get_migration_translation_stats'; +import { useGetMigrationPrebuiltRules } from '../../logic/use_get_migration_prebuilt_rules'; import * as logicI18n from '../../logic/translations'; import { BulkActions } from './bulk_actions'; import { SearchField } from './search_field'; @@ -53,6 +54,9 @@ export const MigrationRulesTable: React.FC = React.mem const { data: translationStats, isLoading: isStatsLoading } = useGetMigrationTranslationStats(migrationId); + const { data: prebuiltRules = {}, isLoading: isPrebuiltRulesLoading } = + useGetMigrationPrebuiltRules(migrationId); + const { data: { ruleMigrations, total } = { ruleMigrations: [], total: 0 }, isLoading: isDataLoading, @@ -129,6 +133,7 @@ export const MigrationRulesTable: React.FC = React.mem migrationRuleDetailsFlyout: rulePreviewFlyout, openMigrationRuleDetails: openRulePreview, } = useMigrationRuleDetailsFlyout({ + prebuiltRules, ruleActionsFactory, }); @@ -138,6 +143,8 @@ export const MigrationRulesTable: React.FC = React.mem installMigrationRule: installSingleRule, }); + const isLoading = isStatsLoading || isPrebuiltRulesLoading || isDataLoading || isTableLoading; + return ( <> = React.mem ; ruleActionsFactory: (ruleMigration: RuleMigration, closeRulePreview: () => void) => ReactNode; extraTabsFactory?: (ruleMigration: RuleMigration) => EuiTabbedContentTab[]; } @@ -23,10 +28,12 @@ interface UseMigrationRuleDetailsFlyoutResult { } export function useMigrationRuleDetailsFlyout({ + prebuiltRules, extraTabsFactory, ruleActionsFactory, }: UseMigrationRuleDetailsFlyoutParams): UseMigrationRuleDetailsFlyoutResult { const [ruleMigration, setMigrationRuleForPreview] = useState(); + const [matchedPrebuiltRule, setMatchedPrebuiltRule] = useState(); const closeMigrationRuleDetails = useCallback(() => setMigrationRuleForPreview(undefined), []); const ruleActions = useMemo( () => ruleMigration && ruleActionsFactory(ruleMigration, closeMigrationRuleDetails), @@ -37,19 +44,33 @@ export function useMigrationRuleDetailsFlyout({ [ruleMigration, extraTabsFactory] ); + const openMigrationRuleDetails = useCallback( + (rule: RuleMigration) => { + setMigrationRuleForPreview(rule); + + // Find matched prebuilt rule if any and prioritize its installed version + const matchedPrebuiltRuleVersion = rule.elastic_rule?.prebuilt_rule_id + ? prebuiltRules[rule.elastic_rule.prebuilt_rule_id] + : undefined; + const prebuiltRule = + matchedPrebuiltRuleVersion?.current ?? matchedPrebuiltRuleVersion?.target; + setMatchedPrebuiltRule(prebuiltRule); + }, + [prebuiltRules] + ); + return { migrationRuleDetailsFlyout: ruleMigration && ( ), - openMigrationRuleDetails: useCallback((rule: RuleMigration) => { - setMigrationRuleForPreview(rule); - }, []), + openMigrationRuleDetails, closeMigrationRuleDetails, }; } diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/translations.ts index 3b13daa8f0682..3f92da4e8ddcc 100644 --- a/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/translations.ts +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/translations.ts @@ -7,6 +7,13 @@ import { i18n } from '@kbn/i18n'; +export const GET_MIGRATION_PREBUILT_RULES_FAILURE = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.getMigrationPrebuiltRulesFailDescription', + { + defaultMessage: 'Failed to fetch prebuilt rules', + } +); + export const GET_MIGRATION_RULES_FAILURE = i18n.translate( 'xpack.securitySolution.siemMigrations.rules.getMigrationRulesFailDescription', { diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/use_get_migration_prebuilt_rules.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/use_get_migration_prebuilt_rules.ts new file mode 100644 index 0000000000000..a855d53555551 --- /dev/null +++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/logic/use_get_migration_prebuilt_rules.ts @@ -0,0 +1,39 @@ +/* + * 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 { replaceParams } from '@kbn/openapi-common/shared'; +import { useQuery } from '@tanstack/react-query'; +import { useAppToasts } from '../../../common/hooks/use_app_toasts'; +import type { GetRuleMigrationPrebuiltRulesResponse } from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; +import { SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH } from '../../../../common/siem_migrations/constants'; +import { getRuleMigrationsPrebuiltRules } from '../api'; +import { DEFAULT_QUERY_OPTIONS } from './constants'; +import * as i18n from './translations'; + +export const useGetMigrationPrebuiltRules = (migrationId: string) => { + const { addError } = useAppToasts(); + + const SPECIFIC_MIGRATIONS_PREBUILT_RULES_PATH = replaceParams( + SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, + { + migration_id: migrationId, + } + ); + + return useQuery( + ['GET', SPECIFIC_MIGRATIONS_PREBUILT_RULES_PATH], + async ({ signal }) => { + return getRuleMigrationsPrebuiltRules({ migrationId, signal }); + }, + { + ...DEFAULT_QUERY_OPTIONS, + onError: (error) => { + addError(error, { title: i18n.GET_MIGRATION_PREBUILT_RULES_FAILURE }); + }, + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/constants.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/constants.ts new file mode 100644 index 0000000000000..215f0089410e7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/constants.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +export const MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL = 50; +export const MAX_PREBUILT_RULES_TO_FETCH = 10_000 as const; +export const MAX_TRANSLATED_RULES_TO_INSTALL = 10_000 as const; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts new file mode 100644 index 0000000000000..551e4a51e477e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts @@ -0,0 +1,73 @@ +/* + * 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 type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; +import type { GetRuleMigrationPrebuiltRulesResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; +import { GetRuleMigrationPrebuiltRulesRequestParams } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; +import { SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH } from '../../../../../common/siem_migrations/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { withLicense } from './util/with_license'; +import { getPrebuiltRules, getUniquePrebuiltRuleIds } from './util/prebuilt_rules'; +import { MAX_PREBUILT_RULES_TO_FETCH } from './constants'; + +export const registerSiemRuleMigrationsPrebuiltRulesRoute = ( + router: SecuritySolutionPluginRouter, + logger: Logger +) => { + router.versioned + .get({ + path: SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, + access: 'internal', + security: { authz: { requiredPrivileges: ['securitySolution'] } }, + }) + .addVersion( + { + version: '1', + validate: { + request: { + params: buildRouteValidationWithZod(GetRuleMigrationPrebuiltRulesRequestParams), + }, + }, + }, + withLicense( + async ( + context, + req, + res + ): Promise> => { + const { migration_id: migrationId } = req.params; + try { + const ctx = await context.resolve(['core', 'alerting', 'securitySolution']); + const ruleMigrationsClient = ctx.securitySolution.getSiemRuleMigrationsClient(); + const savedObjectsClient = ctx.core.savedObjects.client; + const rulesClient = await ctx.alerting.getRulesClient(); + + const result = await ruleMigrationsClient.data.rules.get(migrationId, { + filters: { + prebuilt: true, + }, + from: 0, + size: MAX_PREBUILT_RULES_TO_FETCH, + }); + + const prebuiltRulesIds = getUniquePrebuiltRuleIds(result.data); + const prebuiltRules = await getPrebuiltRules( + rulesClient, + savedObjectsClient, + prebuiltRulesIds + ); + + return res.ok({ body: prebuiltRules }); + } catch (err) { + logger.error(err); + return res.badRequest({ body: err.message }); + } + } + ) + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts index c6f3c51a1bb53..a327d4b28a9bd 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts @@ -20,6 +20,7 @@ import { registerSiemRuleMigrationsResourceGetRoute } from './resources/get'; import { registerSiemRuleMigrationsRetryRoute } from './retry'; import { registerSiemRuleMigrationsInstallRoute } from './install'; import { registerSiemRuleMigrationsInstallTranslatedRoute } from './install_translated'; +import { registerSiemRuleMigrationsPrebuiltRulesRoute } from './get_prebuilt_rules'; export const registerSiemRuleMigrationsRoutes = ( router: SecuritySolutionPluginRouter, @@ -28,6 +29,7 @@ export const registerSiemRuleMigrationsRoutes = ( registerSiemRuleMigrationsCreateRoute(router, logger); registerSiemRuleMigrationsUpdateRoute(router, logger); registerSiemRuleMigrationsStatsAllRoute(router, logger); + registerSiemRuleMigrationsPrebuiltRulesRoute(router, logger); registerSiemRuleMigrationsGetRoute(router, logger); registerSiemRuleMigrationsStartRoute(router, logger); registerSiemRuleMigrationsRetryRoute(router, logger); diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts index 2fce95be9dafe..d74619e4c1251 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts @@ -7,22 +7,23 @@ import type { Logger, SavedObjectsClientContract } from '@kbn/core/server'; import type { RulesClient } from '@kbn/alerting-plugin/server'; -import { - DEFAULT_TRANSLATION_RISK_SCORE, - DEFAULT_TRANSLATION_SEVERITY, -} from '../../../../../../common/siem_migrations/constants'; +import { initPromisePool } from '../../../../../utils/promise_pool'; import type { SecuritySolutionApiRequestHandlerContext } from '../../../../..'; -import { createPrebuiltRuleObjectsClient } from '../../../../detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client'; import { performTimelinesInstallation } from '../../../../detection_engine/prebuilt_rules/logic/perform_timelines_installation'; import { createPrebuiltRules } from '../../../../detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules'; -import type { PrebuiltRuleAsset } from '../../../../detection_engine/prebuilt_rules'; -import { getRuleGroups } from '../../../../detection_engine/prebuilt_rules/model/rule_groups/get_rule_groups'; -import { fetchRuleVersionsTriad } from '../../../../detection_engine/prebuilt_rules/logic/rule_versions/fetch_rule_versions_triad'; -import { createPrebuiltRuleAssetsClient } from '../../../../detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; import type { IDetectionRulesClient } from '../../../../detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface'; -import type { RuleCreateProps } from '../../../../../../common/api/detection_engine'; +import type { RuleResponse } from '../../../../../../common/api/detection_engine'; import type { UpdateRuleMigrationInput } from '../../data/rule_migrations_data_rules_client'; import type { StoredRuleMigration } from '../../types'; +import { getPrebuiltRules, getUniquePrebuiltRuleIds } from './prebuilt_rules'; +import { + MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL, + MAX_TRANSLATED_RULES_TO_INSTALL, +} from '../constants'; +import { + convertMigrationCustomRuleToSecurityRulePayload, + isMigrationCustomRule, +} from '../../../../../../common/siem_migrations/rules/utils'; const installPrebuiltRules = async ( rulesToInstall: StoredRuleMigration[], @@ -31,105 +32,90 @@ const installPrebuiltRules = async ( savedObjectsClient: SavedObjectsClientContract, detectionRulesClient: IDetectionRulesClient ): Promise => { - const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); - const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); - const ruleVersionsMap = await fetchRuleVersionsTriad({ - ruleAssetsClient, - ruleObjectsClient, - }); - const { currentRules, installableRules } = getRuleGroups(ruleVersionsMap); - - const rulesToUpdate: UpdateRuleMigrationInput[] = []; - const assetsToInstall: PrebuiltRuleAsset[] = []; - rulesToInstall.forEach((ruleToInstall) => { - // If prebuilt rule has already been installed, then just update migration rule with the installed rule id - const installedRule = currentRules.find( - (rule) => rule.rule_id === ruleToInstall.elastic_rule?.prebuilt_rule_id - ); - if (installedRule) { - rulesToUpdate.push({ - id: ruleToInstall.id, - elastic_rule: { - id: installedRule.id, - }, - }); - return; - } + // Get required prebuilt rules + const prebuiltRulesIds = getUniquePrebuiltRuleIds(rulesToInstall); + const prebuiltRules = await getPrebuiltRules(rulesClient, savedObjectsClient, prebuiltRulesIds); - // If prebuilt rule is not installed, then keep reference to install it - const installableRule = installableRules.find( - (rule) => rule.rule_id === ruleToInstall.elastic_rule?.prebuilt_rule_id - ); - if (installableRule) { - assetsToInstall.push(installableRule); + const { installed: alreadyInstalledRules, installable } = Object.values(prebuiltRules).reduce( + (acc, item) => { + if (item.current) { + acc.installed.push(item.current); + } else { + acc.installable.push(item.target); + } + return acc; + }, + { installed: [], installable: [] } as { + installed: RuleResponse[]; + installable: RuleResponse[]; } - }); - - // Filter out any duplicates which can occur when multiple translated rules matched the same prebuilt rule - const filteredAssetsToInstall = assetsToInstall.filter( - (value, index, self) => index === self.findIndex((rule) => rule.rule_id === value.rule_id) ); + // Install prebuilt rules // TODO: we need to do an error handling which can happen during the rule installation - const { results: installedRules } = await createPrebuiltRules( + const { results: newlyInstalledRules } = await createPrebuiltRules( detectionRulesClient, - filteredAssetsToInstall + installable ); await performTimelinesInstallation(securitySolutionContext); + const installedRules = [ + ...alreadyInstalledRules, + ...newlyInstalledRules.map((value) => value.result), + ]; + + // Create migration rules updates templates + const rulesToUpdate: UpdateRuleMigrationInput[] = []; installedRules.forEach((installedRule) => { - const rules = rulesToInstall.filter( - (rule) => rule.elastic_rule?.prebuilt_rule_id === installedRule.result.rule_id + const filteredRules = rulesToInstall.filter( + (rule) => rule.elastic_rule?.prebuilt_rule_id === installedRule.rule_id ); - rules.forEach((prebuiltRule) => { - rulesToUpdate.push({ - id: prebuiltRule.id, + rulesToUpdate.push( + ...filteredRules.map(({ id }) => ({ + id, elastic_rule: { - id: installedRule.result.id, + id: installedRule.id, }, - }); - }); + })) + ); }); return rulesToUpdate; }; -const installCustomRules = async ( +export const installCustomRules = async ( rulesToInstall: StoredRuleMigration[], detectionRulesClient: IDetectionRulesClient, logger: Logger ): Promise => { const rulesToUpdate: UpdateRuleMigrationInput[] = []; - await Promise.all( - rulesToInstall.map(async (rule) => { - if (!rule.elastic_rule?.query || !rule.elastic_rule?.description) { + const createCustomRulesOutcome = await initPromisePool({ + concurrency: MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL, + items: rulesToInstall, + executor: async (rule) => { + if (!isMigrationCustomRule(rule.elastic_rule)) { return; } - try { - const payloadRule: RuleCreateProps = { - type: 'esql', - language: 'esql', - query: rule.elastic_rule.query, - name: rule.elastic_rule.title, - description: rule.elastic_rule.description, - severity: DEFAULT_TRANSLATION_SEVERITY, - risk_score: DEFAULT_TRANSLATION_RISK_SCORE, - }; - const createdRule = await detectionRulesClient.createCustomRule({ - params: payloadRule, - }); - rulesToUpdate.push({ - id: rule.id, - elastic_rule: { - id: createdRule.id, - }, - }); - } catch (err) { - // TODO: we need to do an error handling which can happen during the rule creation - logger.debug(`Could not create a rule because of error: ${JSON.stringify(err)}`); - } - }) - ); + const payloadRule = convertMigrationCustomRuleToSecurityRulePayload(rule.elastic_rule); + const createdRule = await detectionRulesClient.createPrebuiltRule({ + params: payloadRule, + }); + rulesToUpdate.push({ + id: rule.id, + elastic_rule: { + id: createdRule.id, + }, + }); + }, + }); + if (createCustomRulesOutcome.errors) { + // TODO: we need to do an error handling which can happen during the rule creation + logger.debug( + `Failed to create some of the rules because of errors: ${JSON.stringify( + createCustomRulesOutcome.errors + )}` + ); + } return rulesToUpdate; }; @@ -179,6 +165,8 @@ export const installTranslated = async ({ const { data: rulesToInstall } = await ruleMigrationsClient.data.rules.get(migrationId, { filters: { ids, installable: true }, + from: 0, + size: MAX_TRANSLATED_RULES_TO_INSTALL, }); const { customRulesToInstall, prebuiltRulesToInstall } = rulesToInstall.reduce( diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts new file mode 100644 index 0000000000000..7760612abc878 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts @@ -0,0 +1,84 @@ +/* + * 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 type { SavedObjectsClientContract } from '@kbn/core/server'; +import type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { RuleResponse } from '../../../../../../common/api/detection_engine'; +import { createPrebuiltRuleObjectsClient } from '../../../../detection_engine/prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client'; +import { fetchRuleVersionsTriad } from '../../../../detection_engine/prebuilt_rules/logic/rule_versions/fetch_rule_versions_triad'; +import { createPrebuiltRuleAssetsClient } from '../../../../detection_engine/prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client'; +import { convertPrebuiltRuleAssetToRuleResponse } from '../../../../detection_engine/rule_management/logic/detection_rules_client/converters/convert_prebuilt_rule_asset_to_rule_response'; +import type { RuleMigration } from '../../../../../../common/siem_migrations/model/rule_migration.gen'; + +export const getUniquePrebuiltRuleIds = (migrationRules: RuleMigration[]): string[] => { + const rulesIds = new Set(); + migrationRules.forEach((rule) => { + if (rule.elastic_rule?.prebuilt_rule_id) { + rulesIds.add(rule.elastic_rule.prebuilt_rule_id); + } + }); + return Array.from(rulesIds); +}; + +export interface PrebuiltRulesResults { + /** + * The latest available version + */ + target: RuleResponse; + + /** + * The currently installed version + */ + current?: RuleResponse; +} + +/** + * Gets Elastic prebuilt rules + * @param rulesClient The rules client to fetch prebuilt rules + * @param savedObjectsClient The saved objects client + * @param rulesIds The list of IDs to filter requested prebuilt rules. If not specified, all available prebuilt rules will be returned. + * @returns + */ +export const getPrebuiltRules = async ( + rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract, + rulesIds?: string[] +): Promise> => { + const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); + const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); + + const prebuiltRulesMap = await fetchRuleVersionsTriad({ + ruleAssetsClient, + ruleObjectsClient, + }); + + // Filter out prebuilt rules by `rule_id` + let filteredPrebuiltRulesMap: typeof prebuiltRulesMap; + if (rulesIds) { + filteredPrebuiltRulesMap = new Map(); + for (const ruleId of rulesIds) { + const prebuiltRule = prebuiltRulesMap.get(ruleId); + if (prebuiltRule) { + filteredPrebuiltRulesMap.set(ruleId, prebuiltRule); + } + } + } else { + filteredPrebuiltRulesMap = prebuiltRulesMap; + } + + const prebuiltRules: Record = {}; + filteredPrebuiltRulesMap.forEach((ruleVersions, ruleId) => { + if (ruleVersions.target) { + prebuiltRules[ruleId] = { + target: convertPrebuiltRuleAssetToRuleResponse(ruleVersions.target), + current: ruleVersions.current, + }; + } + }); + + return prebuiltRules; +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts index 716d19ce16cdf..f11b24e50b95a 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts @@ -42,6 +42,7 @@ export interface RuleMigrationFilters { status?: SiemMigrationStatus | SiemMigrationStatus[]; ids?: string[]; installable?: boolean; + prebuilt?: boolean; searchTerm?: string; } export interface RuleMigrationGetOptions { @@ -54,7 +55,6 @@ export interface RuleMigrationGetOptions { * The 500 number was chosen as a reasonable number to avoid large payloads. It can be adjusted if needed. */ const BULK_MAX_SIZE = 500 as const; -/* The default number of rule migrations to retrieve in a single GET request. */ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient { /** Indexes an array of rule migrations to be processed */ @@ -337,7 +337,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient private getFilterQuery( migrationId: string, - { status, ids, installable, searchTerm }: RuleMigrationFilters = {} + { status, ids, installable, prebuilt, searchTerm }: RuleMigrationFilters = {} ): QueryDslQueryContainer { const filter: QueryDslQueryContainer[] = [{ term: { migration_id: migrationId } }]; if (status) { @@ -353,6 +353,9 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient if (installable) { filter.push(...conditions.isInstallable()); } + if (prebuilt) { + filter.push(conditions.isPrebuilt()); + } if (searchTerm?.length) { filter.push(conditions.matchTitle(searchTerm)); } diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index 98fefc3a74aa4..a6d0ac86a810c 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -99,6 +99,7 @@ import { GetRuleMigrationRequestQueryInput, GetRuleMigrationRequestParamsInput, } from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen'; +import { GetRuleMigrationPrebuiltRulesRequestParamsInput } from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen'; import { GetRuleMigrationResourcesRequestQueryInput, GetRuleMigrationResourcesRequestParamsInput, @@ -957,6 +958,27 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Retrieves all available prebuilt rules (installed and installable) + */ + getRuleMigrationPrebuiltRules( + props: GetRuleMigrationPrebuiltRulesProps, + kibanaSpace: string = 'default' + ) { + return supertest + .get( + routeWithNamespace( + replaceParams( + '/internal/siem_migrations/rules/{migration_id}/prebuilt_rules', + props.params + ), + kibanaSpace + ) + ) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Retrieves resources for an existing SIEM rules migration */ @@ -1731,6 +1753,9 @@ export interface GetRuleMigrationProps { query: GetRuleMigrationRequestQueryInput; params: GetRuleMigrationRequestParamsInput; } +export interface GetRuleMigrationPrebuiltRulesProps { + params: GetRuleMigrationPrebuiltRulesRequestParamsInput; +} export interface GetRuleMigrationResourcesProps { query: GetRuleMigrationResourcesRequestQueryInput; params: GetRuleMigrationResourcesRequestParamsInput;