Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rules migration] Add install and install all migration rules endpoints (#11283) #202026

Merged
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types';

export const SIEM_MIGRATIONS_PATH = '/internal/siem_migrations' as const;
export const SIEM_RULE_MIGRATIONS_PATH = `${SIEM_MIGRATIONS_PATH}/rules` as const;

Expand All @@ -14,6 +16,9 @@ export const SIEM_RULE_MIGRATION_START_PATH = `${SIEM_RULE_MIGRATION_PATH}/start
export const SIEM_RULE_MIGRATION_RETRY_PATH = `${SIEM_RULE_MIGRATION_PATH}/retry` as const;
export const SIEM_RULE_MIGRATION_STATS_PATH = `${SIEM_RULE_MIGRATION_PATH}/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_MIGRATIONS_PATH}/install` as const;
export const SIEM_RULE_MIGRATION_INSTALL_ALL_PATH =
`${SIEM_RULE_MIGRATIONS_PATH}/install_all` as const;

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

Expand All @@ -29,3 +34,6 @@ export enum SiemMigrationRuleTranslationResult {
PARTIAL = 'partial',
UNTRANSLATABLE = 'untranslatable',
}

export const DEFAULT_TRANSLATION_RISK_SCORE = 21;
export const DEFAULT_TRANSLATION_SEVERITY: Severity = 'low';
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ElasticRulePartial,
RuleMigrationTranslationResult,
RuleMigrationComments,
RuleMigrationToInstall,
RuleMigrationTaskStats,
RuleMigration,
RuleMigrationResourceData,
Expand Down Expand Up @@ -88,6 +89,41 @@ export type GetRuleMigrationStatsRequestParamsInput = z.input<
export type GetRuleMigrationStatsResponse = z.infer<typeof GetRuleMigrationStatsResponse>;
export const GetRuleMigrationStatsResponse = RuleMigrationTaskStats;

export type InstallAllMigrationRulesRequestBody = z.infer<
typeof InstallAllMigrationRulesRequestBody
>;
export const InstallAllMigrationRulesRequestBody = z.object({
/**
* The migration id
*/
migrationId: NonEmptyString,
});
export type InstallAllMigrationRulesRequestBodyInput = z.input<
typeof InstallAllMigrationRulesRequestBody
>;

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

export type InstallMigrationRulesRequestBody = z.infer<typeof InstallMigrationRulesRequestBody>;
export const InstallMigrationRulesRequestBody = z.array(RuleMigrationToInstall);
export type InstallMigrationRulesRequestBodyInput = z.input<
typeof InstallMigrationRulesRequestBody
>;

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

export type StartRuleMigrationRequestParams = z.infer<typeof StartRuleMigrationRequestParams>;
export const StartRuleMigrationRequestParams = z.object({
migration_id: NonEmptyString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,70 @@ paths:
type: boolean
description: Indicates rules migrations have been updated.

/internal/siem_migrations/rules/install:
semd marked this conversation as resolved.
Show resolved Hide resolved
post:
summary: Installs translated migration rules
operationId: InstallMigrationRules
x-codegen-enabled: true
description: Installs migration rules
tags:
- SIEM Rule Migrations
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
$ref: '../../rule_migration.schema.yaml#/components/schemas/RuleMigrationToInstall'
semd marked this conversation as resolved.
Show resolved Hide resolved
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/install_all:
semd marked this conversation as resolved.
Show resolved Hide resolved
post:
summary: Installs all translated migration rules
operationId: InstallAllMigrationRules
x-codegen-enabled: true
description: Installs all translated migration rules
tags:
- SIEM Rule Migrations
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- migrationId
properties:
migrationId:
description: The migration id
$ref: '../../common.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/stats:
get:
summary: Retrieves the stats for all rule migrations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@ export const RuleMigrationTaskStats = z.object({
last_updated_at: z.string(),
});

/**
* The rule migration to install.
*/
export type RuleMigrationToInstall = z.infer<typeof RuleMigrationToInstall>;
export const RuleMigrationToInstall = z.object({
/**
* The rule migration id
*/
id: NonEmptyString,
/**
* The migrated elastic rule attributes to install.
*/
elastic_rule: ElasticRule,
});

/**
* The type of the rule migration resource.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,20 @@ components:
items:
type: string

RuleMigrationToInstall:
description: The rule migration to install.
type: object
required:
- id
- elastic_rule
properties:
id:
description: The rule migration id
$ref: './common.schema.yaml#/components/schemas/NonEmptyString'
elastic_rule:
description: The migrated elastic rule attributes to install.
$ref: '#/components/schemas/ElasticRule'

## Rule migration resources

RuleMigrationResourceType:
Expand Down
14 changes: 14 additions & 0 deletions x-pack/plugins/security_solution/common/siem_migrations/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { RuleMigration } from './model/rule_migration.gen';

export const canInstallMigrationRule = (migrationRule: Partial<RuleMigration>) => {
return (
migrationRule.elastic_rule?.prebuilt_rule_id || migrationRule.translation_result === 'full'
semd marked this conversation as resolved.
Show resolved Hide resolved
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ import { KibanaServices } from '../../../common/lib/kibana';

import {
SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
SIEM_RULE_MIGRATION_INSTALL_ALL_PATH,
SIEM_RULE_MIGRATION_INSTALL_PATH,
SIEM_RULE_MIGRATION_PATH,
} from '../../../../common/siem_migrations/constants';
import type {
GetAllStatsRuleMigrationResponse,
GetRuleMigrationResponse,
InstallAllMigrationRulesResponse,
InstallMigrationRulesResponse,
} from '../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import type { InstallAllRulesProps, InstallRulesProps } from '../types';

/**
* Retrieves the stats for all the existing migrations, aggregated by `migration_id`.
Expand Down Expand Up @@ -64,3 +69,33 @@ export const getRuleMigrations = async ({
}
);
};

export const installMigrationRules = async ({
rules,
signal,
}: InstallRulesProps): Promise<InstallMigrationRulesResponse> => {
return KibanaServices.get().http.fetch<InstallMigrationRulesResponse>(
SIEM_RULE_MIGRATION_INSTALL_PATH,
{
method: 'POST',
version: '1',
body: JSON.stringify(rules),
signal,
}
);
};

export const installAllMigrationRules = async ({
migrationId,
signal,
}: InstallAllRulesProps): Promise<InstallAllMigrationRulesResponse> => {
return KibanaServices.get().http.fetch<InstallAllMigrationRulesResponse>(
SIEM_RULE_MIGRATION_INSTALL_ALL_PATH,
{
method: 'POST',
version: '1',
body: JSON.stringify({ migrationId }),
signal,
}
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
*/

import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { replaceParams } from '@kbn/openapi-common/shared';
import { useCallback } from 'react';
import { DEFAULT_QUERY_OPTIONS } from './constants';
import { getRuleMigrations } from '../api';
import type { GetRuleMigrationResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import { SIEM_RULE_MIGRATION_PATH } from '../../../../../common/siem_migrations/constants';

export const useGetRuleMigrationsQuery = (
export const useGetMigrationRulesQuery = (
migrationId: string,
options?: UseQueryOptions<GetRuleMigrationResponse>
) => {
Expand All @@ -31,3 +32,23 @@ export const useGetRuleMigrationsQuery = (
}
);
};

/**
* We should use this hook to invalidate the rule migrations cache. For
* example, rule migrations mutations, like installing a rule, should lead to cache invalidation.
*
* @returns A rule migrations cache invalidation callback
*/
export const useInvalidateGetMigrationRulesQuery = (migrationId: string) => {
const queryClient = useQueryClient();

const SPECIFIC_MIGRATION_PATH = replaceParams(SIEM_RULE_MIGRATION_PATH, {
migration_id: migrationId,
});

return useCallback(() => {
queryClient.invalidateQueries(['GET', SPECIFIC_MIGRATION_PATH], {
refetchType: 'active',
});
}, [SPECIFIC_MIGRATION_PATH, queryClient]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 { UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { InstallAllMigrationRulesResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import { SIEM_RULE_MIGRATION_INSTALL_ALL_PATH } from '../../../../../common/siem_migrations/constants';
import { installAllMigrationRules } from '../api';
import { useInvalidateGetMigrationRulesQuery } from './use_get_migration_rules_query';

export const INSTALL_ALL_MIGRATION_RULES_MUTATION_KEY = [
'POST',
SIEM_RULE_MIGRATION_INSTALL_ALL_PATH,
];

export const useInstallAllMigrationRulesMutation = (
migrationId: string,
options?: UseMutationOptions<InstallAllMigrationRulesResponse, Error>
) => {
const invalidateGetRuleMigrationsQuery = useInvalidateGetMigrationRulesQuery(migrationId);

return useMutation<InstallAllMigrationRulesResponse, Error>(
() => installAllMigrationRules({ migrationId }),
{
...options,
mutationKey: INSTALL_ALL_MIGRATION_RULES_MUTATION_KEY,
onSettled: (...args) => {
invalidateGetRuleMigrationsQuery();

if (options?.onSettled) {
options.onSettled(...args);
}
},
}
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 { UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { RuleMigrationToInstall } from '../../../../../common/siem_migrations/model/rule_migration.gen';
import type { InstallMigrationRulesResponse } from '../../../../../common/siem_migrations/model/api/rules/rule_migration.gen';
import { SIEM_RULE_MIGRATION_INSTALL_PATH } from '../../../../../common/siem_migrations/constants';
import { installMigrationRules } from '../api';
import { useInvalidateGetMigrationRulesQuery } from './use_get_migration_rules_query';

export const INSTALL_MIGRATION_RULES_MUTATION_KEY = ['POST', SIEM_RULE_MIGRATION_INSTALL_PATH];

export const useInstallMigrationRulesMutation = (
migrationId: string,
options?: UseMutationOptions<InstallMigrationRulesResponse, Error, RuleMigrationToInstall[]>
) => {
const invalidateGetRuleMigrationsQuery = useInvalidateGetMigrationRulesQuery(migrationId);

return useMutation<InstallMigrationRulesResponse, Error, RuleMigrationToInstall[]>(
(rules: RuleMigrationToInstall[]) => installMigrationRules({ rules }),
{
...options,
mutationKey: INSTALL_MIGRATION_RULES_MUTATION_KEY,
onSettled: (...args) => {
invalidateGetRuleMigrationsQuery();

if (options?.onSettled) {
options.onSettled(...args);
}
},
}
);
};
Loading