diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts index 88ed777c21d69..e94fc28041ab2 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/constants.ts @@ -25,6 +25,8 @@ 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_MIGRATIONS_INTEGRATIONS_PATH = + `${SIEM_RULE_MIGRATION_PATH}/integrations` as const; export const SIEM_RULE_MIGRATION_RESOURCES_PATH = `${SIEM_RULE_MIGRATION_PATH}/resources` as const; export const SIEM_RULE_MIGRATION_RESOURCES_MISSING_PATH = diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts index 47c06e1e02c7a..f00f5cf17cd44 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.gen.ts @@ -30,6 +30,7 @@ import { } from '../../rule_migration.gen'; import { NonEmptyString } from '../../../../api/model/primitives.gen'; import { ConnectorId, LangSmithOptions } from '../../common.gen'; +import { RelatedIntegration } from '../../../../api/detection_engine/model/rule_schema/common_attributes.gen'; export type CreateRuleMigrationRequestParams = z.infer; export const CreateRuleMigrationRequestParams = z.object({ @@ -79,6 +80,24 @@ export const GetRuleMigrationResponse = z.object({ data: z.array(RuleMigration), }); +export type GetRuleMigrationIntegrationsRequestParams = z.infer< + typeof GetRuleMigrationIntegrationsRequestParams +>; +export const GetRuleMigrationIntegrationsRequestParams = z.object({ + migration_id: NonEmptyString, +}); +export type GetRuleMigrationIntegrationsRequestParamsInput = z.input< + typeof GetRuleMigrationIntegrationsRequestParams +>; + +/** + * The map of related integrations, with the integration id as a key + */ +export type GetRuleMigrationIntegrationsResponse = z.infer< + typeof GetRuleMigrationIntegrationsResponse +>; +export const GetRuleMigrationIntegrationsResponse = z.object({}).catchall(RelatedIntegration); + export type GetRuleMigrationPrebuiltRulesRequestParams = z.infer< typeof GetRuleMigrationPrebuiltRulesRequestParams >; diff --git a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml index 69e43b57dabd3..f1af3290c76e1 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/siem_migrations/model/api/rules/rule_migration.schema.yaml @@ -396,6 +396,33 @@ paths: additionalProperties: $ref: '../../rule_migration.schema.yaml#/components/schemas/PrebuiltRuleVersion' + /internal/siem_migrations/rules/{migration_id}/integrations: + get: + summary: Retrieves all related integrations for a specific migration + operationId: GetRuleMigrationIntegrations + x-codegen-enabled: true + x-internal: true + description: Retrieves all related integrations + tags: + - SIEM Rule Migrations + parameters: + - name: migration_id + in: path + required: true + schema: + description: The migration id to retrieve related integrations for + $ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString' + responses: + 200: + description: Indicates that related integrations have been retrieved correctly. + content: + application/json: + schema: + type: object + description: The map of related integrations, with the integration id as a key + additionalProperties: + $ref: '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml#/components/schemas/RelatedIntegration' + # Rule migration resources APIs /internal/siem_migrations/rules/{migration_id}/resources: diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/api/index.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/api/index.ts index 02fb423b05279..8681244a301ec 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/api/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/api/index.ts @@ -23,6 +23,7 @@ import { SIEM_RULE_MIGRATION_RESOURCES_MISSING_PATH, SIEM_RULE_MIGRATION_RESOURCES_PATH, SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, + SIEM_RULE_MIGRATIONS_INTEGRATIONS_PATH, } from '../../../../common/siem_migrations/constants'; import type { CreateRuleMigrationRequestBody, @@ -39,6 +40,7 @@ import type { UpsertRuleMigrationResourcesResponse, GetRuleMigrationPrebuiltRulesResponse, UpdateRuleMigrationResponse, + GetRuleMigrationIntegrationsResponse, } from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; export interface GetRuleMigrationStatsParams { @@ -279,6 +281,23 @@ export const getRuleMigrationsPrebuiltRules = async ({ ); }; +export interface GetRelatedIntegrationsParams { + /** `id` of the migration to get related integrations for */ + migrationId: string; + /** Optional AbortSignal for cancelling request */ + signal?: AbortSignal; +} +/** Retrieves related integrations for a specific migration. */ +export const getRelatedIntegrations = async ({ + migrationId, + signal, +}: GetRelatedIntegrationsParams): Promise => { + return KibanaServices.get().http.get( + replaceParams(SIEM_RULE_MIGRATIONS_INTEGRATIONS_PATH, { migration_id: migrationId }), + { version: '1', signal } + ); +}; + export interface UpdateRulesParams { /** The list of migration rules data to update */ rulesToUpdate: UpdateRuleMigrationData[]; 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 b883934a0bdcb..f54dba5ca6b82 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 @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; +import type { RelatedIntegration, RuleResponse } from '../../../../../common/api/detection_engine'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen'; import { EmptyMigration } from './empty_migration'; @@ -33,6 +34,7 @@ import { BulkActions } from './bulk_actions'; import { SearchField } from './search_field'; import { RuleTranslationResult } from '../../../../../common/siem_migrations/constants'; import * as i18n from './translations'; +import { useGetRelatedIntegrations } from '../../service/hooks/use_get_integrations'; const DEFAULT_PAGE_SIZE = 10; const DEFAULT_SORT_FIELD = 'translation_result'; @@ -64,6 +66,9 @@ export const MigrationRulesTable: React.FC = React.mem const { data: prebuiltRules = {}, isLoading: isPrebuiltRulesLoading } = useGetMigrationPrebuiltRules(migrationId); + const { integrations, isLoading: isIntegrationsLoading } = + useGetRelatedIntegrations(migrationId); + const { data: { ruleMigrations, total } = { ruleMigrations: [], total: 0 }, isLoading: isDataLoading, @@ -180,7 +185,12 @@ export const MigrationRulesTable: React.FC = React.mem [addError, installTranslatedMigrationRules] ); - const isLoading = isStatsLoading || isPrebuiltRulesLoading || isDataLoading || isTableLoading; + const isLoading = + isStatsLoading || + isPrebuiltRulesLoading || + isIntegrationsLoading || + isDataLoading || + isTableLoading; const ruleActionsFactory = useCallback( (ruleMigration: RuleMigration, closeRulePreview: () => void) => { @@ -221,13 +231,33 @@ export const MigrationRulesTable: React.FC = React.mem [installSingleRule, isLoading] ); - const getMigrationRule = useCallback( + const getMigrationRuleData = useCallback( (ruleId: string) => { - if (!isLoading && ruleMigrations.length) { - return ruleMigrations.find((item) => item.id === ruleId); + if (!isLoading && ruleMigrations.length && integrations) { + const ruleMigration = ruleMigrations.find((item) => item.id === ruleId); + let matchedPrebuiltRule: RuleResponse | undefined; + const relatedIntegrations: RelatedIntegration[] = []; + if (ruleMigration) { + // Find matched prebuilt rule if any and prioritize its installed version + const matchedPrebuiltRuleVersion = ruleMigration.elastic_rule?.prebuilt_rule_id + ? prebuiltRules[ruleMigration.elastic_rule.prebuilt_rule_id] + : undefined; + matchedPrebuiltRule = + matchedPrebuiltRuleVersion?.current ?? matchedPrebuiltRuleVersion?.target; + + if (matchedPrebuiltRule?.related_integrations) { + relatedIntegrations.push(...matchedPrebuiltRule.related_integrations); + } else if (ruleMigration.elastic_rule?.integration_id) { + const integration = integrations[ruleMigration.elastic_rule.integration_id]; + if (integration) { + relatedIntegrations.push(integration); + } + } + } + return { ruleMigration, matchedPrebuiltRule, relatedIntegrations }; } }, - [isLoading, ruleMigrations] + [integrations, isLoading, prebuiltRules, ruleMigrations] ); const { @@ -235,8 +265,7 @@ export const MigrationRulesTable: React.FC = React.mem openMigrationRuleDetails: openRulePreview, } = useMigrationRuleDetailsFlyout({ isLoading, - prebuiltRules, - getMigrationRule, + getMigrationRuleData, ruleActionsFactory, }); @@ -244,6 +273,7 @@ export const MigrationRulesTable: React.FC = React.mem disableActions: isTableLoading, openMigrationRuleDetails: openRulePreview, installMigrationRule: installSingleRule, + getMigrationRuleData, }); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/author.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/author.tsx index 23980f5612f89..cd762db00e2c7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/author.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/author.tsx @@ -34,6 +34,7 @@ export const createAuthorColumn = (): TableColumn => { return ; }, sortable: true, + truncateText: true, width: '10%', }; }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/integrations.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/integrations.tsx new file mode 100644 index 0000000000000..ae7a282683dae --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/integrations.tsx @@ -0,0 +1,37 @@ +/* + * 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 from 'react'; +import type { RelatedIntegration } from '../../../../../common/api/detection_engine'; +import { IntegrationsPopover } from '../../../../detections/components/rules/related_integrations/integrations_popover'; +import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen'; +import * as i18n from './translations'; +import type { TableColumn } from './constants'; + +export const createIntegrationsColumn = ({ + getMigrationRuleData, +}: { + getMigrationRuleData: ( + ruleId: string + ) => { relatedIntegrations?: RelatedIntegration[] } | undefined; +}): TableColumn => { + return { + field: 'elastic_rule.integration_id', + name: i18n.COLUMN_INTEGRATIONS, + render: (_, rule: RuleMigration) => { + const migrationRuleData = getMigrationRuleData(rule.id); + const relatedIntegrations = migrationRuleData?.relatedIntegrations; + if (relatedIntegrations == null || relatedIntegrations.length === 0) { + return null; + } + return ; + }, + truncateText: true, + width: '143px', + align: 'center', + }; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/translations.ts index 64e459a609143..5b4fd6d6a477e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/translations.ts @@ -97,3 +97,10 @@ export const COLUMN_UPDATED = i18n.translate( defaultMessage: 'Updated', } ); + +export const COLUMN_INTEGRATIONS = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.tableColumn.integrationsLabel', + { + defaultMessage: 'Integrations', + } +); diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/updated.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/updated.tsx index cec9f86eb7bde..aaf4e75ac4917 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/updated.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/components/rules_table_columns/updated.tsx @@ -19,7 +19,7 @@ export const createUpdatedColumn = (): TableColumn => { ), sortable: true, - truncateText: false, + truncateText: true, align: 'center', width: '10%', }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/hooks/use_migration_rule_preview_flyout.tsx b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/hooks/use_migration_rule_preview_flyout.tsx index 4efaa4aba7181..9dad5a30ab073 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/hooks/use_migration_rule_preview_flyout.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/hooks/use_migration_rule_preview_flyout.tsx @@ -8,16 +8,18 @@ import type { ReactNode } from 'react'; import React, { useCallback, useState, useMemo } from 'react'; import type { EuiTabbedContentTab } from '@elastic/eui'; -import type { - PrebuiltRuleVersion, - RuleMigration, -} from '../../../../common/siem_migrations/model/rule_migration.gen'; +import type { RuleResponse } from '../../../../common/api/detection_engine'; +import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen'; import { MigrationRuleDetailsFlyout } from '../components/rule_details_flyout'; interface UseMigrationRuleDetailsFlyoutParams { isLoading?: boolean; - prebuiltRules: Record; - getMigrationRule: (ruleId: string) => RuleMigration | undefined; + getMigrationRuleData: (ruleId: string) => + | { + ruleMigration?: RuleMigration; + matchedPrebuiltRule?: RuleResponse; + } + | undefined; ruleActionsFactory: (ruleMigration: RuleMigration, closeRulePreview: () => void) => ReactNode; extraTabsFactory?: (ruleMigration: RuleMigration) => EuiTabbedContentTab[]; } @@ -30,27 +32,17 @@ interface UseMigrationRuleDetailsFlyoutResult { export function useMigrationRuleDetailsFlyout({ isLoading, - prebuiltRules, - getMigrationRule, + getMigrationRuleData, extraTabsFactory, ruleActionsFactory, }: UseMigrationRuleDetailsFlyoutParams): UseMigrationRuleDetailsFlyoutResult { const [migrationRuleId, setMigrationRuleId] = useState(); - const ruleMigration = useMemo(() => { + const migrationRuleData = useMemo(() => { if (migrationRuleId) { - return getMigrationRule(migrationRuleId); + return getMigrationRuleData(migrationRuleId); } - }, [getMigrationRule, migrationRuleId]); - const matchedPrebuiltRule = useMemo(() => { - if (ruleMigration) { - // Find matched prebuilt rule if any and prioritize its installed version - const matchedPrebuiltRuleVersion = ruleMigration.elastic_rule?.prebuilt_rule_id - ? prebuiltRules[ruleMigration.elastic_rule.prebuilt_rule_id] - : undefined; - return matchedPrebuiltRuleVersion?.current ?? matchedPrebuiltRuleVersion?.target; - } - }, [prebuiltRules, ruleMigration]); + }, [getMigrationRuleData, migrationRuleId]); const openMigrationRuleDetails = useCallback((rule: RuleMigration) => { setMigrationRuleId(rule.id); @@ -58,19 +50,24 @@ export function useMigrationRuleDetailsFlyout({ const closeMigrationRuleDetails = useCallback(() => setMigrationRuleId(undefined), []); const ruleActions = useMemo( - () => ruleMigration && ruleActionsFactory(ruleMigration, closeMigrationRuleDetails), - [ruleMigration, ruleActionsFactory, closeMigrationRuleDetails] + () => + migrationRuleData?.ruleMigration && + ruleActionsFactory(migrationRuleData.ruleMigration, closeMigrationRuleDetails), + [migrationRuleData?.ruleMigration, ruleActionsFactory, closeMigrationRuleDetails] ); const extraTabs = useMemo( - () => (ruleMigration && extraTabsFactory ? extraTabsFactory(ruleMigration) : []), - [ruleMigration, extraTabsFactory] + () => + migrationRuleData?.ruleMigration && extraTabsFactory + ? extraTabsFactory(migrationRuleData.ruleMigration) + : [], + [extraTabsFactory, migrationRuleData?.ruleMigration] ); return { - migrationRuleDetailsFlyout: ruleMigration && ( + migrationRuleDetailsFlyout: migrationRuleData?.ruleMigration && ( void; installMigrationRule: (migrationRule: RuleMigration, enable?: boolean) => void; + getMigrationRuleData: ( + ruleId: string + ) => { relatedIntegrations?: RelatedIntegration[] } | undefined; }): TableColumn[] => { return useMemo( () => [ @@ -35,12 +41,13 @@ export const useMigrationRulesTableColumns = ({ createRiskScoreColumn(), createSeverityColumn(), createAuthorColumn(), + createIntegrationsColumn({ getMigrationRuleData }), createActionsColumn({ disableActions, openMigrationRuleDetails, installMigrationRule, }), ], - [disableActions, installMigrationRule, openMigrationRuleDetails] + [disableActions, getMigrationRuleData, installMigrationRule, openMigrationRuleDetails] ); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_get_integrations.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_get_integrations.ts new file mode 100644 index 0000000000000..081ed821ed01a --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/hooks/use_get_integrations.ts @@ -0,0 +1,48 @@ +/* + * 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 { useCallback, useEffect, useReducer, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { RelatedIntegration } from '../../../../../common/api/detection_engine'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; +import { reducer, initialState } from './common/api_request_reducer'; + +export const GET_RELATED_INTEGRATIONS_ERROR = i18n.translate( + 'xpack.securitySolution.siemMigrations.rules.service.getRelatedIntegrationsError', + { defaultMessage: 'Failed to fetch related integrations' } +); + +export const useGetRelatedIntegrations = (migrationId: string) => { + const { siemMigrations, notifications } = useKibana().services; + const [state, dispatch] = useReducer(reducer, initialState); + const [integrations, setIntegrations] = useState< + Record | undefined + >(); + + const getRelatedIntegrations = useCallback(() => { + (async () => { + try { + dispatch({ type: 'start' }); + const results = await siemMigrations.rules.getRelatedIntegrations(migrationId); + + setIntegrations(results); + dispatch({ type: 'success' }); + } catch (err) { + setIntegrations(undefined); + const apiError = err.body ?? err; + notifications.toasts.addError(apiError, { title: GET_RELATED_INTEGRATIONS_ERROR }); + dispatch({ type: 'error', error: apiError }); + } + })(); + }, [siemMigrations.rules, migrationId, notifications.toasts]); + + useEffect(() => { + getRelatedIntegrations(); + }, [getRelatedIntegrations]); + + return { isLoading: state.loading, error: state.error, integrations }; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts index 83ead556b09cc..83c2ee69e4b86 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/siem_migrations/rules/service/rule_migrations_service.ts @@ -12,6 +12,7 @@ import { DEFAULT_ASSISTANT_NAMESPACE, TRACE_OPTIONS_SESSION_STORAGE_KEY, } from '@kbn/elastic-assistant/impl/assistant_context/constants'; +import type { RelatedIntegration } from '../../../../common/api/detection_engine'; import type { LangSmithOptions } from '../../../../common/siem_migrations/model/common.gen'; import type { RuleMigrationResourceData, @@ -35,6 +36,7 @@ import { type GetRuleMigrationsStatsAllParams, getMissingResources, upsertMigrationResources, + getRelatedIntegrations, } from '../api'; import type { RuleMigrationStats } from '../types'; import { getSuccessToast } from './success_notification'; @@ -181,6 +183,12 @@ export class SiemRulesMigrationsService { }); } + public async getRelatedIntegrations( + migrationId: string + ): Promise> { + return getRelatedIntegrations({ migrationId }); + } + private async startTaskStatsPolling(): Promise { let pendingMigrationIds: string[] = []; do { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_integrations.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_integrations.ts new file mode 100644 index 0000000000000..01f97e2ce8caf --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_integrations.ts @@ -0,0 +1,97 @@ +/* + * 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 { RelatedIntegration } from '../../../../../common/api/detection_engine'; +import { + GetRuleMigrationIntegrationsRequestParams, + type GetRuleMigrationIntegrationsResponse, +} from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen'; +import { SIEM_RULE_MIGRATIONS_INTEGRATIONS_PATH } from '../../../../../common/siem_migrations/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { withLicense } from './util/with_license'; +import { getPrebuiltRulesForMigration } from './util/prebuilt_rules'; + +export const registerSiemRuleMigrationsIntegrationsRoute = ( + router: SecuritySolutionPluginRouter, + logger: Logger +) => { + router.versioned + .get({ + path: SIEM_RULE_MIGRATIONS_INTEGRATIONS_PATH, + access: 'internal', + security: { authz: { requiredPrivileges: ['securitySolution'] } }, + }) + .addVersion( + { + version: '1', + validate: { + request: { + params: buildRouteValidationWithZod(GetRuleMigrationIntegrationsRequestParams), + }, + }, + }, + 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(); + + // Retrieve related integrations for migration rules translated into Elastic custom rules + const options = { filters: { custom: true } }; + const batches = ruleMigrationsClient.data.rules.searchBatches(migrationId, options); + + const integrationIdsSet = new Set(); + let results = await batches.next(); + while (results.length) { + results.forEach((rule) => { + if (rule.elastic_rule?.integration_id) { + integrationIdsSet.add(rule.elastic_rule.integration_id); + } + }); + results = await batches.next(); + } + + const relatedIntegrations: Record = {}; + const packages = await ruleMigrationsClient.data.integrations.getIntegrationPackages(); + packages?.forEach(({ id, version, integration }) => { + if (integrationIdsSet.has(id)) { + relatedIntegrations[id] = { package: id, version, integration }; + } + }); + + // Retrieve related integrations for migration rules matched with prebuilt rules + const prebuiltRules = await getPrebuiltRulesForMigration( + migrationId, + ruleMigrationsClient, + rulesClient, + savedObjectsClient + ); + Object.values(prebuiltRules).forEach((rule) => { + const integrations = (rule.current ?? rule.target).related_integrations; + integrations.forEach( + (integration) => (relatedIntegrations[integration.package] = integration) + ); + }); + + return res.ok({ body: relatedIntegrations }); + } catch (err) { + logger.error(err); + return res.badRequest({ body: err.message }); + } + } + ) + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts index 551e4a51e477e..8165b858e2a31 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/get_prebuilt_rules.ts @@ -12,8 +12,7 @@ import { GetRuleMigrationPrebuiltRulesRequestParams } from '../../../../../commo 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'; +import { getPrebuiltRulesForMigration } from './util/prebuilt_rules'; export const registerSiemRuleMigrationsPrebuiltRulesRoute = ( router: SecuritySolutionPluginRouter, @@ -47,19 +46,11 @@ export const registerSiemRuleMigrationsPrebuiltRulesRoute = ( 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( + const prebuiltRules = await getPrebuiltRulesForMigration( + migrationId, + ruleMigrationsClient, rulesClient, - savedObjectsClient, - prebuiltRulesIds + savedObjectsClient ); return res.ok({ body: prebuiltRules }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts index 241e59ac02a27..05d2c8f1a5dc4 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/index.ts @@ -22,6 +22,7 @@ import { registerSiemRuleMigrationsInstallRoute } from './install'; import { registerSiemRuleMigrationsInstallTranslatedRoute } from './install_translated'; import { registerSiemRuleMigrationsResourceGetMissingRoute } from './resources/missing'; import { registerSiemRuleMigrationsPrebuiltRulesRoute } from './get_prebuilt_rules'; +import { registerSiemRuleMigrationsIntegrationsRoute } from './get_integrations'; export const registerSiemRuleMigrationsRoutes = ( router: SecuritySolutionPluginRouter, @@ -39,6 +40,7 @@ export const registerSiemRuleMigrationsRoutes = ( registerSiemRuleMigrationsStopRoute(router, logger); registerSiemRuleMigrationsInstallRoute(router, logger); registerSiemRuleMigrationsInstallTranslatedRoute(router, logger); + registerSiemRuleMigrationsIntegrationsRoute(router, logger); registerSiemRuleMigrationsResourceUpsertRoute(router, logger); registerSiemRuleMigrationsResourceGetRoute(router, logger); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts index de95d818dd18d..aace5ed0ab356 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/installation.ts @@ -15,7 +15,7 @@ import { createPrebuiltRules } from '../../../../detection_engine/prebuilt_rules import type { IDetectionRulesClient } from '../../../../detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface'; import type { RuleResponse } from '../../../../../../common/api/detection_engine'; import type { StoredRuleMigration } from '../../types'; -import { getPrebuiltRules, getUniquePrebuiltRuleIds } from './prebuilt_rules'; +import { getPrebuiltRulesByIds, getUniquePrebuiltRuleIds } from './prebuilt_rules'; import { MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL, MAX_TRANSLATED_RULES_TO_INSTALL, @@ -35,7 +35,11 @@ const installPrebuiltRules = async ( ): Promise => { // Get required prebuilt rules const prebuiltRulesIds = getUniquePrebuiltRuleIds(rulesToInstall); - const prebuiltRules = await getPrebuiltRules(rulesClient, savedObjectsClient, prebuiltRulesIds); + const prebuiltRules = await getPrebuiltRulesByIds( + rulesClient, + savedObjectsClient, + prebuiltRulesIds + ); const { installed: alreadyInstalledRules, installable } = Object.values(prebuiltRules).reduce( (acc, item) => { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts index 7760612abc878..64d7aa8bb5f56 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/api/util/prebuilt_rules.ts @@ -13,6 +13,7 @@ import { fetchRuleVersionsTriad } from '../../../../detection_engine/prebuilt_ru 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'; +import type { SiemRuleMigrationsClient } from '../../siem_rule_migrations_service'; export const getUniquePrebuiltRuleIds = (migrationRules: RuleMigration[]): string[] => { const rulesIds = new Set(); @@ -43,7 +44,7 @@ export interface PrebuiltRulesResults { * @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 ( +export const getPrebuiltRulesByIds = async ( rulesClient: RulesClient, savedObjectsClient: SavedObjectsClientContract, rulesIds?: string[] @@ -82,3 +83,41 @@ export const getPrebuiltRules = async ( return prebuiltRules; }; + +/** + * Gets Elastic prebuilt rules + * @param migrationId The `id` of the migration to get related prebuilt rules for + * @param ruleMigrationsClient The rules migration client to migration rules data + * @param rulesClient The rules client to fetch prebuilt rules + * @param savedObjectsClient The saved objects client + * @returns + */ +export const getPrebuiltRulesForMigration = async ( + migrationId: string, + ruleMigrationsClient: SiemRuleMigrationsClient, + rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract +): Promise> => { + const options = { filters: { prebuilt: true } }; + const batches = ruleMigrationsClient.data.rules.searchBatches(migrationId, options); + + const rulesIds = new Set(); + let results = await batches.next(); + while (results.length) { + results.forEach((rule) => { + if (rule.elastic_rule?.prebuilt_rule_id) { + rulesIds.add(rule.elastic_rule.prebuilt_rule_id); + } + }); + results = await batches.next(); + } + const prebuiltRulesIds = Array.from(rulesIds); + + const prebuiltRules = await getPrebuiltRulesByIds( + rulesClient, + savedObjectsClient, + prebuiltRulesIds + ); + + return prebuiltRules; +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts index c06c889482360..e479c42cce273 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts @@ -6,6 +6,7 @@ */ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { PackageService } from '@kbn/fleet-plugin/server'; import { RuleMigrationsDataIntegrationsClient } from './rule_migrations_data_integrations_client'; import { RuleMigrationsDataPrebuiltRulesClient } from './rule_migrations_data_prebuilt_rules_client'; import { RuleMigrationsDataResourcesClient } from './rule_migrations_data_resources_client'; @@ -25,7 +26,8 @@ export class RuleMigrationsDataClient { indexNameProviders: IndexNameProviders, username: string, esClient: ElasticsearchClient, - logger: Logger + logger: Logger, + packageService?: PackageService ) { this.rules = new RuleMigrationsDataRulesClient( indexNameProviders.rules, @@ -43,7 +45,8 @@ export class RuleMigrationsDataClient { indexNameProviders.integrations, username, esClient, - logger + logger, + packageService ); this.prebuiltRules = new RuleMigrationsDataPrebuiltRulesClient( indexNameProviders.prebuiltrules, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts index fdb063836f9e4..947a206cd0c7a 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts @@ -5,11 +5,15 @@ * 2.0. */ +import type { PackageService } from '@kbn/fleet-plugin/server'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { PackageList } from '@kbn/fleet-plugin/common'; import type { Integration } from '../types'; import { RuleMigrationsDataBaseClient } from './rule_migrations_data_base_client'; /* This will be removed once the package registry changes is performed */ import integrationsFile from './integrations_temp.json'; +import type { IndexNameProvider } from './rule_migrations_data_client'; /* The minimum score required for a integration to be considered correct, might need to change this later */ const MIN_SCORE = 40 as const; @@ -22,6 +26,20 @@ const INTEGRATIONS = integrationsFile as Integration[]; * The 500 number was chosen as a reasonable number to avoid large payloads. It can be adjusted if needed. */ export class RuleMigrationsDataIntegrationsClient extends RuleMigrationsDataBaseClient { + constructor( + getIndexName: IndexNameProvider, + username: string, + esClient: ElasticsearchClient, + logger: Logger, + private packageService?: PackageService + ) { + super(getIndexName, username, esClient, logger); + } + + async getIntegrationPackages(): Promise { + return this.packageService?.asInternalUser.getPackages(); + } + /** Indexes an array of integrations to be used with ELSER semantic search queries */ async create(): Promise { const index = await this.getIndexName(); 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 47bcd56e6433e..8728929a75ba0 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 @@ -43,6 +43,7 @@ export interface RuleMigrationFilters { ids?: string[]; installable?: boolean; prebuilt?: boolean; + custom?: boolean; searchTerm?: string; } export interface RuleMigrationGetOptions { @@ -397,7 +398,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient private getFilterQuery( migrationId: string, - { status, ids, installable, prebuilt, searchTerm }: RuleMigrationFilters = {} + { status, ids, installable, prebuilt, custom, searchTerm }: RuleMigrationFilters = {} ): QueryDslQueryContainer { const filter: QueryDslQueryContainer[] = [{ term: { migration_id: migrationId } }]; if (status) { @@ -416,6 +417,9 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient if (prebuilt) { filter.push(searchConditions.isPrebuilt()); } + if (custom) { + filter.push(searchConditions.isCustom()); + } if (searchTerm?.length) { filter.push(searchConditions.matchTitle(searchTerm)); } diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts index 5799e5ab84c07..6681f0c3903b0 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts @@ -6,6 +6,7 @@ */ import type { AuthenticatedUser, ElasticsearchClient, Logger } from '@kbn/core/server'; import { IndexPatternAdapter, type FieldMap, type InstallParams } from '@kbn/index-adapter'; +import type { PackageService } from '@kbn/fleet-plugin/server'; import type { IndexNameProvider, IndexNameProviders } from './rule_migrations_data_client'; import { RuleMigrationsDataClient } from './rule_migrations_data_client'; import { @@ -24,6 +25,7 @@ interface CreateClientParams { spaceId: string; currentUser: AuthenticatedUser; esClient: ElasticsearchClient; + packageService?: PackageService; } export class RuleMigrationsDataService { @@ -58,7 +60,7 @@ export class RuleMigrationsDataService { ]); } - public createClient({ spaceId, currentUser, esClient }: CreateClientParams) { + public createClient({ spaceId, currentUser, esClient, packageService }: CreateClientParams) { const indexNameProviders: IndexNameProviders = { rules: this.createIndexNameProvider('rules', spaceId), resources: this.createIndexNameProvider('resources', spaceId), @@ -70,7 +72,8 @@ export class RuleMigrationsDataService { indexNameProviders, currentUser.username, esClient, - this.logger + this.logger, + packageService ); } diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts index 3bd8da066a45f..18196246da66d 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/search.ts @@ -28,6 +28,14 @@ export const conditions = { }, }; }, + isCustom(): QueryDslQueryContainer { + return { + nested: { + path: 'elastic_rule', + query: { bool: { must_not: { exists: { field: 'elastic_rule.prebuilt_rule_id' } } } }, + }, + }; + }, matchTitle(title: string): QueryDslQueryContainer { return { nested: { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/sort.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/sort.ts index 2d0ef644b8e56..6f3fcd46612d4 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/sort.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/sort.ts @@ -113,9 +113,21 @@ const sortingOptionsMap: { [key: string]: (direction?: estypes.SortOrder) => estypes.SortCombinations[]; } = { 'elastic_rule.title': sortingOptions.name, - 'elastic_rule.severity': sortingOptions.severity, - 'elastic_rule.prebuilt_rule_id': sortingOptions.matchedPrebuiltRule, - translation_result: sortingOptions.status, + 'elastic_rule.severity': (direction?: estypes.SortOrder) => [ + ...sortingOptions.severity(direction), + ...sortingOptions.status('desc'), + ...sortingOptions.matchedPrebuiltRule('desc'), + ], + 'elastic_rule.prebuilt_rule_id': (direction?: estypes.SortOrder) => [ + ...sortingOptions.matchedPrebuiltRule(direction), + ...sortingOptions.status('desc'), + ...sortingOptions.severity('desc'), + ], + translation_result: (direction?: estypes.SortOrder) => [ + ...sortingOptions.status(direction), + ...sortingOptions.matchedPrebuiltRule('desc'), + ...sortingOptions.severity('desc'), + ], updated_at: sortingOptions.updated, }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/siem_rule_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/siem_rule_migrations_service.ts index d9f4a1c5249cb..3be54a7e3d896 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/siem_rule_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/siem_rule_migrations_service.ts @@ -14,6 +14,7 @@ import type { KibanaRequest, Logger, } from '@kbn/core/server'; +import type { PackageService } from '@kbn/fleet-plugin/server'; import { RuleMigrationsDataService } from './data/rule_migrations_data_service'; import type { RuleMigrationsDataClient } from './data/rule_migrations_data_client'; import type { RuleMigrationsTaskClient } from './task/rule_migrations_task_client'; @@ -29,6 +30,7 @@ export interface SiemRuleMigrationsCreateClientParams { request: KibanaRequest; currentUser: AuthenticatedUser | null; spaceId: string; + packageService?: PackageService; } export interface SiemRuleMigrationsClient { @@ -60,13 +62,19 @@ export class SiemRuleMigrationsService { createClient({ spaceId, currentUser, + packageService, request, }: SiemRuleMigrationsCreateClientParams): SiemRuleMigrationsClient { assert(currentUser, 'Current user must be authenticated'); assert(this.esClusterClient, 'ES client not available, please call setup first'); const esClient = this.esClusterClient.asInternalUser; - const dataClient = this.dataService.createClient({ spaceId, currentUser, esClient }); + const dataClient = this.dataService.createClient({ + spaceId, + currentUser, + esClient, + packageService, + }); const taskClient = this.taskService.createClient({ currentUser, dataClient }); return { data: dataClient, task: taskClient }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/siem_migrations_service.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/siem_migrations_service.ts index 948ae89a39bb0..641838ff5105b 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/siem_migrations_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/siem_migrations_service.ts @@ -7,6 +7,7 @@ import type { LoggerFactory } from '@kbn/core/server'; import { ReplaySubject, type Subject } from 'rxjs'; +import type { PackageService } from '@kbn/fleet-plugin/server'; import type { ConfigType } from '../../config'; import { SiemRuleMigrationsService, @@ -18,6 +19,7 @@ import type { SiemMigrationsSetupParams } from './types'; export class SiemMigrationsService { private pluginStop$: Subject; private rules: SiemRuleMigrationsService; + private packageService?: PackageService; constructor(private config: ConfigType, logger: LoggerFactory, kibanaVersion: string) { this.pluginStop$ = new ReplaySubject(1); @@ -31,7 +33,11 @@ export class SiemMigrationsService { } createRulesClient(params: SiemRuleMigrationsCreateClientParams): SiemRuleMigrationsClient { - return this.rules.createClient(params); + return this.rules.createClient({ ...params, packageService: this.packageService }); + } + + public start(packageService?: PackageService) { + this.packageService = packageService; } stop() { diff --git a/x-pack/solutions/security/plugins/security_solution/server/plugin.ts b/x-pack/solutions/security/plugins/security_solution/server/plugin.ts index d8fa5c61ee7f3..bf1289f959bac 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/plugin.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/plugin.ts @@ -688,6 +688,8 @@ export class Plugin implements ISecuritySolutionPlugin { this.telemetryReceiver ); + this.siemMigrationsService.start(packageService); + securityWorkflowInsightsService .start({ esClient: core.elasticsearch.client.asInternalUser,