mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Rules migration] Improvements & fixes (#206658)
## Summary [Internal link](https://github.com/elastic/security-team/issues/10820) to the feature details This PR includes next improvements and fixes ### Improvements 1. [PR feedback] Improved filtering: https://github.com/elastic/kibana/pull/206089#discussion_r1913256593 2. [PR feedback] Use variable instead of massive destructing object: https://github.com/elastic/kibana/pull/206089#discussion_r1913268303 3. `Upload` missing resources button 4. Show comment as a tooltip within the `Status` column for the failed rule  ### 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 <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5ab8a52187
commit
bd19bcc005
32 changed files with 264 additions and 509 deletions
|
@ -386,8 +386,6 @@ import type {
|
||||||
InstallMigrationRulesRequestParamsInput,
|
InstallMigrationRulesRequestParamsInput,
|
||||||
InstallMigrationRulesRequestBodyInput,
|
InstallMigrationRulesRequestBodyInput,
|
||||||
InstallMigrationRulesResponse,
|
InstallMigrationRulesResponse,
|
||||||
InstallTranslatedMigrationRulesRequestParamsInput,
|
|
||||||
InstallTranslatedMigrationRulesResponse,
|
|
||||||
StartRuleMigrationRequestParamsInput,
|
StartRuleMigrationRequestParamsInput,
|
||||||
StartRuleMigrationRequestBodyInput,
|
StartRuleMigrationRequestBodyInput,
|
||||||
StartRuleMigrationResponse,
|
StartRuleMigrationResponse,
|
||||||
|
@ -1736,24 +1734,6 @@ finalize it.
|
||||||
})
|
})
|
||||||
.catch(catchAxiosErrorFormatAndThrow);
|
.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) {
|
async internalUploadAssetCriticalityRecords(props: InternalUploadAssetCriticalityRecordsProps) {
|
||||||
this.log.info(`${new Date().toISOString()} Calling API InternalUploadAssetCriticalityRecords`);
|
this.log.info(`${new Date().toISOString()} Calling API InternalUploadAssetCriticalityRecords`);
|
||||||
return this.kbnClient
|
return this.kbnClient
|
||||||
|
@ -2541,9 +2521,6 @@ export interface InstallMigrationRulesProps {
|
||||||
export interface InstallPrepackedTimelinesProps {
|
export interface InstallPrepackedTimelinesProps {
|
||||||
body: InstallPrepackedTimelinesRequestBodyInput;
|
body: InstallPrepackedTimelinesRequestBodyInput;
|
||||||
}
|
}
|
||||||
export interface InstallTranslatedMigrationRulesProps {
|
|
||||||
params: InstallTranslatedMigrationRulesRequestParamsInput;
|
|
||||||
}
|
|
||||||
export interface InternalUploadAssetCriticalityRecordsProps {
|
export interface InternalUploadAssetCriticalityRecordsProps {
|
||||||
attachment: FormData;
|
attachment: FormData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ export const SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH =
|
||||||
`${SIEM_RULE_MIGRATION_PATH}/translation_stats` as const;
|
`${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_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_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 =
|
export const SIEM_RULE_MIGRATIONS_PREBUILT_RULES_PATH =
|
||||||
`${SIEM_RULE_MIGRATION_PATH}/prebuilt_rules` as const;
|
`${SIEM_RULE_MIGRATION_PATH}/prebuilt_rules` as const;
|
||||||
|
|
||||||
|
@ -64,16 +62,3 @@ export const DEFAULT_TRANSLATION_FIELDS = {
|
||||||
to: 'now',
|
to: 'now',
|
||||||
interval: '5m',
|
interval: '5m',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export enum AuthorFilter {
|
|
||||||
ELASTIC = 'elastic',
|
|
||||||
CUSTOM = 'custom',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum StatusFilter {
|
|
||||||
INSTALLED = 'installed',
|
|
||||||
TRANSLATED = 'translated',
|
|
||||||
PARTIALLY_TRANSLATED = 'partially_translated',
|
|
||||||
UNTRANSLATABLE = 'untranslatable',
|
|
||||||
FAILED = 'failed',
|
|
||||||
}
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ export type InstallMigrationRulesRequestParamsInput = z.input<
|
||||||
|
|
||||||
export type InstallMigrationRulesRequestBody = z.infer<typeof InstallMigrationRulesRequestBody>;
|
export type InstallMigrationRulesRequestBody = z.infer<typeof InstallMigrationRulesRequestBody>;
|
||||||
export const InstallMigrationRulesRequestBody = z.object({
|
export const InstallMigrationRulesRequestBody = z.object({
|
||||||
ids: z.array(NonEmptyString),
|
ids: z.array(NonEmptyString).optional(),
|
||||||
/**
|
/**
|
||||||
* Indicates whether installed rules should be enabled
|
* Indicates whether installed rules should be enabled
|
||||||
*/
|
*/
|
||||||
|
@ -206,29 +206,9 @@ export type InstallMigrationRulesRequestBodyInput = z.input<
|
||||||
export type InstallMigrationRulesResponse = z.infer<typeof InstallMigrationRulesResponse>;
|
export type InstallMigrationRulesResponse = z.infer<typeof InstallMigrationRulesResponse>;
|
||||||
export const InstallMigrationRulesResponse = z.object({
|
export const InstallMigrationRulesResponse = z.object({
|
||||||
/**
|
/**
|
||||||
* Indicates rules migrations have been installed.
|
* Indicates the number of successfully installed migration rules.
|
||||||
*/
|
*/
|
||||||
installed: z.boolean(),
|
installed: z.number(),
|
||||||
});
|
|
||||||
|
|
||||||
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(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
|
export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
|
||||||
|
|
|
@ -237,8 +237,6 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required:
|
|
||||||
- ids
|
|
||||||
properties:
|
properties:
|
||||||
ids:
|
ids:
|
||||||
type: array
|
type: array
|
||||||
|
@ -259,37 +257,8 @@ paths:
|
||||||
- installed
|
- installed
|
||||||
properties:
|
properties:
|
||||||
installed:
|
installed:
|
||||||
type: boolean
|
type: number
|
||||||
description: Indicates rules migrations have been installed.
|
description: Indicates the number of successfully installed migration rules.
|
||||||
|
|
||||||
/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.
|
|
||||||
|
|
||||||
/internal/siem_migrations/rules/{migration_id}/start:
|
/internal/siem_migrations/rules/{migration_id}/start:
|
||||||
put:
|
put:
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import { replaceParams } from '@kbn/openapi-common/shared';
|
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 { UpdateRuleMigrationData } from '../../../../common/siem_migrations/model/rule_migration.gen';
|
||||||
import type { LangSmithOptions } from '../../../../common/siem_migrations/model/common.gen';
|
import type { LangSmithOptions } from '../../../../common/siem_migrations/model/common.gen';
|
||||||
import { KibanaServices } from '../../../common/lib/kibana';
|
import { KibanaServices } from '../../../common/lib/kibana';
|
||||||
|
@ -15,7 +16,6 @@ import type { SiemMigrationRetryFilter } from '../../../../common/siem_migration
|
||||||
import {
|
import {
|
||||||
SIEM_RULE_MIGRATIONS_PATH,
|
SIEM_RULE_MIGRATIONS_PATH,
|
||||||
SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
|
SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
|
||||||
SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH,
|
|
||||||
SIEM_RULE_MIGRATION_INSTALL_PATH,
|
SIEM_RULE_MIGRATION_INSTALL_PATH,
|
||||||
SIEM_RULE_MIGRATION_PATH,
|
SIEM_RULE_MIGRATION_PATH,
|
||||||
SIEM_RULE_MIGRATION_START_PATH,
|
SIEM_RULE_MIGRATION_START_PATH,
|
||||||
|
@ -32,7 +32,6 @@ import type {
|
||||||
GetAllStatsRuleMigrationResponse,
|
GetAllStatsRuleMigrationResponse,
|
||||||
GetRuleMigrationResponse,
|
GetRuleMigrationResponse,
|
||||||
GetRuleMigrationTranslationStatsResponse,
|
GetRuleMigrationTranslationStatsResponse,
|
||||||
InstallTranslatedMigrationRulesResponse,
|
|
||||||
InstallMigrationRulesResponse,
|
InstallMigrationRulesResponse,
|
||||||
StartRuleMigrationRequestBody,
|
StartRuleMigrationRequestBody,
|
||||||
GetRuleMigrationStatsResponse,
|
GetRuleMigrationStatsResponse,
|
||||||
|
@ -176,22 +175,8 @@ export interface GetRuleMigrationParams {
|
||||||
sortField?: string;
|
sortField?: string;
|
||||||
/** Optional direction to sort results by */
|
/** Optional direction to sort results by */
|
||||||
sortDirection?: 'asc' | 'desc';
|
sortDirection?: 'asc' | 'desc';
|
||||||
/** Optional search term to filter documents */
|
/** Optional parameter to filter documents */
|
||||||
searchTerm?: string;
|
filters?: RuleMigrationFilters;
|
||||||
/** 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 AbortSignal for cancelling request */
|
/** Optional AbortSignal for cancelling request */
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}
|
}
|
||||||
|
@ -202,14 +187,7 @@ export const getRuleMigrations = async ({
|
||||||
perPage,
|
perPage,
|
||||||
sortField,
|
sortField,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
searchTerm,
|
filters,
|
||||||
ids,
|
|
||||||
isPrebuilt,
|
|
||||||
isInstalled,
|
|
||||||
isFullyTranslated,
|
|
||||||
isPartiallyTranslated,
|
|
||||||
isUntranslatable,
|
|
||||||
isFailed,
|
|
||||||
signal,
|
signal,
|
||||||
}: GetRuleMigrationParams): Promise<GetRuleMigrationResponse> => {
|
}: GetRuleMigrationParams): Promise<GetRuleMigrationResponse> => {
|
||||||
return KibanaServices.get().http.get<GetRuleMigrationResponse>(
|
return KibanaServices.get().http.get<GetRuleMigrationResponse>(
|
||||||
|
@ -221,14 +199,14 @@ export const getRuleMigrations = async ({
|
||||||
per_page: perPage,
|
per_page: perPage,
|
||||||
sort_field: sortField,
|
sort_field: sortField,
|
||||||
sort_direction: sortDirection,
|
sort_direction: sortDirection,
|
||||||
search_term: searchTerm,
|
search_term: filters?.searchTerm,
|
||||||
ids,
|
ids: filters?.ids,
|
||||||
is_prebuilt: isPrebuilt,
|
is_prebuilt: filters?.prebuilt,
|
||||||
is_installed: isInstalled,
|
is_installed: filters?.installed,
|
||||||
is_fully_translated: isFullyTranslated,
|
is_fully_translated: filters?.fullyTranslated,
|
||||||
is_partially_translated: isPartiallyTranslated,
|
is_partially_translated: filters?.partiallyTranslated,
|
||||||
is_untranslatable: isUntranslatable,
|
is_untranslatable: filters?.untranslatable,
|
||||||
is_failed: isFailed,
|
is_failed: filters?.failed,
|
||||||
},
|
},
|
||||||
signal,
|
signal,
|
||||||
}
|
}
|
||||||
|
@ -258,7 +236,7 @@ export interface InstallRulesParams {
|
||||||
/** `id` of the migration to install rules for */
|
/** `id` of the migration to install rules for */
|
||||||
migrationId: string;
|
migrationId: string;
|
||||||
/** The rule ids to install */
|
/** The rule ids to install */
|
||||||
ids: string[];
|
ids?: string[];
|
||||||
/** Optional indicator to enable the installed rule */
|
/** Optional indicator to enable the installed rule */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** Optional AbortSignal for cancelling request */
|
/** Optional AbortSignal for cancelling request */
|
||||||
|
@ -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 {
|
export interface GetRuleMigrationsPrebuiltRulesParams {
|
||||||
/** `id` of the migration to install rules for */
|
/** `id` of the migration to install rules for */
|
||||||
migrationId: string;
|
migrationId: string;
|
||||||
|
|
|
@ -101,7 +101,7 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
|
||||||
|
|
||||||
const handleTranslationUpdate = useCallback(
|
const handleTranslationUpdate = useCallback(
|
||||||
async (ruleName: string, ruleQuery: string) => {
|
async (ruleName: string, ruleQuery: string) => {
|
||||||
if (!ruleMigration || isLoading) {
|
if (isLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
|
@ -139,13 +139,11 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
|
||||||
name: i18n.TRANSLATION_TAB_LABEL,
|
name: i18n.TRANSLATION_TAB_LABEL,
|
||||||
content: (
|
content: (
|
||||||
<TabContentPadding>
|
<TabContentPadding>
|
||||||
{ruleMigration && (
|
|
||||||
<TranslationTab
|
<TranslationTab
|
||||||
ruleMigration={ruleMigration}
|
ruleMigration={ruleMigration}
|
||||||
matchedPrebuiltRule={matchedPrebuiltRule}
|
matchedPrebuiltRule={matchedPrebuiltRule}
|
||||||
onTranslationUpdate={handleTranslationUpdate}
|
onTranslationUpdate={handleTranslationUpdate}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</TabContentPadding>
|
</TabContentPadding>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
@ -242,7 +240,7 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
|
||||||
<EuiTitle size="m">
|
<EuiTitle size="m">
|
||||||
<h2 id={migrationsRulesFlyoutTitleId}>
|
<h2 id={migrationsRulesFlyoutTitleId}>
|
||||||
{ruleDetailsToOverview?.name ??
|
{ruleDetailsToOverview?.name ??
|
||||||
ruleMigration?.original_rule.title ??
|
ruleMigration.original_rule.title ??
|
||||||
i18n.UNKNOWN_MIGRATION_RULE_TITLE}
|
i18n.UNKNOWN_MIGRATION_RULE_TITLE}
|
||||||
</h2>
|
</h2>
|
||||||
</EuiTitle>
|
</EuiTitle>
|
||||||
|
|
|
@ -10,10 +10,8 @@ import type { EuiCommentProps } from '@elastic/eui';
|
||||||
import { EuiCommentList, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
import { EuiCommentList, EuiMarkdownFormat, EuiSpacer } from '@elastic/eui';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { AssistantAvatar } from '@kbn/ai-assistant-icon';
|
import { AssistantAvatar } from '@kbn/ai-assistant-icon';
|
||||||
import {
|
import { type RuleMigration } from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||||
RuleMigrationStatusEnum,
|
import { RuleTranslationResult } from '../../../../../../../common/siem_migrations/constants';
|
||||||
type RuleMigration,
|
|
||||||
} from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
|
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
|
|
||||||
interface SummaryTabProps {
|
interface SummaryTabProps {
|
||||||
|
@ -33,8 +31,8 @@ export const SummaryTab: React.FC<SummaryTabProps> = React.memo(({ ruleMigration
|
||||||
timelineAvatarAriaLabel: i18n.ASSISTANT_USERNAME,
|
timelineAvatarAriaLabel: i18n.ASSISTANT_USERNAME,
|
||||||
timelineAvatar: <AssistantAvatar name="machine" size="l" color="subdued" />,
|
timelineAvatar: <AssistantAvatar name="machine" size="l" color="subdued" />,
|
||||||
event:
|
event:
|
||||||
ruleMigration.status === RuleMigrationStatusEnum.failed
|
ruleMigration.translation_result === RuleTranslationResult.UNTRANSLATABLE
|
||||||
? i18n.COMMENT_EVENT_FAILED
|
? i18n.COMMENT_EVENT_UNTRANSLATABLE
|
||||||
: i18n.COMMENT_EVENT_TRANSLATED,
|
: i18n.COMMENT_EVENT_TRANSLATED,
|
||||||
timestamp,
|
timestamp,
|
||||||
children: <EuiMarkdownFormat textSize="s">{comment}</EuiMarkdownFormat>,
|
children: <EuiMarkdownFormat textSize="s">{comment}</EuiMarkdownFormat>,
|
||||||
|
|
|
@ -28,8 +28,8 @@ export const COMMENT_EVENT_TRANSLATED = i18n.translate(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const COMMENT_EVENT_FAILED = i18n.translate(
|
export const COMMENT_EVENT_UNTRANSLATABLE = i18n.translate(
|
||||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.failedLabel',
|
'xpack.securitySolution.siemMigrations.rules.translationDetails.summaryTab.commentEvent.untranslatableLabel',
|
||||||
{
|
{
|
||||||
defaultMessage: 'failed to translate',
|
defaultMessage: 'failed to translate',
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ export const CALLOUT_PARTIALLY_TRANSLATED_RULE_DESCRIPTION = i18n.translate(
|
||||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutDescription',
|
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutDescription',
|
||||||
{
|
{
|
||||||
defaultMessage:
|
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.',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import type { EuiSelectableOption } from '@elastic/eui';
|
import type { EuiSelectableOption } from '@elastic/eui';
|
||||||
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
|
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
|
||||||
import type { EuiSelectableOnChangeEvent } from '@elastic/eui/src/components/selectable/selectable';
|
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';
|
import * as i18n from './translations';
|
||||||
|
|
||||||
const AUTHOR_FILTER_POPOVER_WIDTH = 150;
|
const AUTHOR_FILTER_POPOVER_WIDTH = 150;
|
||||||
|
|
|
@ -7,18 +7,10 @@
|
||||||
|
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { EuiFilterGroup } from '@elastic/eui';
|
import { EuiFilterGroup } from '@elastic/eui';
|
||||||
import type {
|
import type { AuthorFilter, FilterOptions, StatusFilter } from '../../../types';
|
||||||
AuthorFilter,
|
|
||||||
StatusFilter,
|
|
||||||
} from '../../../../../../common/siem_migrations/constants';
|
|
||||||
import { StatusFilterButton } from './status';
|
import { StatusFilterButton } from './status';
|
||||||
import { AuthorFilterButton } from './author';
|
import { AuthorFilterButton } from './author';
|
||||||
|
|
||||||
export interface FilterOptions {
|
|
||||||
status?: StatusFilter;
|
|
||||||
author?: AuthorFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MigrationRulesFilterProps {
|
export interface MigrationRulesFilterProps {
|
||||||
filterOptions?: FilterOptions;
|
filterOptions?: FilterOptions;
|
||||||
onFilterOptionsChanged: (filterOptions?: FilterOptions) => void;
|
onFilterOptionsChanged: (filterOptions?: FilterOptions) => void;
|
||||||
|
|
|
@ -9,12 +9,10 @@ import React, { useCallback, useState } from 'react';
|
||||||
import type { EuiSelectableOption } from '@elastic/eui';
|
import type { EuiSelectableOption } from '@elastic/eui';
|
||||||
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
|
import { EuiFilterButton, EuiPopover, EuiSelectable } from '@elastic/eui';
|
||||||
import type { EuiSelectableOnChangeEvent } from '@elastic/eui/src/components/selectable/selectable';
|
import type { EuiSelectableOnChangeEvent } from '@elastic/eui/src/components/selectable/selectable';
|
||||||
import {
|
import { RuleTranslationResult } from '../../../../../../common/siem_migrations/constants';
|
||||||
RuleTranslationResult,
|
|
||||||
StatusFilter,
|
|
||||||
} from '../../../../../../common/siem_migrations/constants';
|
|
||||||
import * as i18n from './translations';
|
|
||||||
import { convertTranslationResultIntoText } from '../../../utils/translation_results';
|
import { convertTranslationResultIntoText } from '../../../utils/translation_results';
|
||||||
|
import { StatusFilter } from '../../../types';
|
||||||
|
import * as i18n from './translations';
|
||||||
|
|
||||||
const STATUS_FILTER_POPOVER_WIDTH = 250;
|
const STATUS_FILTER_POPOVER_WIDTH = 250;
|
||||||
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 { AuthorFilter, StatusFilter } from '../../../../../common/siem_migrations/constants';
|
|
||||||
import type { FilterOptions } from './filters';
|
|
||||||
|
|
||||||
export const convertFilterOptions = (filterOptions?: FilterOptions) => {
|
|
||||||
return {
|
|
||||||
...(filterOptions?.author === AuthorFilter.ELASTIC ? { isPrebuilt: true } : {}),
|
|
||||||
...(filterOptions?.author === AuthorFilter.CUSTOM ? { isPrebuilt: false } : {}),
|
|
||||||
...(filterOptions?.status === StatusFilter.FAILED ? { isFailed: true } : {}),
|
|
||||||
...(filterOptions?.status === StatusFilter.INSTALLED ? { isInstalled: true } : {}),
|
|
||||||
...(filterOptions?.status === StatusFilter.TRANSLATED
|
|
||||||
? { isInstalled: false, isFullyTranslated: true }
|
|
||||||
: {}),
|
|
||||||
...(filterOptions?.status === StatusFilter.PARTIALLY_TRANSLATED
|
|
||||||
? { isPartiallyTranslated: true }
|
|
||||||
: {}),
|
|
||||||
...(filterOptions?.status === StatusFilter.UNTRANSLATABLE ? { isUntranslatable: true } : {}),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -27,7 +27,6 @@ import { useMigrationRulesTableColumns } from '../../hooks/use_migration_rules_t
|
||||||
import { useMigrationRuleDetailsFlyout } from '../../hooks/use_migration_rule_preview_flyout';
|
import { useMigrationRuleDetailsFlyout } from '../../hooks/use_migration_rule_preview_flyout';
|
||||||
import { useInstallMigrationRules } from '../../logic/use_install_migration_rules';
|
import { useInstallMigrationRules } from '../../logic/use_install_migration_rules';
|
||||||
import { useGetMigrationRules } from '../../logic/use_get_migration_rules';
|
import { useGetMigrationRules } from '../../logic/use_get_migration_rules';
|
||||||
import { useInstallTranslatedMigrationRules } from '../../logic/use_install_translated_migration_rules';
|
|
||||||
import { useGetMigrationTranslationStats } from '../../logic/use_get_migration_translation_stats';
|
import { useGetMigrationTranslationStats } from '../../logic/use_get_migration_translation_stats';
|
||||||
import { useGetMigrationPrebuiltRules } from '../../logic/use_get_migration_prebuilt_rules';
|
import { useGetMigrationPrebuiltRules } from '../../logic/use_get_migration_prebuilt_rules';
|
||||||
import * as logicI18n from '../../logic/translations';
|
import * as logicI18n from '../../logic/translations';
|
||||||
|
@ -39,9 +38,9 @@ import {
|
||||||
} from '../../../../../common/siem_migrations/constants';
|
} from '../../../../../common/siem_migrations/constants';
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
import { useStartMigration } from '../../service/hooks/use_start_migration';
|
import { useStartMigration } from '../../service/hooks/use_start_migration';
|
||||||
import type { FilterOptions } from './filters';
|
import type { FilterOptions } from '../../types';
|
||||||
import { MigrationRulesFilter } from './filters';
|
import { MigrationRulesFilter } from './filters';
|
||||||
import { convertFilterOptions } from './helpers';
|
import { convertFilterOptions } from './utils/filters';
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE = 10;
|
const DEFAULT_PAGE_SIZE = 10;
|
||||||
const DEFAULT_SORT_FIELD = 'translation_result';
|
const DEFAULT_SORT_FIELD = 'translation_result';
|
||||||
|
@ -100,8 +99,10 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem
|
||||||
perPage: pageSize,
|
perPage: pageSize,
|
||||||
sortField,
|
sortField,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
|
filters: {
|
||||||
searchTerm,
|
searchTerm,
|
||||||
...convertFilterOptions(filterOptions),
|
...convertFilterOptions(filterOptions),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [selectedRuleMigrations, setSelectedRuleMigrations] = useState<RuleMigration[]>([]);
|
const [selectedRuleMigrations, setSelectedRuleMigrations] = useState<RuleMigration[]>([]);
|
||||||
|
@ -158,13 +159,11 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { mutateAsync: installMigrationRules } = useInstallMigrationRules(migrationId);
|
const { mutateAsync: installMigrationRules } = useInstallMigrationRules(migrationId);
|
||||||
const { mutateAsync: installTranslatedMigrationRules } =
|
|
||||||
useInstallTranslatedMigrationRules(migrationId);
|
|
||||||
const { startMigration, isLoading: isRetryLoading } = useStartMigration(refetchData);
|
const { startMigration, isLoading: isRetryLoading } = useStartMigration(refetchData);
|
||||||
|
|
||||||
const [isTableLoading, setTableLoading] = useState(false);
|
const [isTableLoading, setTableLoading] = useState(false);
|
||||||
const installSingleRule = useCallback(
|
const installSingleRule = useCallback(
|
||||||
async (migrationRule: RuleMigration, enabled = false) => {
|
async (migrationRule: RuleMigration, enabled?: boolean) => {
|
||||||
setTableLoading(true);
|
setTableLoading(true);
|
||||||
try {
|
try {
|
||||||
await installMigrationRules({ ids: [migrationRule.id], enabled });
|
await installMigrationRules({ ids: [migrationRule.id], enabled });
|
||||||
|
@ -178,7 +177,7 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem
|
||||||
);
|
);
|
||||||
|
|
||||||
const installSelectedRule = useCallback(
|
const installSelectedRule = useCallback(
|
||||||
async (enabled = false) => {
|
async (enabled?: boolean) => {
|
||||||
setTableLoading(true);
|
setTableLoading(true);
|
||||||
try {
|
try {
|
||||||
await installMigrationRules({
|
await installMigrationRules({
|
||||||
|
@ -196,17 +195,17 @@ export const MigrationRulesTable: React.FC<MigrationRulesTableProps> = React.mem
|
||||||
);
|
);
|
||||||
|
|
||||||
const installTranslatedRules = useCallback(
|
const installTranslatedRules = useCallback(
|
||||||
async (enable?: boolean) => {
|
async (enabled?: boolean) => {
|
||||||
setTableLoading(true);
|
setTableLoading(true);
|
||||||
try {
|
try {
|
||||||
await installTranslatedMigrationRules();
|
await installMigrationRules({ enabled });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError(error, { title: logicI18n.INSTALL_MIGRATION_RULES_FAILURE });
|
addError(error, { title: logicI18n.INSTALL_MIGRATION_RULES_FAILURE });
|
||||||
} finally {
|
} finally {
|
||||||
setTableLoading(false);
|
setTableLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[addError, installTranslatedMigrationRules]
|
[addError, installMigrationRules]
|
||||||
);
|
);
|
||||||
|
|
||||||
const reprocessFailedRules = useCallback(async () => {
|
const reprocessFailedRules = useCallback(async () => {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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 { RuleMigrationFilters } from '../../../../../../common/siem_migrations/types';
|
||||||
|
import type { FilterOptions } from '../../../types';
|
||||||
|
import { AuthorFilter, StatusFilter } from '../../../types';
|
||||||
|
|
||||||
|
const AUTHOR_FILTERS: Record<AuthorFilter, RuleMigrationFilters> = {
|
||||||
|
[AuthorFilter.ELASTIC]: { prebuilt: true },
|
||||||
|
[AuthorFilter.CUSTOM]: { prebuilt: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
const STATUS_FILTERS: Record<StatusFilter, RuleMigrationFilters> = {
|
||||||
|
[StatusFilter.FAILED]: { failed: true },
|
||||||
|
[StatusFilter.INSTALLED]: { installed: true },
|
||||||
|
[StatusFilter.TRANSLATED]: { installed: false, fullyTranslated: true },
|
||||||
|
[StatusFilter.PARTIALLY_TRANSLATED]: { partiallyTranslated: true },
|
||||||
|
[StatusFilter.UNTRANSLATABLE]: { untranslatable: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertFilterOptions = (filterOptions?: FilterOptions) => {
|
||||||
|
const filters: RuleMigrationFilters = {};
|
||||||
|
if (filterOptions?.author) {
|
||||||
|
Object.assign(filters, AUTHOR_FILTERS[filterOptions.author]);
|
||||||
|
}
|
||||||
|
if (filterOptions?.status) {
|
||||||
|
Object.assign(filters, STATUS_FILTERS[filterOptions.status]);
|
||||||
|
}
|
||||||
|
return filters;
|
||||||
|
};
|
|
@ -49,8 +49,11 @@ export const StatusBadge: React.FC<StatusBadgeProps> = React.memo(
|
||||||
|
|
||||||
// Failed
|
// Failed
|
||||||
if (migrationRule.status === RuleMigrationStatusEnum.failed) {
|
if (migrationRule.status === RuleMigrationStatusEnum.failed) {
|
||||||
|
const tooltipMessage = migrationRule.comments?.length
|
||||||
|
? migrationRule.comments[0]
|
||||||
|
: i18n.RULE_STATUS_FAILED;
|
||||||
return (
|
return (
|
||||||
<EuiToolTip content={i18n.RULE_STATUS_FAILED}>
|
<EuiToolTip content={tooltipMessage}>
|
||||||
<EuiFlexGroup gutterSize="xs" alignItems="center">
|
<EuiFlexGroup gutterSize="xs" alignItems="center">
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiIcon type="warningFilled" color="danger" />
|
<EuiIcon type="warningFilled" color="danger" />
|
||||||
|
|
|
@ -28,6 +28,12 @@ export const GET_MIGRATION_TRANSLATION_STATS_FAILURE = i18n.translate(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const INSTALL_MIGRATION_RULES_SUCCESS = (succeeded: number) =>
|
||||||
|
i18n.translate('xpack.securitySolution.siemMigrations.rules.installMigrationRulesSuccess', {
|
||||||
|
defaultMessage: '{succeeded, plural, one {# rule} other {# rules}} installed successfully.',
|
||||||
|
values: { succeeded },
|
||||||
|
});
|
||||||
|
|
||||||
export const INSTALL_MIGRATION_RULES_FAILURE = i18n.translate(
|
export const INSTALL_MIGRATION_RULES_FAILURE = i18n.translate(
|
||||||
'xpack.securitySolution.siemMigrations.rules.installMigrationRulesFailDescription',
|
'xpack.securitySolution.siemMigrations.rules.installMigrationRulesFailDescription',
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { replaceParams } from '@kbn/openapi-common/shared';
|
import { replaceParams } from '@kbn/openapi-common/shared';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import type { RuleMigrationFilters } from '../../../../common/siem_migrations/types';
|
||||||
import { SIEM_RULE_MIGRATION_PATH } from '../../../../common/siem_migrations/constants';
|
import { SIEM_RULE_MIGRATION_PATH } from '../../../../common/siem_migrations/constants';
|
||||||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
|
@ -20,14 +21,7 @@ export const useGetMigrationRules = (params: {
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
sortField?: string;
|
sortField?: string;
|
||||||
sortDirection?: 'asc' | 'desc';
|
sortDirection?: 'asc' | 'desc';
|
||||||
searchTerm?: string;
|
filters?: RuleMigrationFilters;
|
||||||
ids?: string[];
|
|
||||||
isPrebuilt?: boolean;
|
|
||||||
isInstalled?: boolean;
|
|
||||||
isFullyTranslated?: boolean;
|
|
||||||
isPartiallyTranslated?: boolean;
|
|
||||||
isUntranslatable?: boolean;
|
|
||||||
isFailed?: boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
const { addError } = useAppToasts();
|
const { addError } = useAppToasts();
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,18 @@ import { installMigrationRules } from '../api';
|
||||||
export const INSTALL_MIGRATION_RULES_MUTATION_KEY = ['POST', SIEM_RULE_MIGRATION_INSTALL_PATH];
|
export const INSTALL_MIGRATION_RULES_MUTATION_KEY = ['POST', SIEM_RULE_MIGRATION_INSTALL_PATH];
|
||||||
|
|
||||||
export const useInstallMigrationRules = (migrationId: string) => {
|
export const useInstallMigrationRules = (migrationId: string) => {
|
||||||
const { addError } = useAppToasts();
|
const { addError, addSuccess } = useAppToasts();
|
||||||
|
|
||||||
const invalidateGetRuleMigrations = useInvalidateGetMigrationRules();
|
const invalidateGetRuleMigrations = useInvalidateGetMigrationRules();
|
||||||
const invalidateGetMigrationTranslationStats = useInvalidateGetMigrationTranslationStats();
|
const invalidateGetMigrationTranslationStats = useInvalidateGetMigrationTranslationStats();
|
||||||
|
|
||||||
return useMutation<InstallMigrationRulesResponse, Error, { ids: string[]; enabled: boolean }>(
|
return useMutation<InstallMigrationRulesResponse, Error, { ids?: string[]; enabled?: boolean }>(
|
||||||
({ ids, enabled = false }) => installMigrationRules({ migrationId, ids, enabled }),
|
({ ids, enabled }) => installMigrationRules({ migrationId, ids, enabled }),
|
||||||
{
|
{
|
||||||
mutationKey: INSTALL_MIGRATION_RULES_MUTATION_KEY,
|
mutationKey: INSTALL_MIGRATION_RULES_MUTATION_KEY,
|
||||||
|
onSuccess: ({ installed }) => {
|
||||||
|
addSuccess(i18n.INSTALL_MIGRATION_RULES_SUCCESS(installed));
|
||||||
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
addError(error, { title: i18n.INSTALL_MIGRATION_RULES_FAILURE });
|
addError(error, { title: i18n.INSTALL_MIGRATION_RULES_FAILURE });
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 { useMutation } from '@tanstack/react-query';
|
|
||||||
import type { InstallTranslatedMigrationRulesResponse } from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
|
||||||
import { SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH } from '../../../../common/siem_migrations/constants';
|
|
||||||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
|
||||||
import * as i18n from './translations';
|
|
||||||
import { useInvalidateGetMigrationRules } from './use_get_migration_rules';
|
|
||||||
import { useInvalidateGetMigrationTranslationStats } from './use_get_migration_translation_stats';
|
|
||||||
import { installTranslatedMigrationRules } from '../api';
|
|
||||||
|
|
||||||
export const INSTALL_ALL_MIGRATION_RULES_MUTATION_KEY = [
|
|
||||||
'POST',
|
|
||||||
SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const useInstallTranslatedMigrationRules = (migrationId: string) => {
|
|
||||||
const { addError } = useAppToasts();
|
|
||||||
|
|
||||||
const invalidateGetRuleMigrations = useInvalidateGetMigrationRules();
|
|
||||||
const invalidateGetMigrationTranslationStats = useInvalidateGetMigrationTranslationStats();
|
|
||||||
|
|
||||||
return useMutation<InstallTranslatedMigrationRulesResponse, Error>(
|
|
||||||
() => installTranslatedMigrationRules({ migrationId }),
|
|
||||||
{
|
|
||||||
mutationKey: INSTALL_ALL_MIGRATION_RULES_MUTATION_KEY,
|
|
||||||
onError: (error) => {
|
|
||||||
addError(error, { title: i18n.INSTALL_MIGRATION_RULES_FAILURE });
|
|
||||||
},
|
|
||||||
onSettled: () => {
|
|
||||||
invalidateGetRuleMigrations(migrationId);
|
|
||||||
invalidateGetMigrationTranslationStats(migrationId);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { EuiSkeletonLoading, EuiSkeletonText, EuiSkeletonTitle } from '@elastic/eui';
|
import { EuiSkeletonLoading, EuiSkeletonText, EuiSkeletonTitle, EuiSpacer } from '@elastic/eui';
|
||||||
import type { RouteComponentProps } from 'react-router-dom';
|
import type { RouteComponentProps } from 'react-router-dom';
|
||||||
import type { RelatedIntegration } from '../../../../common/api/detection_engine';
|
import type { RelatedIntegration } from '../../../../common/api/detection_engine';
|
||||||
import { SiemMigrationTaskStatus } from '../../../../common/siem_migrations/constants';
|
import { SiemMigrationTaskStatus } from '../../../../common/siem_migrations/constants';
|
||||||
|
@ -29,6 +29,7 @@ import { useInvalidateGetMigrationRules } from '../logic/use_get_migration_rules
|
||||||
import { useInvalidateGetMigrationTranslationStats } from '../logic/use_get_migration_translation_stats';
|
import { useInvalidateGetMigrationTranslationStats } from '../logic/use_get_migration_translation_stats';
|
||||||
import { useGetIntegrations } from '../service/hooks/use_get_integrations';
|
import { useGetIntegrations } from '../service/hooks/use_get_integrations';
|
||||||
import { PageTitle } from './page_title';
|
import { PageTitle } from './page_title';
|
||||||
|
import { RuleMigrationsUploadMissingPanel } from '../components/migration_status_panels/upload_missing_panel';
|
||||||
|
|
||||||
type MigrationRulesPageProps = RouteComponentProps<{ migrationId?: string }>;
|
type MigrationRulesPageProps = RouteComponentProps<{ migrationId?: string }>;
|
||||||
|
|
||||||
|
@ -99,19 +100,24 @@ export const MigrationRulesPage: React.FC<MigrationRulesPageProps> = React.memo(
|
||||||
if (!migrationId || !migrationStats) {
|
if (!migrationId || !migrationStats) {
|
||||||
return <UnknownMigration />;
|
return <UnknownMigration />;
|
||||||
}
|
}
|
||||||
if (migrationStats.status === SiemMigrationTaskStatus.FINISHED) {
|
|
||||||
return (
|
return (
|
||||||
|
<RuleMigrationDataInputWrapper onFlyoutClosed={refetchData}>
|
||||||
|
<>
|
||||||
|
{migrationStats.status === SiemMigrationTaskStatus.FINISHED && (
|
||||||
|
<>
|
||||||
|
<RuleMigrationsUploadMissingPanel
|
||||||
|
migrationStats={migrationStats}
|
||||||
|
topSpacerSize="s"
|
||||||
|
/>
|
||||||
|
<EuiSpacer size="m" />
|
||||||
<MigrationRulesTable
|
<MigrationRulesTable
|
||||||
migrationId={migrationId}
|
migrationId={migrationId}
|
||||||
refetchData={refetchData}
|
refetchData={refetchData}
|
||||||
integrations={integrations}
|
integrations={integrations}
|
||||||
isIntegrationsLoading={isIntegrationsLoading}
|
isIntegrationsLoading={isIntegrationsLoading}
|
||||||
/>
|
/>
|
||||||
);
|
</>
|
||||||
}
|
)}
|
||||||
return (
|
|
||||||
<RuleMigrationDataInputWrapper onFlyoutClosed={refetchData}>
|
|
||||||
<>
|
|
||||||
{migrationStats.status === SiemMigrationTaskStatus.READY && (
|
{migrationStats.status === SiemMigrationTaskStatus.READY && (
|
||||||
<MigrationReadyPanel migrationStats={migrationStats} />
|
<MigrationReadyPanel migrationStats={migrationStats} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -13,3 +13,21 @@ export interface RuleMigrationStats extends RuleMigrationTaskStats {
|
||||||
/** The sequential number of the migration */
|
/** The sequential number of the migration */
|
||||||
number: number;
|
number: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AuthorFilter {
|
||||||
|
ELASTIC = 'elastic',
|
||||||
|
CUSTOM = 'custom',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum StatusFilter {
|
||||||
|
INSTALLED = 'installed',
|
||||||
|
TRANSLATED = 'translated',
|
||||||
|
PARTIALLY_TRANSLATED = 'partially_translated',
|
||||||
|
UNTRANSLATABLE = 'untranslatable',
|
||||||
|
FAILED = 'failed',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterOptions {
|
||||||
|
status?: StatusFilter;
|
||||||
|
author?: AuthorFilter;
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL = 50;
|
|
||||||
export const MAX_PREBUILT_RULES_TO_FETCH = 10_000 as const;
|
|
||||||
export const MAX_TRANSLATED_RULES_TO_INSTALL = 10_000 as const;
|
|
|
@ -18,7 +18,6 @@ import { registerSiemRuleMigrationsStatsAllRoute } from './stats_all';
|
||||||
import { registerSiemRuleMigrationsResourceUpsertRoute } from './resources/upsert';
|
import { registerSiemRuleMigrationsResourceUpsertRoute } from './resources/upsert';
|
||||||
import { registerSiemRuleMigrationsResourceGetRoute } from './resources/get';
|
import { registerSiemRuleMigrationsResourceGetRoute } from './resources/get';
|
||||||
import { registerSiemRuleMigrationsInstallRoute } from './install';
|
import { registerSiemRuleMigrationsInstallRoute } from './install';
|
||||||
import { registerSiemRuleMigrationsInstallTranslatedRoute } from './install_translated';
|
|
||||||
import { registerSiemRuleMigrationsResourceGetMissingRoute } from './resources/missing';
|
import { registerSiemRuleMigrationsResourceGetMissingRoute } from './resources/missing';
|
||||||
import { registerSiemRuleMigrationsPrebuiltRulesRoute } from './get_prebuilt_rules';
|
import { registerSiemRuleMigrationsPrebuiltRulesRoute } from './get_prebuilt_rules';
|
||||||
import { registerSiemRuleMigrationsIntegrationsRoute } from './get_integrations';
|
import { registerSiemRuleMigrationsIntegrationsRoute } from './get_integrations';
|
||||||
|
@ -37,7 +36,6 @@ export const registerSiemRuleMigrationsRoutes = (
|
||||||
registerSiemRuleMigrationsTranslationStatsRoute(router, logger);
|
registerSiemRuleMigrationsTranslationStatsRoute(router, logger);
|
||||||
registerSiemRuleMigrationsStopRoute(router, logger);
|
registerSiemRuleMigrationsStopRoute(router, logger);
|
||||||
registerSiemRuleMigrationsInstallRoute(router, logger);
|
registerSiemRuleMigrationsInstallRoute(router, logger);
|
||||||
registerSiemRuleMigrationsInstallTranslatedRoute(router, logger);
|
|
||||||
registerSiemRuleMigrationsIntegrationsRoute(router, logger);
|
registerSiemRuleMigrationsIntegrationsRoute(router, logger);
|
||||||
|
|
||||||
registerSiemRuleMigrationsResourceUpsertRoute(router, logger);
|
registerSiemRuleMigrationsResourceUpsertRoute(router, logger);
|
||||||
|
|
|
@ -49,17 +49,16 @@ export const registerSiemRuleMigrationsInstallRoute = (
|
||||||
const savedObjectsClient = ctx.core.savedObjects.client;
|
const savedObjectsClient = ctx.core.savedObjects.client;
|
||||||
const rulesClient = await ctx.alerting.getRulesClient();
|
const rulesClient = await ctx.alerting.getRulesClient();
|
||||||
|
|
||||||
await installTranslated({
|
const installed = await installTranslated({
|
||||||
migrationId,
|
migrationId,
|
||||||
ids,
|
ids,
|
||||||
enabled,
|
enabled,
|
||||||
securitySolutionContext,
|
securitySolutionContext,
|
||||||
savedObjectsClient,
|
savedObjectsClient,
|
||||||
rulesClient,
|
rulesClient,
|
||||||
logger,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok({ body: { installed: true } });
|
return res.ok({ body: { installed } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
return res.badRequest({ body: err.message });
|
return res.badRequest({ body: err.message });
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { IKibanaResponse, Logger } from '@kbn/core/server';
|
|
||||||
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
|
|
||||||
import { SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH } from '../../../../../common/siem_migrations/constants';
|
|
||||||
import type { InstallTranslatedMigrationRulesResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
|
||||||
import { InstallTranslatedMigrationRulesRequestParams } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
|
|
||||||
import type { SecuritySolutionPluginRouter } from '../../../../types';
|
|
||||||
import { withLicense } from './util/with_license';
|
|
||||||
import { installTranslated } from './util/installation';
|
|
||||||
|
|
||||||
export const registerSiemRuleMigrationsInstallTranslatedRoute = (
|
|
||||||
router: SecuritySolutionPluginRouter,
|
|
||||||
logger: Logger
|
|
||||||
) => {
|
|
||||||
router.versioned
|
|
||||||
.post({
|
|
||||||
path: SIEM_RULE_MIGRATION_INSTALL_TRANSLATED_PATH,
|
|
||||||
access: 'internal',
|
|
||||||
security: { authz: { requiredPrivileges: ['securitySolution'] } },
|
|
||||||
})
|
|
||||||
.addVersion(
|
|
||||||
{
|
|
||||||
version: '1',
|
|
||||||
validate: {
|
|
||||||
request: {
|
|
||||||
params: buildRouteValidationWithZod(InstallTranslatedMigrationRulesRequestParams),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
withLicense(
|
|
||||||
async (
|
|
||||||
context,
|
|
||||||
req,
|
|
||||||
res
|
|
||||||
): Promise<IKibanaResponse<InstallTranslatedMigrationRulesResponse>> => {
|
|
||||||
const { migration_id: migrationId } = req.params;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ctx = await context.resolve(['core', 'alerting', 'securitySolution']);
|
|
||||||
|
|
||||||
const securitySolutionContext = ctx.securitySolution;
|
|
||||||
const savedObjectsClient = ctx.core.savedObjects.client;
|
|
||||||
const rulesClient = await ctx.alerting.getRulesClient();
|
|
||||||
|
|
||||||
await installTranslated({
|
|
||||||
migrationId,
|
|
||||||
enabled: false,
|
|
||||||
securitySolutionContext,
|
|
||||||
savedObjectsClient,
|
|
||||||
rulesClient,
|
|
||||||
logger,
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.ok({ body: { installed: true } });
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err);
|
|
||||||
return res.badRequest({ body: err.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -5,8 +5,9 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Logger, SavedObjectsClientContract } from '@kbn/core/server';
|
import type { SavedObjectsClientContract } from '@kbn/core/server';
|
||||||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||||
|
import { getErrorMessage } from '../../../../../utils/error_helpers';
|
||||||
import type { UpdateRuleMigrationData } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
|
import type { UpdateRuleMigrationData } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||||
import { initPromisePool } from '../../../../../utils/promise_pool';
|
import { initPromisePool } from '../../../../../utils/promise_pool';
|
||||||
import type { SecuritySolutionApiRequestHandlerContext } from '../../../../..';
|
import type { SecuritySolutionApiRequestHandlerContext } from '../../../../..';
|
||||||
|
@ -16,15 +17,13 @@ import type { IDetectionRulesClient } from '../../../../detection_engine/rule_ma
|
||||||
import type { RuleResponse } from '../../../../../../common/api/detection_engine';
|
import type { RuleResponse } from '../../../../../../common/api/detection_engine';
|
||||||
import type { StoredRuleMigration } from '../../types';
|
import type { StoredRuleMigration } from '../../types';
|
||||||
import { getPrebuiltRules, getUniquePrebuiltRuleIds } from './prebuilt_rules';
|
import { getPrebuiltRules, getUniquePrebuiltRuleIds } from './prebuilt_rules';
|
||||||
import {
|
|
||||||
MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL,
|
|
||||||
MAX_TRANSLATED_RULES_TO_INSTALL,
|
|
||||||
} from '../constants';
|
|
||||||
import {
|
import {
|
||||||
convertMigrationCustomRuleToSecurityRulePayload,
|
convertMigrationCustomRuleToSecurityRulePayload,
|
||||||
isMigrationCustomRule,
|
isMigrationCustomRule,
|
||||||
} from '../../../../../../common/siem_migrations/rules/utils';
|
} from '../../../../../../common/siem_migrations/rules/utils';
|
||||||
|
|
||||||
|
const MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL = 50;
|
||||||
|
|
||||||
const installPrebuiltRules = async (
|
const installPrebuiltRules = async (
|
||||||
rulesToInstall: StoredRuleMigration[],
|
rulesToInstall: StoredRuleMigration[],
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
|
@ -32,7 +31,7 @@ const installPrebuiltRules = async (
|
||||||
rulesClient: RulesClient,
|
rulesClient: RulesClient,
|
||||||
savedObjectsClient: SavedObjectsClientContract,
|
savedObjectsClient: SavedObjectsClientContract,
|
||||||
detectionRulesClient: IDetectionRulesClient
|
detectionRulesClient: IDetectionRulesClient
|
||||||
): Promise<UpdateRuleMigrationData[]> => {
|
): Promise<{ rulesToUpdate: UpdateRuleMigrationData[]; errors: Error[] }> => {
|
||||||
// Get required prebuilt rules
|
// Get required prebuilt rules
|
||||||
const prebuiltRulesIds = getUniquePrebuiltRuleIds(rulesToInstall);
|
const prebuiltRulesIds = getUniquePrebuiltRuleIds(rulesToInstall);
|
||||||
const prebuiltRules = await getPrebuiltRules(rulesClient, savedObjectsClient, prebuiltRulesIds);
|
const prebuiltRules = await getPrebuiltRules(rulesClient, savedObjectsClient, prebuiltRulesIds);
|
||||||
|
@ -52,13 +51,16 @@ const installPrebuiltRules = async (
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const errors: Error[] = [];
|
||||||
|
|
||||||
// Install prebuilt rules
|
// Install prebuilt rules
|
||||||
// TODO: we need to do an error handling which can happen during the rule installation
|
const { results: newlyInstalledRules, errors: installPrebuiltRulesErrors } =
|
||||||
const { results: newlyInstalledRules } = await createPrebuiltRules(
|
await createPrebuiltRules(detectionRulesClient, installable);
|
||||||
detectionRulesClient,
|
errors.push(
|
||||||
installable
|
...installPrebuiltRulesErrors.map(
|
||||||
|
(err) => new Error(`Error installing prebuilt rule: ${getErrorMessage(err)}`)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
await performTimelinesInstallation(securitySolutionContext);
|
|
||||||
|
|
||||||
const installedRules = [
|
const installedRules = [
|
||||||
...alreadyInstalledRules,
|
...alreadyInstalledRules,
|
||||||
|
@ -81,15 +83,18 @@ const installPrebuiltRules = async (
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return rulesToUpdate;
|
return { rulesToUpdate, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const installCustomRules = async (
|
export const installCustomRules = async (
|
||||||
rulesToInstall: StoredRuleMigration[],
|
rulesToInstall: StoredRuleMigration[],
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
detectionRulesClient: IDetectionRulesClient,
|
detectionRulesClient: IDetectionRulesClient
|
||||||
logger: Logger
|
): Promise<{
|
||||||
): Promise<UpdateRuleMigrationData[]> => {
|
rulesToUpdate: UpdateRuleMigrationData[];
|
||||||
|
errors: Error[];
|
||||||
|
}> => {
|
||||||
|
const errors: Error[] = [];
|
||||||
const rulesToUpdate: UpdateRuleMigrationData[] = [];
|
const rulesToUpdate: UpdateRuleMigrationData[] = [];
|
||||||
const createCustomRulesOutcome = await initPromisePool({
|
const createCustomRulesOutcome = await initPromisePool({
|
||||||
concurrency: MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL,
|
concurrency: MAX_CUSTOM_RULES_TO_CREATE_IN_PARALLEL,
|
||||||
|
@ -113,15 +118,12 @@ export const installCustomRules = async (
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (createCustomRulesOutcome.errors) {
|
errors.push(
|
||||||
// TODO: we need to do an error handling which can happen during the rule creation
|
...createCustomRulesOutcome.errors.map(
|
||||||
logger.debug(
|
(err) => new Error(`Error installing custom rule: ${getErrorMessage(err)}`)
|
||||||
`Failed to create some of the rules because of errors: ${JSON.stringify(
|
)
|
||||||
createCustomRulesOutcome.errors
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
}
|
return { rulesToUpdate, errors };
|
||||||
return rulesToUpdate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface InstallTranslatedProps {
|
interface InstallTranslatedProps {
|
||||||
|
@ -155,11 +157,6 @@ interface InstallTranslatedProps {
|
||||||
* The saved objects client
|
* The saved objects client
|
||||||
*/
|
*/
|
||||||
savedObjectsClient: SavedObjectsClientContract;
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
|
|
||||||
/**
|
|
||||||
* The logger
|
|
||||||
*/
|
|
||||||
logger: Logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const installTranslated = async ({
|
export const installTranslated = async ({
|
||||||
|
@ -169,33 +166,20 @@ export const installTranslated = async ({
|
||||||
securitySolutionContext,
|
securitySolutionContext,
|
||||||
rulesClient,
|
rulesClient,
|
||||||
savedObjectsClient,
|
savedObjectsClient,
|
||||||
logger,
|
}: InstallTranslatedProps): Promise<number> => {
|
||||||
}: InstallTranslatedProps) => {
|
|
||||||
const detectionRulesClient = securitySolutionContext.getDetectionRulesClient();
|
const detectionRulesClient = securitySolutionContext.getDetectionRulesClient();
|
||||||
const ruleMigrationsClient = securitySolutionContext.getSiemRuleMigrationsClient();
|
const ruleMigrationsClient = securitySolutionContext.getSiemRuleMigrationsClient();
|
||||||
|
|
||||||
const { data: rulesToInstall } = await ruleMigrationsClient.data.rules.get(migrationId, {
|
let installedCount = 0;
|
||||||
filters: { ids, installable: true },
|
const installationErrors: Error[] = [];
|
||||||
from: 0,
|
|
||||||
size: MAX_TRANSLATED_RULES_TO_INSTALL,
|
// Install rules that matched Elastic prebuilt rules
|
||||||
|
const prebuiltRuleBatches = ruleMigrationsClient.data.rules.searchBatches(migrationId, {
|
||||||
|
filters: { ids, installable: true, prebuilt: true },
|
||||||
});
|
});
|
||||||
|
let prebuiltRulesToInstall = await prebuiltRuleBatches.next();
|
||||||
const { customRulesToInstall, prebuiltRulesToInstall } = rulesToInstall.reduce(
|
while (prebuiltRulesToInstall.length) {
|
||||||
(acc, item) => {
|
const { rulesToUpdate, errors } = await installPrebuiltRules(
|
||||||
if (item.elastic_rule?.prebuilt_rule_id) {
|
|
||||||
acc.prebuiltRulesToInstall.push(item);
|
|
||||||
} else {
|
|
||||||
acc.customRulesToInstall.push(item);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{ customRulesToInstall: [], prebuiltRulesToInstall: [] } as {
|
|
||||||
customRulesToInstall: StoredRuleMigration[];
|
|
||||||
prebuiltRulesToInstall: StoredRuleMigration[];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedPrebuiltRules = await installPrebuiltRules(
|
|
||||||
prebuiltRulesToInstall,
|
prebuiltRulesToInstall,
|
||||||
enabled,
|
enabled,
|
||||||
securitySolutionContext,
|
securitySolutionContext,
|
||||||
|
@ -203,17 +187,42 @@ export const installTranslated = async ({
|
||||||
savedObjectsClient,
|
savedObjectsClient,
|
||||||
detectionRulesClient
|
detectionRulesClient
|
||||||
);
|
);
|
||||||
|
installedCount += rulesToUpdate.length;
|
||||||
|
installationErrors.push(...errors);
|
||||||
|
await ruleMigrationsClient.data.rules.update(rulesToUpdate);
|
||||||
|
prebuiltRulesToInstall = await prebuiltRuleBatches.next();
|
||||||
|
}
|
||||||
|
|
||||||
const updatedCustomRules = await installCustomRules(
|
let installTimelinesError: string | undefined;
|
||||||
|
if (installedCount > 0) {
|
||||||
|
const { error } = await performTimelinesInstallation(securitySolutionContext);
|
||||||
|
installTimelinesError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install rules with custom translation
|
||||||
|
const customRuleBatches = ruleMigrationsClient.data.rules.searchBatches(migrationId, {
|
||||||
|
filters: { ids, installable: true, prebuilt: false },
|
||||||
|
});
|
||||||
|
let customRulesToInstall = await customRuleBatches.next();
|
||||||
|
while (customRulesToInstall.length) {
|
||||||
|
const { rulesToUpdate, errors } = await installCustomRules(
|
||||||
customRulesToInstall,
|
customRulesToInstall,
|
||||||
enabled,
|
enabled,
|
||||||
detectionRulesClient,
|
detectionRulesClient
|
||||||
logger
|
|
||||||
);
|
);
|
||||||
|
installedCount += rulesToUpdate.length;
|
||||||
const rulesToUpdate: UpdateRuleMigrationData[] = [...updatedPrebuiltRules, ...updatedCustomRules];
|
installationErrors.push(...errors);
|
||||||
|
|
||||||
if (rulesToUpdate.length) {
|
|
||||||
await ruleMigrationsClient.data.rules.update(rulesToUpdate);
|
await ruleMigrationsClient.data.rules.update(rulesToUpdate);
|
||||||
|
customRulesToInstall = await customRuleBatches.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throw an error if needed
|
||||||
|
if (installTimelinesError) {
|
||||||
|
throw new Error(`Error installing prepackaged timelines: ${installTimelinesError}`);
|
||||||
|
}
|
||||||
|
if (installationErrors.length) {
|
||||||
|
throw new Error(installationErrors.map((err) => err.message).join());
|
||||||
|
}
|
||||||
|
|
||||||
|
return installedCount;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { RuleMigrationRetryFilter } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
|
import type { RuleMigrationRetryFilter } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||||
import type { RuleMigrationFilters } from '../../data/rule_migrations_data_rules_client';
|
import type { RuleMigrationFilters } from '../../../../../../common/siem_migrations/types';
|
||||||
|
|
||||||
const RETRY_FILTERS: Record<RuleMigrationRetryFilter, RuleMigrationFilters> = {
|
const RETRY_FILTERS: Record<RuleMigrationRetryFilter, RuleMigrationFilters> = {
|
||||||
failed: { failed: true },
|
failed: { failed: true },
|
||||||
|
|
|
@ -15,6 +15,7 @@ import type {
|
||||||
QueryDslQueryContainer,
|
QueryDslQueryContainer,
|
||||||
Duration,
|
Duration,
|
||||||
} from '@elastic/elasticsearch/lib/api/types';
|
} from '@elastic/elasticsearch/lib/api/types';
|
||||||
|
import type { RuleMigrationFilters } from '../../../../../common/siem_migrations/types';
|
||||||
import type { InternalUpdateRuleMigrationData, StoredRuleMigration } from '../types';
|
import type { InternalUpdateRuleMigrationData, StoredRuleMigration } from '../types';
|
||||||
import {
|
import {
|
||||||
SiemMigrationStatus,
|
SiemMigrationStatus,
|
||||||
|
@ -36,18 +37,6 @@ export type CreateRuleMigrationInput = Omit<
|
||||||
export type RuleMigrationDataStats = Omit<RuleMigrationTaskStats, 'status'>;
|
export type RuleMigrationDataStats = Omit<RuleMigrationTaskStats, 'status'>;
|
||||||
export type RuleMigrationAllDataStats = RuleMigrationDataStats[];
|
export type RuleMigrationAllDataStats = RuleMigrationDataStats[];
|
||||||
|
|
||||||
export interface RuleMigrationFilters {
|
|
||||||
status?: SiemMigrationStatus | SiemMigrationStatus[];
|
|
||||||
ids?: string[];
|
|
||||||
installed?: boolean;
|
|
||||||
installable?: boolean;
|
|
||||||
prebuilt?: boolean;
|
|
||||||
failed?: boolean;
|
|
||||||
fullyTranslated?: boolean;
|
|
||||||
partiallyTranslated?: boolean;
|
|
||||||
untranslatable?: boolean;
|
|
||||||
searchTerm?: string;
|
|
||||||
}
|
|
||||||
export interface RuleMigrationGetOptions {
|
export interface RuleMigrationGetOptions {
|
||||||
filters?: RuleMigrationFilters;
|
filters?: RuleMigrationFilters;
|
||||||
sort?: RuleMigrationSort;
|
sort?: RuleMigrationSort;
|
||||||
|
@ -397,66 +386,55 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
|
||||||
|
|
||||||
private getFilterQuery(
|
private getFilterQuery(
|
||||||
migrationId: string,
|
migrationId: string,
|
||||||
{
|
filters: RuleMigrationFilters = {}
|
||||||
status,
|
|
||||||
ids,
|
|
||||||
installed,
|
|
||||||
installable,
|
|
||||||
prebuilt,
|
|
||||||
searchTerm,
|
|
||||||
failed,
|
|
||||||
fullyTranslated,
|
|
||||||
partiallyTranslated,
|
|
||||||
untranslatable,
|
|
||||||
}: RuleMigrationFilters = {}
|
|
||||||
): QueryDslQueryContainer {
|
): QueryDslQueryContainer {
|
||||||
const filter: QueryDslQueryContainer[] = [{ term: { migration_id: migrationId } }];
|
const filter: QueryDslQueryContainer[] = [{ term: { migration_id: migrationId } }];
|
||||||
if (status) {
|
if (filters.status) {
|
||||||
if (Array.isArray(status)) {
|
if (Array.isArray(filters.status)) {
|
||||||
filter.push({ terms: { status } });
|
filter.push({ terms: { status: filters.status } });
|
||||||
} else {
|
} else {
|
||||||
filter.push({ term: { status } });
|
filter.push({ term: { status: filters.status } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ids) {
|
if (filters.ids) {
|
||||||
filter.push({ terms: { _id: ids } });
|
filter.push({ terms: { _id: filters.ids } });
|
||||||
}
|
}
|
||||||
if (searchTerm?.length) {
|
if (filters.searchTerm?.length) {
|
||||||
filter.push(searchConditions.matchTitle(searchTerm));
|
filter.push(searchConditions.matchTitle(filters.searchTerm));
|
||||||
}
|
}
|
||||||
if (installed === true) {
|
if (filters.installed === true) {
|
||||||
filter.push(searchConditions.isInstalled());
|
filter.push(searchConditions.isInstalled());
|
||||||
} else if (installed === false) {
|
} else if (filters.installed === false) {
|
||||||
filter.push(searchConditions.isNotInstalled());
|
filter.push(searchConditions.isNotInstalled());
|
||||||
}
|
}
|
||||||
if (installable === true) {
|
if (filters.installable === true) {
|
||||||
filter.push(...searchConditions.isInstallable());
|
filter.push(...searchConditions.isInstallable());
|
||||||
} else if (installable === false) {
|
} else if (filters.installable === false) {
|
||||||
filter.push(...searchConditions.isNotInstallable());
|
filter.push(...searchConditions.isNotInstallable());
|
||||||
}
|
}
|
||||||
if (prebuilt === true) {
|
if (filters.prebuilt === true) {
|
||||||
filter.push(searchConditions.isPrebuilt());
|
filter.push(searchConditions.isPrebuilt());
|
||||||
} else if (prebuilt === false) {
|
} else if (filters.prebuilt === false) {
|
||||||
filter.push(searchConditions.isCustom());
|
filter.push(searchConditions.isCustom());
|
||||||
}
|
}
|
||||||
if (failed === true) {
|
if (filters.failed === true) {
|
||||||
filter.push(searchConditions.isFailed());
|
filter.push(searchConditions.isFailed());
|
||||||
} else if (failed === false) {
|
} else if (filters.failed === false) {
|
||||||
filter.push(searchConditions.isNotFailed());
|
filter.push(searchConditions.isNotFailed());
|
||||||
}
|
}
|
||||||
if (fullyTranslated === true) {
|
if (filters.fullyTranslated === true) {
|
||||||
filter.push(searchConditions.isFullyTranslated());
|
filter.push(searchConditions.isFullyTranslated());
|
||||||
} else if (fullyTranslated === false) {
|
} else if (filters.fullyTranslated === false) {
|
||||||
filter.push(searchConditions.isNotFullyTranslated());
|
filter.push(searchConditions.isNotFullyTranslated());
|
||||||
}
|
}
|
||||||
if (partiallyTranslated === true) {
|
if (filters.partiallyTranslated === true) {
|
||||||
filter.push(searchConditions.isPartiallyTranslated());
|
filter.push(searchConditions.isPartiallyTranslated());
|
||||||
} else if (partiallyTranslated === false) {
|
} else if (filters.partiallyTranslated === false) {
|
||||||
filter.push(searchConditions.isNotPartiallyTranslated());
|
filter.push(searchConditions.isNotPartiallyTranslated());
|
||||||
}
|
}
|
||||||
if (untranslatable === true) {
|
if (filters.untranslatable === true) {
|
||||||
filter.push(searchConditions.isUntranslatable());
|
filter.push(searchConditions.isUntranslatable());
|
||||||
} else if (untranslatable === false) {
|
} else if (filters.untranslatable === false) {
|
||||||
filter.push(searchConditions.isNotUntranslatable());
|
filter.push(searchConditions.isNotUntranslatable());
|
||||||
}
|
}
|
||||||
return { bool: { filter } };
|
return { bool: { filter } };
|
||||||
|
|
|
@ -8,16 +8,14 @@
|
||||||
import type { AuthenticatedUser, Logger } from '@kbn/core/server';
|
import type { AuthenticatedUser, Logger } from '@kbn/core/server';
|
||||||
import { AbortError, abortSignalToPromise } from '@kbn/kibana-utils-plugin/server';
|
import { AbortError, abortSignalToPromise } from '@kbn/kibana-utils-plugin/server';
|
||||||
import type { RunnableConfig } from '@langchain/core/runnables';
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
||||||
|
import type { RuleMigrationFilters } from '../../../../../common/siem_migrations/types';
|
||||||
import {
|
import {
|
||||||
SiemMigrationStatus,
|
SiemMigrationStatus,
|
||||||
SiemMigrationTaskStatus,
|
SiemMigrationTaskStatus,
|
||||||
} from '../../../../../common/siem_migrations/constants';
|
} from '../../../../../common/siem_migrations/constants';
|
||||||
import type { RuleMigrationTaskStats } from '../../../../../common/siem_migrations/model/rule_migration.gen';
|
import type { RuleMigrationTaskStats } from '../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||||
import type { RuleMigrationsDataClient } from '../data/rule_migrations_data_client';
|
import type { RuleMigrationsDataClient } from '../data/rule_migrations_data_client';
|
||||||
import type {
|
import type { RuleMigrationDataStats } from '../data/rule_migrations_data_rules_client';
|
||||||
RuleMigrationDataStats,
|
|
||||||
RuleMigrationFilters,
|
|
||||||
} from '../data/rule_migrations_data_rules_client';
|
|
||||||
import type { SiemRuleMigrationsClientDependencies } from '../types';
|
import type { SiemRuleMigrationsClientDependencies } from '../types';
|
||||||
import { getRuleMigrationAgent } from './agent';
|
import { getRuleMigrationAgent } from './agent';
|
||||||
import type { MigrateRuleState } from './agent/types';
|
import type { MigrateRuleState } from './agent/types';
|
||||||
|
|
|
@ -123,7 +123,6 @@ import {
|
||||||
InstallMigrationRulesRequestBodyInput,
|
InstallMigrationRulesRequestBodyInput,
|
||||||
} from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
|
} from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||||
import { InstallPrepackedTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/install_prepackaged_timelines/install_prepackaged_timelines_route.gen';
|
import { InstallPrepackedTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/install_prepackaged_timelines/install_prepackaged_timelines_route.gen';
|
||||||
import { InstallTranslatedMigrationRulesRequestParamsInput } from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
|
|
||||||
import { ListEntitiesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/entities/list_entities.gen';
|
import { ListEntitiesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/entities/list_entities.gen';
|
||||||
import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen';
|
import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen';
|
||||||
import { PatchTimelineRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/patch_timelines/patch_timeline_route.gen';
|
import { PatchTimelineRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/patch_timelines/patch_timeline_route.gen';
|
||||||
|
@ -1208,27 +1207,6 @@ finalize it.
|
||||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||||
.send(props.body as object);
|
.send(props.body as object);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Installs all translated migration rules
|
|
||||||
*/
|
|
||||||
installTranslatedMigrationRules(
|
|
||||||
props: InstallTranslatedMigrationRulesProps,
|
|
||||||
kibanaSpace: string = 'default'
|
|
||||||
) {
|
|
||||||
return supertest
|
|
||||||
.post(
|
|
||||||
routeWithNamespace(
|
|
||||||
replaceParams(
|
|
||||||
'/internal/siem_migrations/rules/{migration_id}/install_translated',
|
|
||||||
props.params
|
|
||||||
),
|
|
||||||
kibanaSpace
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.set('kbn-xsrf', 'true')
|
|
||||||
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
|
|
||||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana');
|
|
||||||
},
|
|
||||||
internalUploadAssetCriticalityRecords(kibanaSpace: string = 'default') {
|
internalUploadAssetCriticalityRecords(kibanaSpace: string = 'default') {
|
||||||
return supertest
|
return supertest
|
||||||
.post(routeWithNamespace('/internal/asset_criticality/upload_csv', kibanaSpace))
|
.post(routeWithNamespace('/internal/asset_criticality/upload_csv', kibanaSpace))
|
||||||
|
@ -1860,9 +1838,6 @@ export interface InstallMigrationRulesProps {
|
||||||
export interface InstallPrepackedTimelinesProps {
|
export interface InstallPrepackedTimelinesProps {
|
||||||
body: InstallPrepackedTimelinesRequestBodyInput;
|
body: InstallPrepackedTimelinesRequestBodyInput;
|
||||||
}
|
}
|
||||||
export interface InstallTranslatedMigrationRulesProps {
|
|
||||||
params: InstallTranslatedMigrationRulesRequestParamsInput;
|
|
||||||
}
|
|
||||||
export interface ListEntitiesProps {
|
export interface ListEntitiesProps {
|
||||||
query: ListEntitiesRequestQueryInput;
|
query: ListEntitiesRequestQueryInput;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue