From 4b65a658d1f7d3c812c1f65ca131f2ffd24364fe Mon Sep 17 00:00:00 2001
From: Ievgen Sorokopud <ievgen.sorokopud@elastic.co>
Date: Fri, 22 Nov 2024 15:48:14 +0100
Subject: [PATCH] [Rules migration][UI] Basic rule migrations UI (#10820)
 (#200978)

## Summary

[Internal link](https://github.com/elastic/security-team/issues/10820)
to the feature details

This is a very first version of the SIEM rules migrations UI
functionality. The main goal is to setup and agree on a folder structure
where the feature gonna live. Tests covering feature will follow in a
separate PR (see [internal
link](https://github.com/elastic/security-team/issues/11232) for more
details).

The code follows the structure of prebuilt rules feature
https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table
and hidden behind `siemMigrationsEnabled` feature flag.

### Key UI changes

* New "SIEM Rules Migrations." rules management sub-page
* Navigation between different "finished" migrations
* InMemory table with all the translations within the selected migration
* Translation details preview flyout with `Translation` and `Overview`
tabs
* User cannot modify translations via UI

### Testing locally

Enable the flag

```
xpack.securitySolution.enableExperimental: ['siemMigrationsEnabled']
```
### Screenshot


https://github.com/user-attachments/assets/a5a7e777-c5f8-40b4-be1d-1bd07a2729ac
---
 .github/CODEOWNERS                            |   1 +
 packages/deeplinks/security/deep_links.ts     |   1 +
 .../security_solution/common/constants.ts     |   2 +
 .../public/app/translations.ts                |   7 +
 .../public/lazy_sub_plugins.tsx               |   2 +
 .../security_solution/public/plugin.tsx       |   4 +
 .../security_solution/public/rules/links.ts   |   3 +
 .../public/siem_migrations/index.ts           |  19 ++
 .../public/siem_migrations/jest.config.js     |  19 ++
 .../public/siem_migrations/links.ts           |  35 +++
 .../public/siem_migrations/routes.tsx         |  31 +++
 .../public/siem_migrations/rules/api/api.ts   |  66 +++++
 .../rules/api/hooks/constants.ts              |  13 +
 .../api/hooks/use_get_rule_migrations.ts      |  33 +++
 .../use_get_rule_migrations_stats_all.ts      |  30 +++
 .../rules/components/header_buttons/index.tsx |  84 ++++++
 .../components/header_buttons/translations.ts |  23 ++
 .../rules/components/no_migrations/index.tsx  |  53 ++++
 .../components/no_migrations/translations.ts  |  29 +++
 .../rules/components/rules_table/filters.tsx  |  59 +++++
 .../rules/components/rules_table/index.tsx    | 125 +++++++++
 .../rules_table/no_items_message.tsx          |  52 ++++
 .../components/rules_table/translations.ts    |  36 +++
 .../components/status_badge/index.test.tsx    |  19 ++
 .../rules/components/status_badge/index.tsx   |  49 ++++
 .../translation_details_flyout/constants.ts   |   9 +
 .../translation_details_flyout/index.tsx      | 246 ++++++++++++++++++
 .../translation_tab/header.tsx                |  20 ++
 .../translation_tab/index.tsx                 | 110 ++++++++
 .../translation_tab/rule_query.tsx            |  49 ++++
 .../translation_tab/translations.ts           |  43 +++
 .../translations.ts                           |  29 +++
 .../rules/hooks/translations.ts               |  15 ++
 .../hooks/use_filter_rules_to_install.ts      |  33 +++
 .../rules/hooks/use_rule_preview_flyout.tsx   |  55 ++++
 .../rules/hooks/use_rules_table_columns.tsx   | 107 ++++++++
 .../siem_migrations/rules/pages/index.tsx     | 104 ++++++++
 .../rules/pages/translations.tsx              |  12 +
 .../siem_migrations/rules/utils/constants.ts  |  11 +
 .../siem_migrations/rules/utils/helpers.tsx   |  28 ++
 .../rules/utils/translations.ts               |  36 +++
 .../plugins/security_solution/public/types.ts |   3 +
 42 files changed, 1705 insertions(+)
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/index.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/jest.config.js
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/links.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/api/api.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/constants.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations_stats_all.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/filters.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/no_items_message.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.test.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/constants.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/header.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/rule_query.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/translations.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_filter_rules_to_install.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rule_preview_flyout.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rules_table_columns.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/utils/constants.ts
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx
 create mode 100644 x-pack/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index aabd44dd6f15f..c0adb7f980d49 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2098,6 +2098,7 @@ x-pack/test/security_solution_api_integration/test_suites/sources @elastic/secur
 
 /x-pack/plugins/security_solution/server/lib/siem_migrations @elastic/security-threat-hunting
 /x-pack/plugins/security_solution/common/siem_migrations @elastic/security-threat-hunting
+/x-pack/plugins/security_solution/public/siem_migrations @elastic/security-threat-hunting
 
 ## Security Solution Threat Hunting areas - Threat Hunting Investigations
 
diff --git a/packages/deeplinks/security/deep_links.ts b/packages/deeplinks/security/deep_links.ts
index 644691bd5b8bc..c1d9b3b3cb6af 100644
--- a/packages/deeplinks/security/deep_links.ts
+++ b/packages/deeplinks/security/deep_links.ts
@@ -69,6 +69,7 @@ export enum SecurityPageName {
   rulesAdd = 'rules-add',
   rulesCreate = 'rules-create',
   rulesLanding = 'rules-landing',
+  siemMigrationsRules = 'siem_migrations-rules',
   /*
    * Warning: Computed values are not permitted in an enum with string valued members
    * All threat intelligence page names must match `TIPageId` in x-pack/plugins/threat_intelligence/public/common/navigation/types.ts
diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index b366a0e555357..265af5a47e1fe 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -138,6 +138,8 @@ export const APP_BLOCKLIST_PATH = `${APP_PATH}${BLOCKLIST_PATH}` as const;
 export const APP_RESPONSE_ACTIONS_HISTORY_PATH =
   `${APP_PATH}${RESPONSE_ACTIONS_HISTORY_PATH}` as const;
 export const NOTES_PATH = `${MANAGEMENT_PATH}/notes` as const;
+export const SIEM_MIGRATIONS_PATH = '/siem_migrations' as const;
+export const SIEM_MIGRATIONS_RULES_PATH = `${SIEM_MIGRATIONS_PATH}/rules` as const;
 
 // cloud logs to exclude from default index pattern
 export const EXCLUDE_ELASTIC_CLOUD_INDICES = ['-*elastic-cloud-logs-*'];
diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts
index 709bb5f614f7b..1769a805f488f 100644
--- a/x-pack/plugins/security_solution/public/app/translations.ts
+++ b/x-pack/plugins/security_solution/public/app/translations.ts
@@ -101,6 +101,13 @@ export const EXCEPTIONS = i18n.translate('xpack.securitySolution.navigation.exce
   defaultMessage: 'Shared exception lists',
 });
 
+export const SIEM_MIGRATIONS_RULES = i18n.translate(
+  'xpack.securitySolution.navigation.siemMigrationsRules',
+  {
+    defaultMessage: 'SIEM Rules Migrations',
+  }
+);
+
 export const ALERTS = i18n.translate('xpack.securitySolution.navigation.alerts', {
   defaultMessage: 'Alerts',
 });
diff --git a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx
index 8bbba3885a2ab..1be423c988397 100644
--- a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx
+++ b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx
@@ -29,6 +29,7 @@ import { EntityAnalytics } from './entity_analytics';
 import { Assets } from './assets';
 import { Investigations } from './investigations';
 import { MachineLearning } from './machine_learning';
+import { SiemMigrations } from './siem_migrations';
 
 /**
  * The classes used to instantiate the sub plugins. These are grouped into a single object for the sake of bundling them in a single dynamic import.
@@ -53,5 +54,6 @@ const subPluginClasses = {
   Assets,
   Investigations,
   MachineLearning,
+  SiemMigrations,
 };
 export { subPluginClasses };
diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx
index b74d0cffdc88d..f933832264247 100644
--- a/x-pack/plugins/security_solution/public/plugin.tsx
+++ b/x-pack/plugins/security_solution/public/plugin.tsx
@@ -245,6 +245,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
         assets: new subPluginClasses.Assets(),
         investigations: new subPluginClasses.Investigations(),
         machineLearning: new subPluginClasses.MachineLearning(),
+        siemMigrations: new subPluginClasses.SiemMigrations(),
       };
     }
     return this._subPlugins;
@@ -279,6 +280,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
       assets: subPlugins.assets.start(),
       investigations: subPlugins.investigations.start(),
       machineLearning: subPlugins.machineLearning.start(),
+      siemMigrations: subPlugins.siemMigrations.start(
+        this.experimentalFeatures.siemMigrationsEnabled
+      ),
     };
   }
 
diff --git a/x-pack/plugins/security_solution/public/rules/links.ts b/x-pack/plugins/security_solution/public/rules/links.ts
index 5564a8b9b4e2a..ba4280739bbe6 100644
--- a/x-pack/plugins/security_solution/public/rules/links.ts
+++ b/x-pack/plugins/security_solution/public/rules/links.ts
@@ -29,6 +29,7 @@ import type { LinkItem } from '../common/links';
 import { IconConsoleCloud } from '../common/icons/console_cloud';
 import { IconRollup } from '../common/icons/rollup';
 import { IconDashboards } from '../common/icons/dashboards';
+import { siemMigrationsLinks } from '../siem_migrations/links';
 
 export const links: LinkItem = {
   id: SecurityPageName.rulesLanding,
@@ -106,6 +107,7 @@ export const links: LinkItem = {
         }),
       ],
     },
+    siemMigrationsLinks,
   ],
   categories: [
     {
@@ -116,6 +118,7 @@ export const links: LinkItem = {
         SecurityPageName.rules,
         SecurityPageName.cloudSecurityPostureBenchmarks,
         SecurityPageName.exceptions,
+        SecurityPageName.siemMigrationsRules,
       ],
     },
     {
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/index.ts b/x-pack/plugins/security_solution/public/siem_migrations/index.ts
new file mode 100644
index 0000000000000..4b842e2a8a56c
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/index.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 { SecuritySubPlugin } from '../app/types';
+import { routes } from './routes';
+
+export class SiemMigrations {
+  public setup() {}
+
+  public start(isEnabled = false): SecuritySubPlugin {
+    return {
+      routes: isEnabled ? routes : [],
+    };
+  }
+}
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/jest.config.js b/x-pack/plugins/security_solution/public/siem_migrations/jest.config.js
new file mode 100644
index 0000000000000..fd313059456a1
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/jest.config.js
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+module.exports = {
+  preset: '@kbn/test',
+  rootDir: '../../../../..',
+  roots: ['<rootDir>/x-pack/plugins/security_solution/public/siem_migrations'],
+  coverageDirectory:
+    '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/siem_migrations',
+  coverageReporters: ['text', 'html'],
+  collectCoverageFrom: [
+    '<rootDir>/x-pack/plugins/security_solution/public/siem_migrations/**/*.{ts,tsx}',
+  ],
+  moduleNameMapper: require('../../server/__mocks__/module_name_map'),
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/links.ts b/x-pack/plugins/security_solution/public/siem_migrations/links.ts
new file mode 100644
index 0000000000000..34db8a357785a
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/links.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+import {
+  SecurityPageName,
+  SERVER_APP_ID,
+  SIEM_MIGRATIONS_RULES_PATH,
+} from '../../common/constants';
+import { SIEM_MIGRATIONS_RULES } from '../app/translations';
+import type { LinkItem } from '../common/links/types';
+import { IconConsoleCloud } from '../common/icons/console_cloud';
+
+export const siemMigrationsLinks: LinkItem = {
+  id: SecurityPageName.siemMigrationsRules,
+  title: SIEM_MIGRATIONS_RULES,
+  description: i18n.translate('xpack.securitySolution.appLinks.siemMigrationsRulesDescription', {
+    defaultMessage: 'SIEM Rules Migrations.',
+  }),
+  landingIcon: IconConsoleCloud,
+  path: SIEM_MIGRATIONS_RULES_PATH,
+  capabilities: [`${SERVER_APP_ID}.show`],
+  skipUrlState: true,
+  hideTimeline: true,
+  globalSearchKeywords: [
+    i18n.translate('xpack.securitySolution.appLinks.siemMigrationsRules', {
+      defaultMessage: 'SIEM Rules Migrations',
+    }),
+  ],
+  experimentalKey: 'siemMigrationsEnabled',
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx b/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
new file mode 100644
index 0000000000000..610eb7e2a72d8
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
@@ -0,0 +1,31 @@
+/*
+ * 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 { SecuritySubPluginRoutes } from '../app/types';
+import { SIEM_MIGRATIONS_RULES_PATH, SecurityPageName } from '../../common/constants';
+import { RulesPage } from './rules/pages';
+import { PluginTemplateWrapper } from '../common/components/plugin_template_wrapper';
+import { SecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper';
+
+export const RulesRoutes = () => {
+  return (
+    <PluginTemplateWrapper>
+      <SecurityRoutePageWrapper pageName={SecurityPageName.siemMigrationsRules}>
+        <RulesPage />
+      </SecurityRoutePageWrapper>
+    </PluginTemplateWrapper>
+  );
+};
+
+export const routes: SecuritySubPluginRoutes = [
+  {
+    path: SIEM_MIGRATIONS_RULES_PATH,
+    component: RulesRoutes,
+  },
+];
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/api.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/api.ts
new file mode 100644
index 0000000000000..7232cb722bd1a
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/api.ts
@@ -0,0 +1,66 @@
+/*
+ * 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 { KibanaServices } from '../../../common/lib/kibana';
+
+import {
+  SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
+  SIEM_RULE_MIGRATION_PATH,
+} from '../../../../common/siem_migrations/constants';
+import type {
+  GetAllStatsRuleMigrationResponse,
+  GetRuleMigrationResponse,
+} from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
+
+/**
+ * Retrieves the stats for all the existing migrations, aggregated by `migration_id`.
+ *
+ * @param signal AbortSignal for cancelling request
+ *
+ * @throws An error if response is not OK
+ */
+export const getRuleMigrationsStatsAll = async ({
+  signal,
+}: {
+  signal: AbortSignal | undefined;
+}): Promise<GetAllStatsRuleMigrationResponse> => {
+  return KibanaServices.get().http.fetch<GetAllStatsRuleMigrationResponse>(
+    SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
+    {
+      method: 'GET',
+      version: '1',
+      signal,
+    }
+  );
+};
+
+/**
+ * Retrieves all the migration rule documents of a specific migration.
+ *
+ * @param migrationId `id` of the migration to retrieve rule documents for
+ * @param signal AbortSignal for cancelling request
+ *
+ * @throws An error if response is not OK
+ */
+export const getRuleMigrations = async ({
+  migrationId,
+  signal,
+}: {
+  migrationId: string;
+  signal: AbortSignal | undefined;
+}): Promise<GetRuleMigrationResponse> => {
+  return KibanaServices.get().http.fetch<GetRuleMigrationResponse>(
+    replaceParams(SIEM_RULE_MIGRATION_PATH, { migration_id: migrationId }),
+    {
+      method: 'GET',
+      version: '1',
+      signal,
+    }
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/constants.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/constants.ts
new file mode 100644
index 0000000000000..61e0d1e05f7f0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/constants.ts
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ */
+
+const ONE_MINUTE = 60000;
+
+export const DEFAULT_QUERY_OPTIONS = {
+  refetchIntervalInBackground: false,
+  staleTime: ONE_MINUTE * 5,
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations.ts
new file mode 100644
index 0000000000000..76cf01c6c35d0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations.ts
@@ -0,0 +1,33 @@
+/*
+ * 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 { UseQueryOptions } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
+import { replaceParams } from '@kbn/openapi-common/shared';
+import { DEFAULT_QUERY_OPTIONS } from './constants';
+import { getRuleMigrations } from '../api';
+import type { GetRuleMigrationResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
+import { SIEM_RULE_MIGRATION_PATH } from '../../../../../common/siem_migrations/constants';
+
+export const useGetRuleMigrationsQuery = (
+  migrationId: string,
+  options?: UseQueryOptions<GetRuleMigrationResponse>
+) => {
+  const SPECIFIC_MIGRATION_PATH = replaceParams(SIEM_RULE_MIGRATION_PATH, {
+    migration_id: migrationId,
+  });
+  return useQuery<GetRuleMigrationResponse>(
+    ['GET', SPECIFIC_MIGRATION_PATH],
+    async ({ signal }) => {
+      return getRuleMigrations({ migrationId, signal });
+    },
+    {
+      ...DEFAULT_QUERY_OPTIONS,
+      ...options,
+    }
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations_stats_all.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations_stats_all.ts
new file mode 100644
index 0000000000000..026e407050e97
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/api/hooks/use_get_rule_migrations_stats_all.ts
@@ -0,0 +1,30 @@
+/*
+ * 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 { UseQueryOptions } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
+import { DEFAULT_QUERY_OPTIONS } from './constants';
+import { getRuleMigrationsStatsAll } from '../api';
+import type { GetAllStatsRuleMigrationResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
+import { SIEM_RULE_MIGRATIONS_ALL_STATS_PATH } from '../../../../../common/siem_migrations/constants';
+
+export const GET_RULE_MIGRATIONS_STATS_ALL_QUERY_KEY = ['GET', SIEM_RULE_MIGRATIONS_ALL_STATS_PATH];
+
+export const useGetRuleMigrationsStatsAllQuery = (
+  options?: UseQueryOptions<GetAllStatsRuleMigrationResponse>
+) => {
+  return useQuery<GetAllStatsRuleMigrationResponse>(
+    GET_RULE_MIGRATIONS_STATS_ALL_QUERY_KEY,
+    async ({ signal }) => {
+      return getRuleMigrationsStatsAll({ signal });
+    },
+    {
+      ...DEFAULT_QUERY_OPTIONS,
+      ...options,
+    }
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx
new file mode 100644
index 0000000000000..ba73bd9c84946
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/index.tsx
@@ -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 React, { useMemo } from 'react';
+
+import type { EuiComboBoxOptionOption } from '@elastic/eui';
+import { EuiComboBox, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import * as i18n from './translations';
+
+export interface HeaderButtonsProps {
+  /**
+   * Available rule migrations ids
+   */
+  migrationsIds: string[];
+
+  /**
+   * Selected rule migration id
+   */
+  selectedMigrationId: string | undefined;
+
+  /**
+   * Handles migration selection changes
+   * @param selectedId Selected migration id
+   * @returns
+   */
+  onMigrationIdChange: (selectedId?: string) => void;
+}
+
+const HeaderButtonsComponent: React.FC<HeaderButtonsProps> = ({
+  migrationsIds,
+  selectedMigrationId,
+  onMigrationIdChange,
+}) => {
+  const migrationOptions = useMemo(() => {
+    const options: Array<EuiComboBoxOptionOption<string>> = migrationsIds.map((id, index) => ({
+      value: id,
+      'data-test-subj': `migrationSelectionOption-${index}`,
+      label: i18n.SIEM_MIGRATIONS_OPTION_LABEL(index + 1),
+    }));
+    return options;
+  }, [migrationsIds]);
+  const selectedMigrationOption = useMemo<Array<EuiComboBoxOptionOption<string>>>(() => {
+    const index = migrationsIds.findIndex((id) => id === selectedMigrationId);
+    return index !== -1
+      ? [
+          {
+            value: selectedMigrationId,
+            'data-test-subj': `migrationSelectionOption-${index}`,
+            label: i18n.SIEM_MIGRATIONS_OPTION_LABEL(index + 1),
+          },
+        ]
+      : [];
+  }, [migrationsIds, selectedMigrationId]);
+
+  const onChange = (selected: Array<EuiComboBoxOptionOption<string>>) => {
+    onMigrationIdChange(selected[0].value);
+  };
+
+  if (!migrationsIds.length) {
+    return null;
+  }
+
+  return (
+    <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
+      <EuiFlexItem grow={false}>
+        <EuiComboBox
+          aria-label={i18n.SIEM_MIGRATIONS_OPTION_AREAL_LABEL}
+          onChange={onChange}
+          options={migrationOptions}
+          selectedOptions={selectedMigrationOption}
+          singleSelection={{ asPlainText: true }}
+          isClearable={false}
+        />
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+export const HeaderButtons = React.memo(HeaderButtonsComponent);
+HeaderButtons.displayName = 'HeaderButtons';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/translations.ts
new file mode 100644
index 0000000000000..e00721c70103a
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/header_buttons/translations.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const SIEM_MIGRATIONS_OPTION_AREAL_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.selectionOption.arealLabel',
+  {
+    defaultMessage: 'Select a migration',
+  }
+);
+
+export const SIEM_MIGRATIONS_OPTION_LABEL = (optionIndex: number) =>
+  i18n.translate('xpack.securitySolution.siemMigrations.rules.selectionOption.title', {
+    defaultMessage: 'SIEM rule migration {optionIndex}',
+    values: {
+      optionIndex,
+    },
+  });
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx
new file mode 100644
index 0000000000000..e4b3701d94c73
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/index.tsx
@@ -0,0 +1,53 @@
+/*
+ * 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 { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React from 'react';
+import { SecurityPageName } from '../../../../../common';
+import { useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
+import * as i18n from './translations';
+
+const NoMigrationsComponent = () => {
+  const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
+  const { onClick: onClickLink } = getSecuritySolutionLinkProps({
+    deepLinkId: SecurityPageName.landing,
+    path: 'siem_migrations',
+  });
+
+  return (
+    <EuiFlexGroup
+      alignItems="center"
+      gutterSize="s"
+      responsive={false}
+      direction="column"
+      wrap={true}
+    >
+      <EuiFlexItem grow={false}>
+        <EuiEmptyPrompt
+          title={<h2>{i18n.NO_MIGRATIONS_AVAILABLE}</h2>}
+          titleSize="s"
+          body={i18n.NO_MIGRATIONS_AVAILABLE_BODY}
+          data-test-subj="noMigrationsAvailable"
+        />
+      </EuiFlexItem>
+      <EuiFlexItem grow={false}>
+        <EuiButton
+          fill
+          iconType="arrowLeft"
+          color={'primary'}
+          onClick={onClickLink}
+          data-test-subj="goToSiemMigrationsButton"
+        >
+          {i18n.GO_BACK_TO_RULES_TABLE_BUTTON}
+        </EuiButton>
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+export const NoMigrations = React.memo(NoMigrationsComponent);
+NoMigrations.displayName = 'NoMigrations';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts
new file mode 100644
index 0000000000000..77ec0454fb5a5
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/no_migrations/translations.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const NO_MIGRATIONS_AVAILABLE = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.noMigrationsTitle',
+  {
+    defaultMessage: 'No migrations',
+  }
+);
+
+export const NO_MIGRATIONS_AVAILABLE_BODY = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.noMigrationsBodyTitle',
+  {
+    defaultMessage: 'There are no migrations available',
+  }
+);
+
+export const GO_BACK_TO_RULES_TABLE_BUTTON = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.table.goToMigrationsPageButton',
+  {
+    defaultMessage: 'Go back to SIEM Migrations',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/filters.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/filters.tsx
new file mode 100644
index 0000000000000..5f4ae3098b6a3
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/filters.tsx
@@ -0,0 +1,59 @@
+/*
+ * 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 { EuiFlexGroup } from '@elastic/eui';
+import type { Dispatch, SetStateAction } from 'react';
+import React, { useCallback } from 'react';
+import styled from 'styled-components';
+import * as i18n from './translations';
+import { RuleSearchField } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rule_search_field';
+import type { TableFilterOptions } from '../../hooks/use_filter_rules_to_install';
+
+const FilterWrapper = styled(EuiFlexGroup)`
+  margin-bottom: ${({ theme }) => theme.eui.euiSizeM};
+`;
+
+export interface FiltersComponentProps {
+  /**
+   * Currently selected table filter
+   */
+  filterOptions: TableFilterOptions;
+
+  /**
+   * Handles filter options changes
+   */
+  setFilterOptions: Dispatch<SetStateAction<TableFilterOptions>>;
+}
+
+/**
+ * Collection of filters for filtering data within the SIEM Rules Migrations table.
+ * Contains search bar and tag selection
+ */
+const FiltersComponent: React.FC<FiltersComponentProps> = ({ filterOptions, setFilterOptions }) => {
+  const handleOnSearch = useCallback(
+    (filterString: string) => {
+      setFilterOptions((filters) => ({
+        ...filters,
+        filter: filterString.trim(),
+      }));
+    },
+    [setFilterOptions]
+  );
+
+  return (
+    <FilterWrapper gutterSize="m" justifyContent="flexEnd" wrap>
+      <RuleSearchField
+        initialValue={filterOptions.filter}
+        onSearch={handleOnSearch}
+        placeholder={i18n.SEARCH_PLACEHOLDER}
+      />
+    </FilterWrapper>
+  );
+};
+
+export const Filters = React.memo(FiltersComponent);
+Filters.displayName = 'Filters';
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
new file mode 100644
index 0000000000000..0cd3e07ea11a4
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/index.tsx
@@ -0,0 +1,125 @@
+/*
+ * 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 {
+  EuiInMemoryTable,
+  EuiSkeletonLoading,
+  EuiProgress,
+  EuiSkeletonTitle,
+  EuiSkeletonText,
+  EuiFlexGroup,
+  EuiFlexItem,
+} from '@elastic/eui';
+import React, { useState } from 'react';
+
+import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen';
+import {
+  RULES_TABLE_INITIAL_PAGE_SIZE,
+  RULES_TABLE_PAGE_SIZE_OPTIONS,
+} from '../../../../detection_engine/rule_management_ui/components/rules_table/constants';
+import { NoItemsMessage } from './no_items_message';
+import { Filters } from './filters';
+import { useRulesTableColumns } from '../../hooks/use_rules_table_columns';
+import { useGetRuleMigrationsQuery } from '../../api/hooks/use_get_rule_migrations';
+import type { TableFilterOptions } from '../../hooks/use_filter_rules_to_install';
+import { useFilterRulesToInstall } from '../../hooks/use_filter_rules_to_install';
+
+export interface RulesTableComponentProps {
+  /**
+   * Selected rule migration id
+   */
+  migrationId: string;
+
+  /**
+   * Opens the flyout with the details of the rule migration
+   * @param rule Rule migration
+   * @returns
+   */
+  openRulePreview: (rule: RuleMigration) => void;
+}
+
+/**
+ * Table Component for displaying SIEM rules migrations
+ */
+const RulesTableComponent: React.FC<RulesTableComponentProps> = ({
+  migrationId,
+  openRulePreview,
+}) => {
+  const { data: ruleMigrations, isLoading } = useGetRuleMigrationsQuery(migrationId);
+
+  const [selectedRuleMigrations, setSelectedRuleMigrations] = useState<RuleMigration[]>([]);
+
+  const [filterOptions, setFilterOptions] = useState<TableFilterOptions>({
+    filter: '',
+  });
+
+  const filteredRuleMigrations = useFilterRulesToInstall({
+    filterOptions,
+    ruleMigrations: ruleMigrations ?? [],
+  });
+
+  const shouldShowProgress = isLoading;
+
+  const rulesColumns = useRulesTableColumns({
+    openRulePreview,
+  });
+
+  return (
+    <>
+      {shouldShowProgress && (
+        <EuiProgress
+          data-test-subj="loadingRulesInfoProgress"
+          size="xs"
+          position="absolute"
+          color="accent"
+        />
+      )}
+      <EuiSkeletonLoading
+        isLoading={isLoading}
+        loadingContent={
+          <>
+            <EuiSkeletonTitle />
+            <EuiSkeletonText />
+          </>
+        }
+        loadedContent={
+          !filteredRuleMigrations.length ? (
+            <NoItemsMessage />
+          ) : (
+            <>
+              <EuiFlexGroup direction="column">
+                <EuiFlexItem grow={false}>
+                  <Filters filterOptions={filterOptions} setFilterOptions={setFilterOptions} />
+                </EuiFlexItem>
+              </EuiFlexGroup>
+
+              <EuiInMemoryTable
+                items={filteredRuleMigrations}
+                sorting
+                pagination={{
+                  initialPageSize: RULES_TABLE_INITIAL_PAGE_SIZE,
+                  pageSizeOptions: RULES_TABLE_PAGE_SIZE_OPTIONS,
+                }}
+                selection={{
+                  selectable: () => true,
+                  onSelectionChange: setSelectedRuleMigrations,
+                  initialSelected: selectedRuleMigrations,
+                }}
+                itemId="rule_id"
+                data-test-subj="rules-translation-table"
+                columns={rulesColumns}
+              />
+            </>
+          )
+        }
+      />
+    </>
+  );
+};
+
+export const RulesTable = React.memo(RulesTableComponent);
+RulesTable.displayName = 'RulesTable';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/no_items_message.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/no_items_message.tsx
new file mode 100644
index 0000000000000..7aeaac7ab2f6b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/no_items_message.tsx
@@ -0,0 +1,52 @@
+/*
+ * 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 { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React from 'react';
+import { SecurityPageName } from '../../../../../common';
+import { useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
+import * as i18n from './translations';
+
+const NoItemsMessageComponent = () => {
+  const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
+  const { onClick: onClickLink } = getSecuritySolutionLinkProps({
+    deepLinkId: SecurityPageName.landing,
+    path: 'siem_migrations',
+  });
+
+  return (
+    <EuiFlexGroup
+      alignItems="center"
+      gutterSize="s"
+      responsive={false}
+      direction="column"
+      wrap={true}
+    >
+      <EuiFlexItem grow={false}>
+        <EuiEmptyPrompt
+          title={<h2>{i18n.NO_TRANSLATIONS_AVAILABLE_FOR_INSTALL}</h2>}
+          titleSize="s"
+          body={i18n.NO_TRANSLATIONS_AVAILABLE_FOR_INSTALL_BODY}
+          data-test-subj="noRulesTranslationAvailableForInstall"
+        />
+      </EuiFlexItem>
+      <EuiFlexItem grow={false}>
+        <EuiButton
+          fill
+          iconType="arrowLeft"
+          color={'primary'}
+          onClick={onClickLink}
+          data-test-subj="goToSiemMigrationsButton"
+        >
+          {i18n.GO_BACK_TO_RULES_TABLE_BUTTON}
+        </EuiButton>
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+export const NoItemsMessage = React.memo(NoItemsMessageComponent);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/translations.ts
new file mode 100644
index 0000000000000..812f26f628e49
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/rules_table/translations.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 { i18n } from '@kbn/i18n';
+
+export const SEARCH_PLACEHOLDER = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.table.searchBarPlaceholder',
+  {
+    defaultMessage: 'Search by rule name',
+  }
+);
+
+export const NO_TRANSLATIONS_AVAILABLE_FOR_INSTALL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.table.noRulesTitle',
+  {
+    defaultMessage: 'Empty migration',
+  }
+);
+
+export const NO_TRANSLATIONS_AVAILABLE_FOR_INSTALL_BODY = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.table.noRulesBodyTitle',
+  {
+    defaultMessage: 'There are no translations available for installation',
+  }
+);
+
+export const GO_BACK_TO_RULES_TABLE_BUTTON = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.table.goToMigrationsPageButton',
+  {
+    defaultMessage: 'Go back to SIEM Migrations',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.test.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.test.tsx
new file mode 100644
index 0000000000000..aaf256cfb60b5
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.test.tsx
@@ -0,0 +1,19 @@
+/*
+ * 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 { shallow } from 'enzyme';
+
+import { StatusBadge } from '.';
+
+describe('StatusBadge', () => {
+  it('renders correctly', () => {
+    const wrapper = shallow(<StatusBadge value="full" />);
+
+    expect(wrapper.find('HealthTruncateText')).toHaveLength(1);
+  });
+});
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx
new file mode 100644
index 0000000000000..40b3c5ceb5719
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/status_badge/index.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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 { euiLightVars } from '@kbn/ui-theme';
+
+import type { RuleMigrationTranslationResult } from '../../../../../common/siem_migrations/model/rule_migration.gen';
+import { HealthTruncateText } from '../../../../common/components/health_truncate_text';
+import { convertTranslationResultIntoText } from '../../utils/helpers';
+
+const { euiColorVis0, euiColorVis7, euiColorVis9 } = euiLightVars;
+const statusToColorMap: Record<RuleMigrationTranslationResult, string> = {
+  full: euiColorVis0,
+  partial: euiColorVis7,
+  untranslatable: euiColorVis9,
+};
+
+interface Props {
+  value?: RuleMigrationTranslationResult;
+  installedRuleId?: string;
+  'data-test-subj'?: string;
+}
+
+const StatusBadgeComponent: React.FC<Props> = ({
+  value,
+  installedRuleId,
+  'data-test-subj': dataTestSubj = 'translation-result',
+}) => {
+  const translationResult = installedRuleId || !value ? 'full' : value;
+  const displayValue = convertTranslationResultIntoText(translationResult);
+  const color = statusToColorMap[translationResult];
+
+  return (
+    <HealthTruncateText
+      healthColor={color}
+      tooltipContent={displayValue}
+      dataTestSubj={dataTestSubj}
+    >
+      {displayValue}
+    </HealthTruncateText>
+  );
+};
+
+export const StatusBadge = React.memo(StatusBadgeComponent);
+StatusBadge.displayName = 'StatusBadge';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/constants.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/constants.ts
new file mode 100644
index 0000000000000..4d6bcd542b866
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/constants.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS: [string, string] = ['50%', '50%'];
+export const LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS: [string, string] = ['30%', '70%'];
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/index.tsx
new file mode 100644
index 0000000000000..4aaff21281d64
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/index.tsx
@@ -0,0 +1,246 @@
+/*
+ * 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 { FC, PropsWithChildren } from 'react';
+import React, { useMemo, useState, useEffect } from 'react';
+import styled from 'styled-components';
+import { css } from '@emotion/css';
+import { euiThemeVars } from '@kbn/ui-theme';
+import {
+  EuiButtonEmpty,
+  EuiTitle,
+  EuiFlyout,
+  EuiFlyoutHeader,
+  EuiFlyoutBody,
+  EuiFlyoutFooter,
+  EuiTabbedContent,
+  EuiSpacer,
+  EuiFlexGroup,
+  EuiFlexItem,
+  useGeneratedHtmlId,
+} from '@elastic/eui';
+import type { EuiTabbedContentTab, EuiTabbedContentProps, EuiFlyoutProps } from '@elastic/eui';
+
+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 * as i18n from './translations';
+import {
+  DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS,
+  LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS,
+} from './constants';
+import { TranslationTab } from './translation_tab';
+import {
+  DEFAULT_TRANSLATION_RISK_SCORE,
+  DEFAULT_TRANSLATION_SEVERITY,
+} from '../../utils/constants';
+
+const StyledEuiFlyoutBody = styled(EuiFlyoutBody)`
+  .euiFlyoutBody__overflow {
+    display: flex;
+    flex: 1;
+    overflow: hidden;
+
+    .euiFlyoutBody__overflowContent {
+      flex: 1;
+      overflow: hidden;
+      padding: ${({ theme }) => `0 ${theme.eui.euiSizeL} 0`};
+    }
+  }
+`;
+
+const StyledFlexGroup = styled(EuiFlexGroup)`
+  height: 100%;
+`;
+
+const StyledEuiFlexItem = styled(EuiFlexItem)`
+  &.euiFlexItem {
+    flex: 1 0 0;
+    overflow: hidden;
+  }
+`;
+
+const StyledEuiTabbedContent = styled(EuiTabbedContent)`
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  overflow: hidden;
+
+  > [role='tabpanel'] {
+    display: flex;
+    flex: 1;
+    flex-direction: column;
+    overflow: hidden;
+    overflow-y: auto;
+
+    ::-webkit-scrollbar {
+      -webkit-appearance: none;
+      width: 7px;
+    }
+
+    ::-webkit-scrollbar-thumb {
+      border-radius: 4px;
+      background-color: rgba(0, 0, 0, 0.5);
+      -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
+    }
+  }
+`;
+
+/*
+ * Fixes tabs to the top and allows the content to scroll.
+ */
+const ScrollableFlyoutTabbedContent = (props: EuiTabbedContentProps) => (
+  <StyledFlexGroup direction="column" gutterSize="none">
+    <StyledEuiFlexItem grow={true}>
+      <StyledEuiTabbedContent {...props} />
+    </StyledEuiFlexItem>
+  </StyledFlexGroup>
+);
+
+const tabPaddingClassName = css`
+  padding: 0 ${euiThemeVars.euiSizeM} ${euiThemeVars.euiSizeXL} ${euiThemeVars.euiSizeM};
+`;
+
+export const TabContentPadding: FC<PropsWithChildren<unknown>> = ({ children }) => (
+  <div className={tabPaddingClassName}>{children}</div>
+);
+
+interface TranslationDetailsFlyoutProps {
+  ruleActions?: React.ReactNode;
+  ruleMigration: RuleMigration;
+  size?: EuiFlyoutProps['size'];
+  extraTabs?: EuiTabbedContentTab[];
+  closeFlyout: () => void;
+}
+
+export const TranslationDetailsFlyout = ({
+  ruleActions,
+  ruleMigration,
+  size = 'm',
+  extraTabs = [],
+  closeFlyout,
+}: TranslationDetailsFlyoutProps) => {
+  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 values
+      severity: (ruleMigration.elastic_rule?.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY,
+      risk_score: DEFAULT_TRANSLATION_RISK_SCORE,
+      from: 'now-360s',
+      to: 'now',
+      interval: '5m',
+    } as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter
+  }, [ruleMigration]);
+
+  const translationTab: EuiTabbedContentTab = useMemo(
+    () => ({
+      id: 'translation',
+      name: i18n.TRANSLATION_TAB_LABEL,
+      content: (
+        <TabContentPadding>
+          <TranslationTab ruleMigration={ruleMigration} />
+        </TabContentPadding>
+      ),
+    }),
+    [ruleMigration]
+  );
+
+  const overviewTab: EuiTabbedContentTab = useMemo(
+    () => ({
+      id: 'overview',
+      name: i18n.OVERVIEW_TAB_LABEL,
+      content: (
+        <TabContentPadding>
+          <RuleOverviewTab
+            rule={rule}
+            columnWidths={
+              size === 'l'
+                ? LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS
+                : DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS
+            }
+            expandedOverviewSections={expandedOverviewSections}
+            toggleOverviewSection={toggleOverviewSection}
+          />
+        </TabContentPadding>
+      ),
+    }),
+    [rule, size, expandedOverviewSections, toggleOverviewSection]
+  );
+
+  const tabs = useMemo(() => {
+    return [...extraTabs, translationTab, overviewTab];
+  }, [extraTabs, translationTab, overviewTab]);
+
+  const [selectedTabId, setSelectedTabId] = useState<string>(tabs[0].id);
+  const selectedTab = tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0];
+
+  useEffect(() => {
+    if (!tabs.find((tab) => tab.id === selectedTabId)) {
+      // Switch to first tab if currently selected tab is not available for this rule
+      setSelectedTabId(tabs[0].id);
+    }
+  }, [tabs, selectedTabId]);
+
+  const onTabClick = (tab: EuiTabbedContentTab) => {
+    setSelectedTabId(tab.id);
+  };
+
+  const migrationsRulesFlyoutTitleId = useGeneratedHtmlId({
+    prefix: 'migrationRulesFlyoutTitle',
+  });
+
+  return (
+    <EuiFlyout
+      size={size}
+      onClose={closeFlyout}
+      key="migrations-rules-flyout"
+      paddingSize="l"
+      data-test-subj="ruleMigrationDetailsFlyout"
+      aria-labelledby={migrationsRulesFlyoutTitleId}
+      ownFocus
+    >
+      <EuiFlyoutHeader>
+        <EuiTitle size="m">
+          <h2 id={migrationsRulesFlyoutTitleId}>{rule.name}</h2>
+        </EuiTitle>
+        <EuiSpacer size="l" />
+      </EuiFlyoutHeader>
+      <StyledEuiFlyoutBody>
+        <ScrollableFlyoutTabbedContent
+          tabs={tabs}
+          selectedTab={selectedTab}
+          onTabClick={onTabClick}
+        />
+      </StyledEuiFlyoutBody>
+      <EuiFlyoutFooter>
+        <EuiFlexGroup justifyContent="spaceBetween">
+          <EuiFlexItem grow={false}>
+            <EuiButtonEmpty onClick={closeFlyout} flush="left">
+              {i18n.DISMISS_BUTTON_LABEL}
+            </EuiButtonEmpty>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>{ruleActions}</EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiFlyoutFooter>
+    </EuiFlyout>
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/header.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/header.tsx
new file mode 100644
index 0000000000000..57e99440e60a1
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/header.tsx
@@ -0,0 +1,20 @@
+/*
+ * 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 { EuiFlexGroup, EuiTitle } from '@elastic/eui';
+import * as i18n from './translations';
+
+export function TranslationTabHeader(): JSX.Element {
+  return (
+    <EuiFlexGroup direction="row" alignItems="center">
+      <EuiTitle data-test-subj="ruleTranslationLabel" size="xs">
+        <h5>{i18n.TAB_HEADER_TITLE}</h5>
+      </EuiTitle>
+    </EuiFlexGroup>
+  );
+}
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/index.tsx
new file mode 100644
index 0000000000000..66836b8ea5631
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/index.tsx
@@ -0,0 +1,110 @@
+/*
+ * 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 {
+  EuiAccordion,
+  EuiBadge,
+  EuiFieldText,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiFormRow,
+  EuiSpacer,
+  EuiSplitPanel,
+  EuiTitle,
+  useEuiTheme,
+} from '@elastic/eui';
+import { css } from '@emotion/css';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { RuleMigration } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
+import { TranslationTabHeader } from './header';
+import { RuleQueryComponent } from './rule_query';
+import * as i18n from './translations';
+import { convertTranslationResultIntoText } from '../../../utils/helpers';
+
+interface TranslationTabProps {
+  ruleMigration: RuleMigration;
+}
+
+export const TranslationTab = ({ ruleMigration }: TranslationTabProps) => {
+  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';
+
+  return (
+    <>
+      <EuiSpacer size="m" />
+      <EuiFormRow label={i18n.NAME_LABEL} fullWidth>
+        <EuiFieldText value={name} fullWidth />
+      </EuiFormRow>
+      <EuiSpacer size="m" />
+      <EuiAccordion
+        id="translationQueryItem"
+        buttonContent={<TranslationTabHeader />}
+        initialIsOpen={true}
+      >
+        <EuiFlexItem>
+          <EuiSpacer size="s" />
+          <EuiSplitPanel.Outer grow hasShadow={false} hasBorder={true}>
+            <EuiSplitPanel.Inner grow={false} color="subdued" paddingSize="s">
+              <EuiFlexGroup justifyContent="flexEnd">
+                <EuiFlexItem grow={false}>
+                  <EuiTitle size="xxs">
+                    <h2>
+                      <FormattedMessage
+                        id="xpack.securitySolution.detectionEngine.translationDetails.translationTab.statusTitle"
+                        defaultMessage="Translation status"
+                      />
+                    </h2>
+                  </EuiTitle>
+                </EuiFlexItem>
+                <EuiFlexItem grow={false}>
+                  <EuiBadge
+                    iconSide="right"
+                    iconType="arrowDown"
+                    color="primary"
+                    onClick={() => {}}
+                    onClickAriaLabel={'Click to update translation status'}
+                  >
+                    {convertTranslationResultIntoText(ruleMigration.translation_result)}
+                  </EuiBadge>
+                </EuiFlexItem>
+              </EuiFlexGroup>
+            </EuiSplitPanel.Inner>
+            <EuiSplitPanel.Inner grow>
+              <EuiFlexGroup gutterSize="s" alignItems="flexStart">
+                <EuiFlexItem grow={1}>
+                  <RuleQueryComponent
+                    title={i18n.SPLUNK_QUERY_TITLE}
+                    query={originalQuery}
+                    canEdit={false}
+                  />
+                </EuiFlexItem>
+                <EuiFlexItem
+                  grow={0}
+                  css={css`
+                    align-self: stretch;
+                    border-right: ${euiTheme.border.thin};
+                  `}
+                />
+                <EuiFlexItem grow={1}>
+                  <RuleQueryComponent
+                    title={i18n.ESQL_TRANSLATION_TITLE}
+                    query={elasticQuery}
+                    canEdit={false}
+                  />
+                </EuiFlexItem>
+              </EuiFlexGroup>
+            </EuiSplitPanel.Inner>
+          </EuiSplitPanel.Outer>
+        </EuiFlexItem>
+      </EuiAccordion>
+    </>
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/rule_query.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/rule_query.tsx
new file mode 100644
index 0000000000000..50977cafb18d0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/rule_query.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useMemo } from 'react';
+import { EuiMarkdownEditor, EuiMarkdownFormat, EuiTitle } from '@elastic/eui';
+import { SideHeader } from '../../../../../detection_engine/rule_management/components/rule_details/three_way_diff/components/side_header';
+import { FinalSideHelpInfo } from '../../../../../detection_engine/rule_management/components/rule_details/three_way_diff/final_side/final_side_help_info';
+import * as i18n from './translations';
+
+interface RuleQueryProps {
+  title: string;
+  query: string;
+  canEdit?: boolean;
+}
+
+export const RuleQueryComponent = ({ title, query, canEdit }: RuleQueryProps) => {
+  const queryTextComponent = useMemo(() => {
+    if (canEdit) {
+      return (
+        <EuiMarkdownEditor
+          aria-label={i18n.TRANSLATED_QUERY_AREAL_LABEL}
+          value={query}
+          onChange={() => {}}
+          height={400}
+          initialViewMode={'viewing'}
+        />
+      );
+    } else {
+      return <EuiMarkdownFormat>{query}</EuiMarkdownFormat>;
+    }
+  }, [canEdit, query]);
+  return (
+    <>
+      <SideHeader>
+        <EuiTitle size="xxs">
+          <h3>
+            {title}
+            <FinalSideHelpInfo />
+          </h3>
+        </EuiTitle>
+      </SideHeader>
+      {queryTextComponent}
+    </>
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/translations.ts
new file mode 100644
index 0000000000000..e7532a5a8b2e3
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translation_tab/translations.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const TAB_HEADER_TITLE = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.title',
+  {
+    defaultMessage: 'Translation',
+  }
+);
+
+export const NAME_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.nameLabel',
+  {
+    defaultMessage: 'Name',
+  }
+);
+
+export const SPLUNK_QUERY_TITLE = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.splunkQueryTitle',
+  {
+    defaultMessage: 'Splunk query',
+  }
+);
+
+export const ESQL_TRANSLATION_TITLE = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.esqlTranslationTitle',
+  {
+    defaultMessage: 'ES|QL translation',
+  }
+);
+
+export const TRANSLATED_QUERY_AREAL_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.queryArealLabel',
+  {
+    defaultMessage: 'Translated query',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translations.ts
new file mode 100644
index 0000000000000..8e6582b8c198e
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/components/translation_details_flyout/translations.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const OVERVIEW_TAB_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.overviewTabLabel',
+  {
+    defaultMessage: 'Overview',
+  }
+);
+
+export const TRANSLATION_TAB_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTabLabel',
+  {
+    defaultMessage: 'Translation',
+  }
+);
+
+export const DISMISS_BUTTON_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationDetails.dismissButtonLabel',
+  {
+    defaultMessage: 'Dismiss',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/translations.ts
new file mode 100644
index 0000000000000..74845b5f257ad
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/translations.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const COLUMN_STATUS = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.columns.statusTitle',
+  {
+    defaultMessage: 'Status',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_filter_rules_to_install.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_filter_rules_to_install.ts
new file mode 100644
index 0000000000000..f6862d3d90380
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_filter_rules_to_install.ts
@@ -0,0 +1,33 @@
+/*
+ * 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 { useMemo } from 'react';
+import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen';
+import type { FilterOptions } from '../../../detection_engine/rule_management/logic/types';
+
+export type TableFilterOptions = Pick<FilterOptions, 'filter'>;
+
+export const useFilterRulesToInstall = ({
+  ruleMigrations,
+  filterOptions,
+}: {
+  ruleMigrations: RuleMigration[];
+  filterOptions: TableFilterOptions;
+}) => {
+  const filteredRules = useMemo(() => {
+    const { filter } = filterOptions;
+    return ruleMigrations.filter((migration) => {
+      const name = migration.elastic_rule?.title ?? migration.original_rule.title;
+      if (filter && !name.toLowerCase().includes(filter.toLowerCase())) {
+        return false;
+      }
+      return true;
+    });
+  }, [filterOptions, ruleMigrations]);
+
+  return filteredRules;
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rule_preview_flyout.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rule_preview_flyout.tsx
new file mode 100644
index 0000000000000..1721b4e280aad
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rule_preview_flyout.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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 { ReactNode } from 'react';
+import React, { useCallback, useState, useMemo } from 'react';
+import type { EuiTabbedContentTab } from '@elastic/eui';
+import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen';
+import { TranslationDetailsFlyout } from '../components/translation_details_flyout';
+
+interface UseRulePreviewFlyoutParams {
+  ruleActionsFactory: (ruleMigration: RuleMigration, closeRulePreview: () => void) => ReactNode;
+  extraTabsFactory?: (ruleMigration: RuleMigration) => EuiTabbedContentTab[];
+}
+
+interface UseRulePreviewFlyoutResult {
+  rulePreviewFlyout: ReactNode;
+  openRulePreview: (rule: RuleMigration) => void;
+  closeRulePreview: () => void;
+}
+
+export function useRulePreviewFlyout({
+  extraTabsFactory,
+  ruleActionsFactory,
+}: UseRulePreviewFlyoutParams): UseRulePreviewFlyoutResult {
+  const [ruleMigration, setRuleMigrationForPreview] = useState<RuleMigration | undefined>();
+  const closeRulePreview = useCallback(() => setRuleMigrationForPreview(undefined), []);
+  const ruleActions = useMemo(
+    () => ruleMigration && ruleActionsFactory(ruleMigration, closeRulePreview),
+    [ruleMigration, ruleActionsFactory, closeRulePreview]
+  );
+  const extraTabs = useMemo(
+    () => (ruleMigration && extraTabsFactory ? extraTabsFactory(ruleMigration) : []),
+    [ruleMigration, extraTabsFactory]
+  );
+
+  return {
+    rulePreviewFlyout: ruleMigration && (
+      <TranslationDetailsFlyout
+        ruleMigration={ruleMigration}
+        size="l"
+        closeFlyout={closeRulePreview}
+        ruleActions={ruleActions}
+        extraTabs={extraTabs}
+      />
+    ),
+    openRulePreview: useCallback((rule: RuleMigration) => {
+      setRuleMigrationForPreview(rule);
+    }, []),
+    closeRulePreview,
+  };
+}
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rules_table_columns.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rules_table_columns.tsx
new file mode 100644
index 0000000000000..3b13b9e631ccb
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/hooks/use_rules_table_columns.tsx
@@ -0,0 +1,107 @@
+/*
+ * 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 { EuiBasicTableColumn } from '@elastic/eui';
+import { EuiText, EuiLink } from '@elastic/eui';
+import React, { useMemo } from 'react';
+import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types';
+import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen';
+import { SeverityBadge } from '../../../common/components/severity_badge';
+import * as rulesI18n from '../../../detections/pages/detection_engine/rules/translations';
+import * as i18n from './translations';
+import { getNormalizedSeverity } from '../../../detection_engine/rule_management_ui/components/rules_table/helpers';
+import { StatusBadge } from '../components/status_badge';
+import { DEFAULT_TRANSLATION_RISK_SCORE, DEFAULT_TRANSLATION_SEVERITY } from '../utils/constants';
+
+export type TableColumn = EuiBasicTableColumn<RuleMigration>;
+
+interface RuleNameProps {
+  name: string;
+  rule: RuleMigration;
+  openRulePreview: (rule: RuleMigration) => void;
+}
+
+const RuleName = ({ name, rule, openRulePreview }: RuleNameProps) => {
+  return (
+    <EuiLink
+      onClick={() => {
+        openRulePreview(rule);
+      }}
+      data-test-subj="ruleName"
+    >
+      {name}
+    </EuiLink>
+  );
+};
+
+const createRuleNameColumn = ({
+  openRulePreview,
+}: {
+  openRulePreview: (rule: RuleMigration) => void;
+}): TableColumn => {
+  return {
+    field: 'original_rule.title',
+    name: rulesI18n.COLUMN_RULE,
+    render: (value: RuleMigration['original_rule']['title'], rule: RuleMigration) => (
+      <RuleName name={value} rule={rule} openRulePreview={openRulePreview} />
+    ),
+    sortable: true,
+    truncateText: true,
+    width: '40%',
+    align: 'left',
+  };
+};
+
+const STATUS_COLUMN: TableColumn = {
+  field: 'translation_result',
+  name: i18n.COLUMN_STATUS,
+  render: (value: RuleMigration['translation_result'], rule: RuleMigration) => (
+    <StatusBadge value={value} installedRuleId={rule.elastic_rule?.id} />
+  ),
+  sortable: false,
+  truncateText: true,
+  width: '12%',
+};
+
+export const useRulesTableColumns = ({
+  openRulePreview,
+}: {
+  openRulePreview: (rule: RuleMigration) => void;
+}): TableColumn[] => {
+  return useMemo(
+    () => [
+      createRuleNameColumn({ openRulePreview }),
+      STATUS_COLUMN,
+      {
+        field: 'risk_score',
+        name: rulesI18n.COLUMN_RISK_SCORE,
+        render: () => (
+          <EuiText data-test-subj="riskScore" size="s">
+            {DEFAULT_TRANSLATION_RISK_SCORE}
+          </EuiText>
+        ),
+        sortable: true,
+        truncateText: true,
+        width: '75px',
+      },
+      {
+        field: 'elastic_rule.severity',
+        name: rulesI18n.COLUMN_SEVERITY,
+        render: (value?: Severity) => (
+          <SeverityBadge value={value ?? DEFAULT_TRANSLATION_SEVERITY} />
+        ),
+        sortable: ({ elastic_rule: elasticRule }: RuleMigration) =>
+          getNormalizedSeverity(
+            (elasticRule?.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY
+          ),
+        truncateText: true,
+        width: '12%',
+      },
+    ],
+    [openRulePreview]
+  );
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
new file mode 100644
index 0000000000000..616c85e7e7dee
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/index.tsx
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+
+import { EuiSkeletonLoading, EuiSkeletonText, EuiSkeletonTitle } from '@elastic/eui';
+import type { RuleMigration } from '../../../../common/siem_migrations/model/rule_migration.gen';
+import { SecurityPageName } from '../../../app/types';
+import { HeaderPage } from '../../../common/components/header_page';
+import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper';
+import { SpyRoute } from '../../../common/utils/route/spy_routes';
+
+import * as i18n from './translations';
+import { RulesTable } from '../components/rules_table';
+import { NeedAdminForUpdateRulesCallOut } from '../../../detections/components/callouts/need_admin_for_update_callout';
+import { MissingPrivilegesCallOut } from '../../../detections/components/callouts/missing_privileges_callout';
+import { HeaderButtons } from '../components/header_buttons';
+import { useGetRuleMigrationsStatsAllQuery } from '../api/hooks/use_get_rule_migrations_stats_all';
+import { useRulePreviewFlyout } from '../hooks/use_rule_preview_flyout';
+import { NoMigrations } from '../components/no_migrations';
+
+const RulesPageComponent: React.FC = () => {
+  const { data: ruleMigrationsStatsAll, isLoading: isLoadingMigrationsStats } =
+    useGetRuleMigrationsStatsAllQuery();
+
+  const migrationsIds = useMemo(() => {
+    if (isLoadingMigrationsStats || !ruleMigrationsStatsAll?.length) {
+      return [];
+    }
+    return ruleMigrationsStatsAll
+      .filter((migration) => migration.status === 'finished')
+      .map((migration) => migration.migration_id);
+  }, [isLoadingMigrationsStats, ruleMigrationsStatsAll]);
+
+  const [selectedMigrationId, setSelectedMigrationId] = useState<string | undefined>();
+  const onMigrationIdChange = (selectedId?: string) => {
+    setSelectedMigrationId(selectedId);
+  };
+
+  useEffect(() => {
+    if (!migrationsIds.length) {
+      return;
+    }
+    const index = migrationsIds.findIndex((id) => id === selectedMigrationId);
+    if (index === -1) {
+      setSelectedMigrationId(migrationsIds[0]);
+    }
+  }, [migrationsIds, selectedMigrationId]);
+
+  const ruleActionsFactory = useCallback(
+    (ruleMigration: RuleMigration, closeRulePreview: () => void) => {
+      // TODO: Add flyout action buttons
+      return null;
+    },
+    []
+  );
+
+  const { rulePreviewFlyout, openRulePreview } = useRulePreviewFlyout({
+    ruleActionsFactory,
+  });
+
+  return (
+    <>
+      <NeedAdminForUpdateRulesCallOut />
+      <MissingPrivilegesCallOut />
+
+      <SecuritySolutionPageWrapper>
+        <HeaderPage title={i18n.PAGE_TITLE}>
+          <HeaderButtons
+            migrationsIds={migrationsIds}
+            selectedMigrationId={selectedMigrationId}
+            onMigrationIdChange={onMigrationIdChange}
+          />
+        </HeaderPage>
+        <EuiSkeletonLoading
+          isLoading={isLoadingMigrationsStats}
+          loadingContent={
+            <>
+              <EuiSkeletonTitle />
+              <EuiSkeletonText />
+            </>
+          }
+          loadedContent={
+            selectedMigrationId ? (
+              <RulesTable migrationId={selectedMigrationId} openRulePreview={openRulePreview} />
+            ) : (
+              <NoMigrations />
+            )
+          }
+        />
+        {rulePreviewFlyout}
+      </SecuritySolutionPageWrapper>
+
+      <SpyRoute pageName={SecurityPageName.siemMigrationsRules} />
+    </>
+  );
+};
+
+export const RulesPage = React.memo(RulesPageComponent);
+RulesPage.displayName = 'RulesPage';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx
new file mode 100644
index 0000000000000..3c95eaab8fe90
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/pages/translations.tsx
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const PAGE_TITLE = i18n.translate('xpack.securitySolution.siemMigrations.rules.pageTitle', {
+  defaultMessage: 'Translated rules',
+});
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/constants.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/constants.ts
new file mode 100644
index 0000000000000..7400d4b0bcb63
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/constants.ts
@@ -0,0 +1,11 @@
+/*
+ * 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 '@kbn/securitysolution-io-ts-alerting-types';
+
+export const DEFAULT_TRANSLATION_RISK_SCORE = 21;
+export const DEFAULT_TRANSLATION_SEVERITY: Severity = 'low';
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx
new file mode 100644
index 0000000000000..cd49311db21eb
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/helpers.tsx
@@ -0,0 +1,28 @@
+/*
+ * 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 {
+  RuleMigrationTranslationResultEnum,
+  type RuleMigrationTranslationResult,
+} from '../../../../common/siem_migrations/model/rule_migration.gen';
+import * as i18n from './translations';
+
+export const convertTranslationResultIntoText = (status?: RuleMigrationTranslationResult) => {
+  switch (status) {
+    case RuleMigrationTranslationResultEnum.full:
+      return i18n.SIEM_TRANSLATION_RESULT_FULL_LABEL;
+
+    case RuleMigrationTranslationResultEnum.partial:
+      return i18n.SIEM_TRANSLATION_RESULT_PARTIAL_LABEL;
+
+    case RuleMigrationTranslationResultEnum.untranslatable:
+      return i18n.SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL;
+
+    default:
+      return i18n.SIEM_TRANSLATION_RESULT_UNKNOWN_LABEL;
+  }
+};
diff --git a/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/translations.ts
new file mode 100644
index 0000000000000..bc098936c00f7
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/siem_migrations/rules/utils/translations.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 { i18n } from '@kbn/i18n';
+
+export const SIEM_TRANSLATION_RESULT_FULL_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationResult.full',
+  {
+    defaultMessage: 'Fully translated',
+  }
+);
+
+export const SIEM_TRANSLATION_RESULT_PARTIAL_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationResult.partially',
+  {
+    defaultMessage: 'Partially translated',
+  }
+);
+
+export const SIEM_TRANSLATION_RESULT_UNTRANSLATABLE_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationResult.untranslatable',
+  {
+    defaultMessage: 'Not translated',
+  }
+);
+
+export const SIEM_TRANSLATION_RESULT_UNKNOWN_LABEL = i18n.translate(
+  'xpack.securitySolution.siemMigrations.rules.translationResult.unknown',
+  {
+    defaultMessage: 'Unknown',
+  }
+);
diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts
index 6642e02d5ecd6..9829fe2ad0c25 100644
--- a/x-pack/plugins/security_solution/public/types.ts
+++ b/x-pack/plugins/security_solution/public/types.ts
@@ -83,6 +83,7 @@ import type { EntityAnalytics } from './entity_analytics';
 import type { Assets } from './assets';
 import type { Investigations } from './investigations';
 import type { MachineLearning } from './machine_learning';
+import type { SiemMigrations } from './siem_migrations';
 
 import type { Dashboards } from './dashboards';
 import type { BreadcrumbsNav } from './common/breadcrumbs/types';
@@ -243,6 +244,7 @@ export interface SubPlugins {
   assets: Assets;
   investigations: Investigations;
   machineLearning: MachineLearning;
+  siemMigrations: SiemMigrations;
 }
 
 // TODO: find a better way to defined these types
@@ -266,4 +268,5 @@ export interface StartedSubPlugins {
   assets: ReturnType<Assets['start']>;
   investigations: ReturnType<Investigations['start']>;
   machineLearning: ReturnType<MachineLearning['start']>;
+  siemMigrations: ReturnType<SiemMigrations['start']>;
 }