Skip to content

Commit

Permalink
[Rules migration][UI] Basic rule migrations UI (#10820) (#200978)
Browse files Browse the repository at this point in the history
## Summary

[Internal link](elastic/security-team#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](elastic/security-team#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
  • Loading branch information
e40pud authored Nov 22, 2024
1 parent 556edb9 commit a627e01
Show file tree
Hide file tree
Showing 42 changed files with 1,705 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions packages/deeplinks/security/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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-*'];
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/security_solution/public/app/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -53,5 +54,6 @@ const subPluginClasses = {
Assets,
Investigations,
MachineLearning,
SiemMigrations,
};
export { subPluginClasses };
4 changes: 4 additions & 0 deletions x-pack/plugins/security_solution/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
),
};
}

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/security_solution/public/rules/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -106,6 +107,7 @@ export const links: LinkItem = {
}),
],
},
siemMigrationsLinks,
],
categories: [
{
Expand All @@ -116,6 +118,7 @@ export const links: LinkItem = {
SecurityPageName.rules,
SecurityPageName.cloudSecurityPostureBenchmarks,
SecurityPageName.exceptions,
SecurityPageName.siemMigrationsRules,
],
},
{
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/security_solution/public/siem_migrations/index.ts
Original file line number Diff line number Diff line change
@@ -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 : [],
};
}
}
Original file line number Diff line number Diff line change
@@ -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'),
};
35 changes: 35 additions & 0 deletions x-pack/plugins/security_solution/public/siem_migrations/links.ts
Original file line number Diff line number Diff line change
@@ -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',
};
31 changes: 31 additions & 0 deletions x-pack/plugins/security_solution/public/siem_migrations/routes.tsx
Original file line number Diff line number Diff line change
@@ -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,
},
];
Original file line number Diff line number Diff line change
@@ -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,
}
);
};
Original file line number Diff line number Diff line change
@@ -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,
};
Original file line number Diff line number Diff line change
@@ -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,
}
);
};
Original file line number Diff line number Diff line change
@@ -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,
}
);
};
Loading

0 comments on commit a627e01

Please sign in to comment.