Skip to content

Commit

Permalink
[Rules migration] Add sorting functionality to rules migration table (e…
Browse files Browse the repository at this point in the history
…lastic#11379) (elastic#203396)

## Summary

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

These changes add sorting functionality to the migration rules table. It
is possible to sort migration rules by next columns: `Updated`, `Name`,
`Status`, `Risk Score`, `Severity` and `Author`.

### Other changes

Next fixes and adjustments were also implemented as part of this PR:
* `Installed` status in migration rules table to indicate whether the
rule was installed
* Rules selection and installation of selected rules
* Disable selection for not fully translated rules
* `Author` column to show whether the translated rule matched one of the
existing Elastic prebuilt rules
* `Install and enable` and `Install without enabling` buttons within the
migration rule details flyout
  • Loading branch information
e40pud authored Dec 9, 2024
1 parent ebb4f50 commit 70a5bb3
Show file tree
Hide file tree
Showing 30 changed files with 541 additions and 122 deletions.
1 change: 1 addition & 0 deletions packages/kbn-index-adapter/src/field_maps/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type FieldMap<T extends string = string> = Record<
array?: boolean;
doc_values?: boolean;
enabled?: boolean;
fields?: Record<string, { type: string }>;
format?: string;
ignore_above?: number;
multi_fields?: MultiField[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export type GetRuleMigrationRequestQuery = z.infer<typeof GetRuleMigrationReques
export const GetRuleMigrationRequestQuery = z.object({
page: z.coerce.number().optional(),
per_page: z.coerce.number().optional(),
sort_field: NonEmptyString.optional(),
sort_direction: z.enum(['asc', 'desc']).optional(),
search_term: z.string().optional(),
});
export type GetRuleMigrationRequestQueryInput = z.input<typeof GetRuleMigrationRequestQuery>;
Expand Down Expand Up @@ -154,7 +156,13 @@ export type InstallMigrationRulesRequestParamsInput = z.input<
>;

export type InstallMigrationRulesRequestBody = z.infer<typeof InstallMigrationRulesRequestBody>;
export const InstallMigrationRulesRequestBody = z.array(NonEmptyString);
export const InstallMigrationRulesRequestBody = z.object({
ids: z.array(NonEmptyString),
/**
* Indicates whether installed rules should be enabled
*/
enabled: z.boolean().optional(),
});
export type InstallMigrationRulesRequestBodyInput = z.input<
typeof InstallMigrationRulesRequestBody
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ paths:
required: false
schema:
type: number
- name: sort_field
in: query
required: false
schema:
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
- name: sort_direction
in: query
required: false
schema:
type: string
enum:
- asc
- desc
- name: search_term
in: query
required: false
Expand Down Expand Up @@ -180,10 +193,18 @@ paths:
content:
application/json:
schema:
type: array
items:
description: The rule migration id
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
type: object
required:
- ids
properties:
ids:
type: array
items:
description: The rule migration id
$ref: '../../../../../common/api/model/primitives.schema.yaml#/components/schemas/NonEmptyString'
enabled:
type: boolean
description: Indicates whether installed rules should be enabled
responses:
200:
description: Indicates rules migrations have been installed correctly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ export const isMigrationCustomRule = (rule?: ElasticRule): rule is MigrationCust
!isMigrationPrebuiltRule(rule) &&
!!(rule?.title && rule?.description && rule?.query && rule?.query_language);

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

...DEFAULT_TRANSLATION_FIELDS,
severity: (rule.severity as Severity) ?? DEFAULT_TRANSLATION_SEVERITY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ export interface GetRuleMigrationParams {
page?: number;
/** Optional number of documents per page to retrieve */
perPage?: number;
/** Optional field of the rule migration object to sort results by */
sortField?: string;
/** Optional direction to sort results by */
sortDirection?: 'asc' | 'desc';
/** Optional search term to filter documents */
searchTerm?: string;
/** Optional AbortSignal for cancelling request */
Expand All @@ -130,12 +134,24 @@ export const getRuleMigrations = async ({
migrationId,
page,
perPage,
sortField,
sortDirection,
searchTerm,
signal,
}: GetRuleMigrationParams): Promise<GetRuleMigrationResponse> => {
return KibanaServices.get().http.get<GetRuleMigrationResponse>(
replaceParams(SIEM_RULE_MIGRATION_PATH, { migration_id: migrationId }),
{ version: '1', query: { page, per_page: perPage, search_term: searchTerm }, signal }
{
version: '1',
query: {
page,
per_page: perPage,
sort_field: sortField,
sort_direction: sortDirection,
search_term: searchTerm,
},
signal,
}
);
};

Expand Down Expand Up @@ -163,18 +179,21 @@ export interface InstallRulesParams {
migrationId: string;
/** The rule ids to install */
ids: string[];
/** Optional indicator to enable the installed rule */
enabled?: boolean;
/** Optional AbortSignal for cancelling request */
signal?: AbortSignal;
}
/** Installs the provided rule ids for a specific migration. */
export const installMigrationRules = async ({
migrationId,
ids,
enabled,
signal,
}: InstallRulesParams): Promise<InstallMigrationRulesResponse> => {
return KibanaServices.get().http.post<InstallMigrationRulesResponse>(
replaceParams(SIEM_RULE_MIGRATION_INSTALL_PATH, { migration_id: migrationId }),
{ version: '1', body: JSON.stringify(ids), signal }
{ version: '1', body: JSON.stringify({ ids, enabled }), signal }
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
const rule = useMemo(() => {
if (isMigrationCustomRule(ruleMigration.elastic_rule)) {
return convertMigrationCustomRuleToSecurityRulePayload(
ruleMigration.elastic_rule
ruleMigration.elastic_rule,
false
) as RuleResponse; // TODO: we need to adjust RuleOverviewTab to allow partial RuleResponse as a parameter;
}
return matchedPrebuiltRule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
*/

import React from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import {
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiLoadingSpinner,
} from '@elastic/eui';
import * as i18n from './translations';

export interface BulkActionsProps {
Expand All @@ -29,21 +35,22 @@ export const BulkActions: React.FC<BulkActionsProps> = React.memo(
installSelectedRule,
}) => {
const disableInstallTranslatedRulesButton = isTableLoading || !numberOfTranslatedRules;
const showInstallSelectedRulesButton =
disableInstallTranslatedRulesButton && numberOfSelectedRules > 0;
const showInstallSelectedRulesButton = isTableLoading || numberOfSelectedRules > 0;
return (
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
{showInstallSelectedRulesButton ? (
<EuiFlexItem grow={false}>
<EuiButton
<EuiButtonEmpty
iconType="plusInCircle"
color={'primary'}
onClick={installSelectedRule}
disabled={isTableLoading}
data-test-subj="installSelectedRulesButton"
aria-label={i18n.INSTALL_SELECTED_ARIA_LABEL}
>
{i18n.INSTALL_SELECTED_RULES(numberOfSelectedRules)}
{isTableLoading && <EuiLoadingSpinner size="s" />}
</EuiButton>
</EuiButtonEmpty>
</EuiFlexItem>
) : null}
<EuiFlexItem grow={false}>
Expand Down
Loading

0 comments on commit 70a5bb3

Please sign in to comment.