Skip to content

Commit

Permalink
[Rules migration] Improvements & fixes (#206658)
Browse files Browse the repository at this point in the history
## Summary

[Internal link](elastic/security-team#10820)
to the feature details

This PR includes next improvements and fixes

### Improvements

1. [PR feedback] Improved filtering:
#206089 (comment)
2. [PR feedback] Use variable instead of massive destructing object:
#206089 (comment)
3. `Upload` missing resources button
4. Show comment as a tooltip within the `Status` column for the failed
rule
![Screenshot 2025-01-15 at 13 34
11](https://github.com/user-attachments/assets/4c25aeab-3193-490b-90eb-ccc4f4ef8a9f)

### Fixes

1. Better error handling
2. Fetch all existing rules (via batches search) instead of 10k limit

> [!NOTE]  
> This feature needs `siemMigrationsEnabled` experimental flag enabled
to work.

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
e40pud and kibanamachine authored Jan 17, 2025
1 parent 5ab8a52 commit bd19bcc
Show file tree
Hide file tree
Showing 32 changed files with 262 additions and 507 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,6 @@ import type {
InstallMigrationRulesRequestParamsInput,
InstallMigrationRulesRequestBodyInput,
InstallMigrationRulesResponse,
InstallTranslatedMigrationRulesRequestParamsInput,
InstallTranslatedMigrationRulesResponse,
StartRuleMigrationRequestParamsInput,
StartRuleMigrationRequestBodyInput,
StartRuleMigrationResponse,
Expand Down Expand Up @@ -1736,24 +1734,6 @@ finalize it.
})
.catch(catchAxiosErrorFormatAndThrow);
}
/**
* Installs all translated migration rules
*/
async installTranslatedMigrationRules(props: InstallTranslatedMigrationRulesProps) {
this.log.info(`${new Date().toISOString()} Calling API InstallTranslatedMigrationRules`);
return this.kbnClient
.request<InstallTranslatedMigrationRulesResponse>({
path: replaceParams(
'/internal/siem_migrations/rules/{migration_id}/install_translated',
props.params
),
headers: {
[ELASTIC_HTTP_VERSION_HEADER]: '1',
},
method: 'POST',
})
.catch(catchAxiosErrorFormatAndThrow);
}
async internalUploadAssetCriticalityRecords(props: InternalUploadAssetCriticalityRecordsProps) {
this.log.info(`${new Date().toISOString()} Calling API InternalUploadAssetCriticalityRecords`);
return this.kbnClient
Expand Down Expand Up @@ -2541,9 +2521,6 @@ export interface InstallMigrationRulesProps {
export interface InstallPrepackedTimelinesProps {
body: InstallPrepackedTimelinesRequestBodyInput;
}
export interface InstallTranslatedMigrationRulesProps {
params: InstallTranslatedMigrationRulesRequestParamsInput;
}
export interface InternalUploadAssetCriticalityRecordsProps {
attachment: FormData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export const SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH =
`${SIEM_RULE_MIGRATION_PATH}/translation_stats` as const;
export const SIEM_RULE_MIGRATION_STOP_PATH = `${SIEM_RULE_MIGRATION_PATH}/stop` as const;
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;

Expand Down Expand Up @@ -64,16 +62,3 @@ export const DEFAULT_TRANSLATION_FIELDS = {
to: 'now',
interval: '5m',
} as const;

export enum AuthorFilter {
ELASTIC = 'elastic',
CUSTOM = 'custom',
}

export enum StatusFilter {
INSTALLED = 'installed',
TRANSLATED = 'translated',
PARTIALLY_TRANSLATED = 'partially_translated',
UNTRANSLATABLE = 'untranslatable',
FAILED = 'failed',
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export type InstallMigrationRulesRequestParamsInput = z.input<

export type InstallMigrationRulesRequestBody = z.infer<typeof InstallMigrationRulesRequestBody>;
export const InstallMigrationRulesRequestBody = z.object({
ids: z.array(NonEmptyString),
ids: z.array(NonEmptyString).optional(),
/**
* Indicates whether installed rules should be enabled
*/
Expand All @@ -206,29 +206,9 @@ export type InstallMigrationRulesRequestBodyInput = z.input<
export type InstallMigrationRulesResponse = z.infer<typeof InstallMigrationRulesResponse>;
export const InstallMigrationRulesResponse = z.object({
/**
* Indicates rules migrations have been installed.
* Indicates the number of successfully installed migration rules.
*/
installed: z.boolean(),
});

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

export type InstallTranslatedMigrationRulesResponse = z.infer<
typeof InstallTranslatedMigrationRulesResponse
>;
export const InstallTranslatedMigrationRulesResponse = z.object({
/**
* Indicates rules migrations have been installed.
*/
installed: z.boolean(),
installed: z.number(),
});

export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,6 @@ paths:
application/json:
schema:
type: object
required:
- ids
properties:
ids:
type: array
Expand All @@ -259,37 +257,8 @@ paths:
- installed
properties:
installed:
type: boolean
description: Indicates rules migrations have been installed.

/internal/siem_migrations/rules/{migration_id}/install_translated:
post:
summary: Installs all translated migration rules
operationId: InstallTranslatedMigrationRules
x-codegen-enabled: true
description: Installs all translated migration rules
tags:
- SIEM Rule Migrations
parameters:
- name: migration_id
in: path
required: true
schema:
description: The migration id to install translated rules for
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
responses:
200:
description: Indicates rules migrations have been installed correctly.
content:
application/json:
schema:
type: object
required:
- installed
properties:
installed:
type: boolean
description: Indicates rules migrations have been installed.
type: number
description: Indicates the number of successfully installed migration rules.

/internal/siem_migrations/rules/{migration_id}/start:
put:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 { SiemMigrationStatus } from './constants';

export interface RuleMigrationFilters {
status?: SiemMigrationStatus | SiemMigrationStatus[];
ids?: string[];
installed?: boolean;
installable?: boolean;
prebuilt?: boolean;
failed?: boolean;
fullyTranslated?: boolean;
partiallyTranslated?: boolean;
untranslatable?: boolean;
searchTerm?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { replaceParams } from '@kbn/openapi-common/shared';

import type { RuleMigrationFilters } from '../../../../common/siem_migrations/types';
import type { UpdateRuleMigrationData } from '../../../../common/siem_migrations/model/rule_migration.gen';
import type { LangSmithOptions } from '../../../../common/siem_migrations/model/common.gen';
import { KibanaServices } from '../../../common/lib/kibana';
Expand All @@ -15,7 +16,6 @@ import type { SiemMigrationRetryFilter } from '../../../../common/siem_migration
import {
SIEM_RULE_MIGRATIONS_PATH,
SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH,
SIEM_RULE_MIGRATION_INSTALL_PATH,
SIEM_RULE_MIGRATION_PATH,
SIEM_RULE_MIGRATION_START_PATH,
Expand All @@ -32,7 +32,6 @@ import type {
GetAllStatsRuleMigrationResponse,
GetRuleMigrationResponse,
GetRuleMigrationTranslationStatsResponse,
InstallTranslatedMigrationRulesResponse,
InstallMigrationRulesResponse,
StartRuleMigrationRequestBody,
GetRuleMigrationStatsResponse,
Expand Down Expand Up @@ -176,22 +175,8 @@ export interface GetRuleMigrationParams {
sortField?: string;
/** Optional direction to sort results by */
sortDirection?: 'asc' | 'desc';
/** Optional search term to filter documents */
searchTerm?: string;
/** Optional rules ids to filter documents */
ids?: string[];
/** Optional attribute to retrieve prebuilt migration rules */
isPrebuilt?: boolean;
/** Optional attribute to retrieve installed migration rules */
isInstalled?: boolean;
/** Optional attribute to retrieve fully translated migration rules */
isFullyTranslated?: boolean;
/** Optional attribute to retrieve partially translated migration rules */
isPartiallyTranslated?: boolean;
/** Optional attribute to retrieve untranslated migration rules */
isUntranslatable?: boolean;
/** Optional attribute to retrieve failed migration rules */
isFailed?: boolean;
/** Optional parameter to filter documents */
filters?: RuleMigrationFilters;
/** Optional AbortSignal for cancelling request */
signal?: AbortSignal;
}
Expand All @@ -202,14 +187,7 @@ export const getRuleMigrations = async ({
perPage,
sortField,
sortDirection,
searchTerm,
ids,
isPrebuilt,
isInstalled,
isFullyTranslated,
isPartiallyTranslated,
isUntranslatable,
isFailed,
filters,
signal,
}: GetRuleMigrationParams): Promise<GetRuleMigrationResponse> => {
return KibanaServices.get().http.get<GetRuleMigrationResponse>(
Expand All @@ -221,14 +199,14 @@ export const getRuleMigrations = async ({
per_page: perPage,
sort_field: sortField,
sort_direction: sortDirection,
search_term: searchTerm,
ids,
is_prebuilt: isPrebuilt,
is_installed: isInstalled,
is_fully_translated: isFullyTranslated,
is_partially_translated: isPartiallyTranslated,
is_untranslatable: isUntranslatable,
is_failed: isFailed,
search_term: filters?.searchTerm,
ids: filters?.ids,
is_prebuilt: filters?.prebuilt,
is_installed: filters?.installed,
is_fully_translated: filters?.fullyTranslated,
is_partially_translated: filters?.partiallyTranslated,
is_untranslatable: filters?.untranslatable,
is_failed: filters?.failed,
},
signal,
}
Expand Down Expand Up @@ -258,7 +236,7 @@ export interface InstallRulesParams {
/** `id` of the migration to install rules for */
migrationId: string;
/** The rule ids to install */
ids: string[];
ids?: string[];
/** Optional indicator to enable the installed rule */
enabled?: boolean;
/** Optional AbortSignal for cancelling request */
Expand All @@ -277,23 +255,6 @@ export const installMigrationRules = async ({
);
};

export interface InstallTranslatedRulesParams {
/** `id` of the migration to install rules for */
migrationId: string;
/** Optional AbortSignal for cancelling request */
signal?: AbortSignal;
}
/** Installs all the translated rules for a specific migration. */
export const installTranslatedMigrationRules = async ({
migrationId,
signal,
}: InstallTranslatedRulesParams): Promise<InstallTranslatedMigrationRulesResponse> => {
return KibanaServices.get().http.post<InstallTranslatedMigrationRulesResponse>(
replaceParams(SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH, { migration_id: migrationId }),
{ version: '1', signal }
);
};

export interface GetRuleMigrationsPrebuiltRulesParams {
/** `id` of the migration to install rules for */
migrationId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp

const handleTranslationUpdate = useCallback(
async (ruleName: string, ruleQuery: string) => {
if (!ruleMigration || isLoading) {
if (isLoading) {
return;
}
setIsUpdating(true);
Expand Down Expand Up @@ -139,13 +139,11 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
name: i18n.TRANSLATION_TAB_LABEL,
content: (
<TabContentPadding>
{ruleMigration && (
<TranslationTab
ruleMigration={ruleMigration}
matchedPrebuiltRule={matchedPrebuiltRule}
onTranslationUpdate={handleTranslationUpdate}
/>
)}
<TranslationTab
ruleMigration={ruleMigration}
matchedPrebuiltRule={matchedPrebuiltRule}
onTranslationUpdate={handleTranslationUpdate}
/>
</TabContentPadding>
),
}),
Expand Down Expand Up @@ -242,7 +240,7 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
<EuiTitle size="m">
<h2 id={migrationsRulesFlyoutTitleId}>
{ruleDetailsToOverview?.name ??
ruleMigration?.original_rule.title ??
ruleMigration.original_rule.title ??
i18n.UNKNOWN_MIGRATION_RULE_TITLE}
</h2>
</EuiTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import type { EuiCommentProps } from '@elastic/eui';
import { EuiCommentList, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
import moment from 'moment';
import { AssistantAvatar } from '@kbn/ai-assistant-icon';
import {
RuleMigrationStatusEnum,
type RuleMigration,
} from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
import { type RuleMigration } from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
import { RuleTranslationResult } from '../../../../../../../common/siem_migrations/constants';
import * as i18n from './translations';

interface SummaryTabProps {
Expand All @@ -33,8 +31,8 @@ export const SummaryTab: React.FC<SummaryTabProps> = React.memo(({ ruleMigration
timelineAvatarAriaLabel: i18n.ASSISTANT_USERNAME,
timelineAvatar: <AssistantAvatar name="machine" size="l" color="subdued" />,
event:
ruleMigration.status === RuleMigrationStatusEnum.failed
? i18n.COMMENT_EVENT_FAILED
ruleMigration.translation_result === RuleTranslationResult.UNTRANSLATABLE
? i18n.COMMENT_EVENT_UNTRANSLATABLE
: i18n.COMMENT_EVENT_TRANSLATED,
timestamp,
children: <EuiMarkdownFormat textSize="s">{comment}</EuiMarkdownFormat>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const COMMENT_EVENT_TRANSLATED = i18n.translate(
}
);

export const COMMENT_EVENT_FAILED = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.failedLabel',
export const COMMENT_EVENT_UNTRANSLATABLE = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.untranslatableLabel',
{
defaultMessage: 'failed to translate',
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const CALLOUT_PARTIALLY_TRANSLATED_RULE_DESCRIPTION = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutDescription',
{
defaultMessage:
'To save this rule, finish the query and define its severity. If you need help, please contact Elastic support.',
'To save this rule, finish writing the query. If you need help, please contact Elastic support.',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { useCallback, useMemo, useState } from 'react';
import type { EuiSelectableOption } from '@elastic/eui';
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
import type { EuiSelectableOnChangeEvent } from '@elastic/eui/src/components/selectable/selectable';
import { AuthorFilter } from '../../../../../../common/siem_migrations/constants';
import { AuthorFilter } from '../../../types';
import * as i18n from './translations';

const AUTHOR_FILTER_POPOVER_WIDTH = 150;
Expand Down
Loading

0 comments on commit bd19bcc

Please sign in to comment.