Skip to content

Commit

Permalink
[8.x] [Rules migration] Add functionality to display matched prebuilt…
Browse files Browse the repository at this point in the history
… rules details (elastic#11360) (elastic#203035) (elastic#203360)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Rules migration] Add functionality to display matched prebuilt rules
details (elastic#11360)
(elastic#203035)](elastic#203035)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-08T22:04:39Z","message":"[Rules
migration] Add functionality to display matched prebuilt rules details
(elastic#11360) (elastic#203035)\n\n## Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nThese changes add functionality that allows to
display matched prebuilt\r\nrules details.\r\n\r\n### New
route\r\n\r\nThere is a new
route\r\n`/internal/siem_migrations/rules/{migration_id}/prebuilt_rules`
that\r\nwill return all prebuilt rules matched by translated rules
within a\r\nspecific migration.\r\n\r\n### UI changes\r\n\r\nThe rule
migration details flyout was updated to display matched\r\nprebuilt rule
data in both `Translation` and `Overview`
tabs.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/3da49653-e0ab-4d8b-892e-dd05cf73743b\r\n\r\n###
Other changes\r\n\r\nAlso, as part of this PR, batching of a rule
installation/creation
was\r\nadded.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Sergi Massaneda
<[email protected]>","sha":"b3d6d914b347bb1abc8822131d20de9f20f38ec0","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Threat
Hunting","Team: SecuritySolution","backport:prev-minor"],"title":"[Rules
migration] Add functionality to display matched prebuilt rules details
(elastic#11360)","number":203035,"url":"https://github.com/elastic/kibana/pull/203035","mergeCommit":{"message":"[Rules
migration] Add functionality to display matched prebuilt rules details
(elastic#11360) (elastic#203035)\n\n## Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nThese changes add functionality that allows to
display matched prebuilt\r\nrules details.\r\n\r\n### New
route\r\n\r\nThere is a new
route\r\n`/internal/siem_migrations/rules/{migration_id}/prebuilt_rules`
that\r\nwill return all prebuilt rules matched by translated rules
within a\r\nspecific migration.\r\n\r\n### UI changes\r\n\r\nThe rule
migration details flyout was updated to display matched\r\nprebuilt rule
data in both `Translation` and `Overview`
tabs.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/3da49653-e0ab-4d8b-892e-dd05cf73743b\r\n\r\n###
Other changes\r\n\r\nAlso, as part of this PR, batching of a rule
installation/creation
was\r\nadded.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Sergi Massaneda
<[email protected]>","sha":"b3d6d914b347bb1abc8822131d20de9f20f38ec0"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203035","number":203035,"mergeCommit":{"message":"[Rules
migration] Add functionality to display matched prebuilt rules details
(elastic#11360) (elastic#203035)\n\n## Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nThese changes add functionality that allows to
display matched prebuilt\r\nrules details.\r\n\r\n### New
route\r\n\r\nThere is a new
route\r\n`/internal/siem_migrations/rules/{migration_id}/prebuilt_rules`
that\r\nwill return all prebuilt rules matched by translated rules
within a\r\nspecific migration.\r\n\r\n### UI changes\r\n\r\nThe rule
migration details flyout was updated to display matched\r\nprebuilt rule
data in both `Translation` and `Overview`
tabs.\r\n\r\n\r\nhttps://github.com/user-attachments/assets/3da49653-e0ab-4d8b-892e-dd05cf73743b\r\n\r\n###
Other changes\r\n\r\nAlso, as part of this PR, batching of a rule
installation/creation
was\r\nadded.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>\r\nCo-authored-by:
Sergi Massaneda
<[email protected]>","sha":"b3d6d914b347bb1abc8822131d20de9f20f38ec0"}}]}]
BACKPORT-->

Co-authored-by: Ievgen Sorokopud <[email protected]>
  • Loading branch information
kibanamachine and e40pud authored Dec 8, 2024
1 parent d8e9182 commit bf23999
Show file tree
Hide file tree
Showing 22 changed files with 638 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ import type {
GetRuleMigrationRequestQueryInput,
GetRuleMigrationRequestParamsInput,
GetRuleMigrationResponse,
GetRuleMigrationPrebuiltRulesRequestParamsInput,
GetRuleMigrationPrebuiltRulesResponse,
GetRuleMigrationResourcesRequestQueryInput,
GetRuleMigrationResourcesRequestParamsInput,
GetRuleMigrationResourcesResponse,
Expand Down Expand Up @@ -1478,6 +1480,24 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Retrieves all available prebuilt rules (installed and installable)
*/
async getRuleMigrationPrebuiltRules(props: GetRuleMigrationPrebuiltRulesProps) {
this.log.info(`${new Date().toISOString()} Calling API GetRuleMigrationPrebuiltRules`);
return this.kbnClient
.request<GetRuleMigrationPrebuiltRulesResponse>({
path: replaceParams(
'/internal/siem_migrations/rules/{migration_id}/prebuilt_rules',
props.params
),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '1',
},
method: 'GET',
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Retrieves resources for an existing SIEM rules migration
*/
Expand Down Expand Up @@ -2428,6 +2448,9 @@ export interface GetRuleMigrationProps {
query: GetRuleMigrationRequestQueryInput;
params: GetRuleMigrationRequestParamsInput;
}
export interface GetRuleMigrationPrebuiltRulesProps {
params: GetRuleMigrationPrebuiltRulesRequestParamsInput;
}
export interface GetRuleMigrationResourcesProps {
query: GetRuleMigrationResourcesRequestQueryInput;
params: GetRuleMigrationResourcesRequestParamsInput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const SIEM_RULE_MIGRATION_STOP_PATH = `${SIEM_RULE_MIGRATION_PATH}/stop`
export const SIEM_RULE_MIGRATION_INSTALL_PATH = `${SIEM_RULE_MIGRATION_PATH}/install` as const;
export const SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH =
`${SIEM_RULE_MIGRATION_PATH}/install_translated` as const;
export const SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH =
`${SIEM_RULE_MIGRATION_PATH}/prebuilt_rules` as const;

export const SIEM_RULE_MIGRATION_RESOURCES_PATH = `${SIEM_RULE_MIGRATION_PATH}/resources` as const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
OriginalRule,
RuleMigration,
RuleMigrationTranslationStats,
PrebuiltRuleVersion,
RuleMigrationResourceData,
RuleMigrationResourceType,
RuleMigrationResource,
Expand Down Expand Up @@ -76,6 +77,24 @@ export const GetRuleMigrationResponse = z.object({
total: z.number(),
data: z.array(RuleMigration),
});

export type GetRuleMigrationPrebuiltRulesRequestParams = z.infer<
typeof GetRuleMigrationPrebuiltRulesRequestParams
>;
export const GetRuleMigrationPrebuiltRulesRequestParams = z.object({
migration_id: NonEmptyString,
});
export type GetRuleMigrationPrebuiltRulesRequestParamsInput = z.input<
typeof GetRuleMigrationPrebuiltRulesRequestParams
>;

/**
* The map of prebuilt rules, with the rules id as a key
*/
export type GetRuleMigrationPrebuiltRulesResponse = z.infer<
typeof GetRuleMigrationPrebuiltRulesResponse
>;
export const GetRuleMigrationPrebuiltRulesResponse = z.object({}).catchall(PrebuiltRuleVersion);
export type GetRuleMigrationResourcesRequestQuery = z.infer<
typeof GetRuleMigrationResourcesRequestQuery
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,33 @@ paths:
204:
description: Indicates the migration id was not found running.

/internal/siem_migrations/rules/{migration_id}/prebuilt_rules:
get:
summary: Retrieves all prebuilt rules for a specific migration
operationId: GetRuleMigrationPrebuiltRules
x-codegen-enabled: true
x-internal: true
description: Retrieves all available prebuilt rules (installed and installable)
tags:
- SIEM Rule Migrations
parameters:
- name: migration_id
in: path
required: true
schema:
description: The migration id to retrieve prebuilt rules for
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates prebuilt rules have been retrieved correctly.
content:
application/json:
schema:
type: object
description: The map of prebuilt rules, with the rules id as a key
additionalProperties:
$ref: '../../rule_migration.schema.yaml#/components/schemas/PrebuiltRuleVersion'

# Rule migration resources APIs

/internal/siem_migrations/rules/{migration_id}/resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { z } from '@kbn/zod';

import { NonEmptyString } from '../../api/model/primitives.gen';
import { RuleResponse } from '../../api/detection_engine/model/rule_schema/rule_schemas.gen';

/**
* The original rule vendor identifier.
Expand Down Expand Up @@ -117,6 +118,21 @@ export const ElasticRule = z.object({
export type ElasticRulePartial = z.infer<typeof ElasticRulePartial>;
export const ElasticRulePartial = ElasticRule.partial();

/**
* The prebuilt rule version.
*/
export type PrebuiltRuleVersion = z.infer<typeof PrebuiltRuleVersion>;
export const PrebuiltRuleVersion = z.object({
/**
* The latest available version of prebuilt rule.
*/
target: RuleResponse,
/**
* The currently installed version of prebuilt rule.
*/
current: RuleResponse.optional(),
});

/**
* The rule translation result.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ components:
$ref: '#/components/schemas/ElasticRule'
x-modify: partial

PrebuiltRuleVersion:
type: object
description: The prebuilt rule version.
required:
- target
properties:
target:
description: The latest available version of prebuilt rule.
$ref: '../../../common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'
current:
description: The currently installed version of prebuilt rule.
$ref: '../../../common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml#/components/schemas/RuleResponse'

RuleMigration:
description: The rule migration document object.
allOf:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { Severity } from '../../api/detection_engine';
import { DEFAULT_TRANSLATION_FIELDS, DEFAULT_TRANSLATION_SEVERITY } from '../constants';
import type { ElasticRule, ElasticRulePartial } from '../model/rule_migration.gen';

export type MigrationPrebuiltRule = ElasticRulePartial &
Required<Pick<ElasticRulePartial, 'title' | 'description' | 'prebuilt_rule_id'>>;

export type MigrationCustomRule = ElasticRulePartial &
Required<Pick<ElasticRulePartial, 'title' | 'description' | 'query' | 'query_language'>>;

export const isMigrationPrebuiltRule = (rule?: ElasticRule): rule is MigrationPrebuiltRule =>
!!(rule?.title && rule?.description && rule?.prebuilt_rule_id);

export const isMigrationCustomRule = (rule?: ElasticRule): rule is MigrationCustomRule =>
!isMigrationPrebuiltRule(rule) &&
!!(rule?.title && rule?.description && rule?.query && rule?.query_language);

export const convertMigrationCustomRuleToSecurityRulePayload = (rule: MigrationCustomRule) => {
return {
type: rule.query_language,
language: rule.query_language,
query: rule.query,
name: rule.title,
description: rule.description,

...DEFAULT_TRANSLATION_FIELDS,
severity: (rule.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SIEM_RULE_MIGRATION_START_PATH,
SIEM_RULE_MIGRATION_STATS_PATH,
SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH,
SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH,
} from '../../../../common/siem_migrations/constants';
import type {
CreateRuleMigrationRequestBody,
Expand All @@ -30,6 +31,7 @@ import type {
InstallMigrationRulesResponse,
StartRuleMigrationRequestBody,
GetRuleMigrationStatsResponse,
GetRuleMigrationPrebuiltRulesResponse,
} from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen';

export interface GetRuleMigrationStatsParams {
Expand Down Expand Up @@ -192,3 +194,20 @@ export const installTranslatedMigrationRules = async ({
{ version: '1', signal }
);
};

export interface GetRuleMigrationsPrebuiltRulesParams {
/** `id` of the migration to install rules for */
migrationId: string;
/** Optional AbortSignal for cancelling request */
signal?: AbortSignal;
}
/** Retrieves all prebuilt rules matched within a specific migration. */
export const getRuleMigrationsPrebuiltRules = async ({
migrationId,
signal,
}: GetRuleMigrationsPrebuiltRulesParams): Promise<GetRuleMigrationPrebuiltRulesResponse> => {
return KibanaServices.get().http.get<GetRuleMigrationPrebuiltRulesResponse>(
replaceParams(SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH, { migration_id: migrationId }),
{ version: '1', signal }
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,23 @@ import {
} from '@elastic/eui';
import type { EuiTabbedContentTab, EuiTabbedContentProps, EuiFlyoutProps } from '@elastic/eui';

import {
DEFAULT_TRANSLATION_SEVERITY,
DEFAULT_TRANSLATION_FIELDS,
} from '../../../../../common/siem_migrations/constants';
import type { RuleMigration } from '../../../../../common/siem_migrations/model/rule_migration.gen';
import {
RuleOverviewTab,
useOverviewTabSections,
} from '../../../../detection_engine/rule_management/components/rule_details/rule_overview_tab';
import {
type RuleResponse,
type Severity,
} from '../../../../../common/api/detection_engine/model/rule_schema';
import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema';

import * as i18n from './translations';
import {
DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS,
LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS,
} from './constants';
import { TranslationTab } from './translation_tab';
import {
convertMigrationCustomRuleToSecurityRulePayload,
isMigrationCustomRule,
} from '../../../../../common/siem_migrations/rules/utils';

/*
* Fixes tabs to the top and allows the content to scroll.
Expand All @@ -67,6 +64,7 @@ export const TabContentPadding: FC<PropsWithChildren<unknown>> = ({ children })
interface MigrationRuleDetailsFlyoutProps {
ruleActions?: React.ReactNode;
ruleMigration: RuleMigration;
matchedPrebuiltRule?: RuleResponse;
size?: EuiFlyoutProps['size'];
extraTabs?: EuiTabbedContentTab[];
closeFlyout: () => void;
Expand All @@ -76,38 +74,36 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
({
ruleActions,
ruleMigration,
matchedPrebuiltRule,
size = 'm',
extraTabs = [],
closeFlyout,
}: MigrationRuleDetailsFlyoutProps) => {
const { expandedOverviewSections, toggleOverviewSection } = useOverviewTabSections();

const rule: RuleResponse = useMemo(() => {
const esqlLanguage = ruleMigration.elastic_rule?.query_language ?? 'esql';
return {
type: esqlLanguage,
language: esqlLanguage,
name: ruleMigration.elastic_rule?.title,
description: ruleMigration.elastic_rule?.description,
query: ruleMigration.elastic_rule?.query,

...DEFAULT_TRANSLATION_FIELDS,
severity:
(ruleMigration.elastic_rule?.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY,
} as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter
}, [ruleMigration]);
const rule = useMemo(() => {
if (isMigrationCustomRule(ruleMigration.elastic_rule)) {
return convertMigrationCustomRuleToSecurityRulePayload(
ruleMigration.elastic_rule
) as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter;
}
return matchedPrebuiltRule;
}, [matchedPrebuiltRule, ruleMigration]);

const translationTab: EuiTabbedContentTab = useMemo(
() => ({
id: 'translation',
name: i18n.TRANSLATION_TAB_LABEL,
content: (
<TabContentPadding>
<TranslationTab ruleMigration={ruleMigration} />
<TranslationTab
ruleMigration={ruleMigration}
matchedPrebuiltRule={matchedPrebuiltRule}
/>
</TabContentPadding>
),
}),
[ruleMigration]
[matchedPrebuiltRule, ruleMigration]
);

const overviewTab: EuiTabbedContentTab = useMemo(
Expand All @@ -116,16 +112,18 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
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}
/>
{rule && (
<RuleOverviewTab
rule={rule}
columnWidths={
size === 'l'
? LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS
: DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS
}
expandedOverviewSections={expandedOverviewSections}
toggleOverviewSection={toggleOverviewSection}
/>
)}
</TabContentPadding>
),
}),
Expand Down Expand Up @@ -166,7 +164,9 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
>
<EuiFlyoutHeader>
<EuiTitle size="m">
<h2 id={migrationsRulesFlyoutTitleId}>{rule.name}</h2>
<h2 id={migrationsRulesFlyoutTitleId}>
{rule?.name ?? ruleMigration.original_rule.title}
</h2>
</EuiTitle>
<EuiSpacer size="l" />
</EuiFlyoutHeader>
Expand Down
Loading

0 comments on commit bf23999

Please sign in to comment.