Skip to content

Commit

Permalink
Reduce the _review rule upgrade endpoint response size
Browse files Browse the repository at this point in the history
  • Loading branch information
xcrzx committed Feb 21, 2025
1 parent 705df21 commit 024e246
Show file tree
Hide file tree
Showing 39 changed files with 948 additions and 750 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { z } from '@kbn/zod';

export enum RuleCustomizationStatus {
CUSTOMIZED = 'CUSTOMIZED',
NOT_CUSTOMIZED = 'NOT_CUSTOMIZED',
}

export type PrebuiltRuleFilter = z.infer<typeof PrebuiltRuleFilter>;
export const PrebuiltRuleFilter = z.object({
/**
* Rule IDs to return upgrade info for
*/
rule_ids: z.array(z.string()).optional(),
/**
* Tags to filter by
*/
tags: z.array(z.string()).optional(),
/**
* Rule name to filter by
*/
name: z.string().optional(),
/**
* Rule customization status to filter by
*/
customization_status: z.nativeEnum(RuleCustomizationStatus).optional(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@
export interface GetPrebuiltRulesStatusResponseBody {
/** Aggregated info about all prebuilt rules */
stats: PrebuiltRulesStatusStats;

/**
* Aggregated info about upgradeable prebuilt rules. This fields is optional
* for backward compatibility. After one serverless release cycle, it can be
* made required.
* */
aggregated_fields?: {
upgradeable_rules: {
/** List of all tags of the current versions of upgradeable rules */
tags: string[];
};
};
}

export interface PrebuiltRulesStatusStats {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './model/diff/three_way_diff/three_way_diff_outcome';
export * from './model/diff/three_way_diff/three_way_diff';
export * from './model/diff/three_way_diff/three_way_diff_conflict';
export * from './model/diff/three_way_diff/three_way_merge_outcome';
export * from './common/prebuilt_rule_filter';
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { mapValues } from 'lodash';
import { RuleResponse } from '../../model/rule_schema/rule_schemas.gen';
import { AggregatedPrebuiltRuleError, DiffableAllFields } from '../model';
import { RuleSignatureId, RuleVersion } from '../../model';
import { PrebuiltRuleFilter } from '../common/prebuilt_rule_filter';

export type Mode = z.infer<typeof Mode>;
export const Mode = z.enum(['ALL_RULES', 'SPECIFIC_RULES']);
Expand Down Expand Up @@ -111,21 +112,31 @@ export const RuleUpgradeSpecifier = z.object({
fields: RuleFieldsToUpgrade.optional(),
});

export type UpgradeConflictResolution = z.infer<typeof UpgradeConflictResolution>;
export const UpgradeConflictResolution = z.enum(['SKIP', 'OVERWRITE']);
export type UpgradeConflictResolutionEnum = typeof UpgradeConflictResolution.enum;
export const UpgradeConflictResolutionEnum = UpgradeConflictResolution.enum;

export type UpgradeSpecificRulesRequest = z.infer<typeof UpgradeSpecificRulesRequest>;
export const UpgradeSpecificRulesRequest = z.object({
mode: z.literal('SPECIFIC_RULES'),
rules: z.array(RuleUpgradeSpecifier).min(1),
pick_version: PickVersionValues.optional(),
on_conflict: UpgradeConflictResolution.optional(),
dry_run: z.boolean().optional(),
});

export type UpgradeAllRulesRequest = z.infer<typeof UpgradeAllRulesRequest>;
export const UpgradeAllRulesRequest = z.object({
mode: z.literal('ALL_RULES'),
pick_version: PickVersionValues.optional(),
filter: PrebuiltRuleFilter.optional(),
on_conflict: UpgradeConflictResolution.optional(),
dry_run: z.boolean().optional(),
});

export type SkipRuleUpgradeReason = z.infer<typeof SkipRuleUpgradeReason>;
export const SkipRuleUpgradeReason = z.enum(['RULE_UP_TO_DATE']);
export const SkipRuleUpgradeReason = z.enum(['RULE_UP_TO_DATE', 'CONFLICT']);
export type SkipRuleUpgradeReasonEnum = typeof SkipRuleUpgradeReason.enum;
export const SkipRuleUpgradeReasonEnum = SkipRuleUpgradeReason.enum;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,86 @@
* 2.0.
*/

import type { RuleObjectId, RuleSignatureId, RuleTagArray } from '../../model';
import { z } from '@kbn/zod';
import { SortOrder, type RuleObjectId, type RuleSignatureId, type RuleTagArray } from '../../model';
import type { PartialRuleDiff } from '../model';
import type { RuleResponse } from '../../model/rule_schema';
import type { RuleResponse, RuleVersion } from '../../model/rule_schema';
import { FindRulesSortField } from '../../rule_management';
import { PrebuiltRuleFilter } from '../common/prebuilt_rule_filter';

export type ReviewRuleUpgradeSort = z.infer<typeof ReviewRuleUpgradeSort>;
export const ReviewRuleUpgradeSort = z.object({
/**
* Field to sort by
*/
field: FindRulesSortField.optional(),
/**
* Sort order
*/
order: SortOrder.optional(),
});

export type ReviewRuleUpgradeRequestBody = z.infer<typeof ReviewRuleUpgradeRequestBody>;
export const ReviewRuleUpgradeRequestBody = z
.object({
filter: PrebuiltRuleFilter.optional(),
sort: ReviewRuleUpgradeSort.optional(),

page: z.coerce.number().int().min(1).optional().default(1),
/**
* Rules per page
*/
per_page: z.coerce.number().int().min(0).optional().default(20),
})
.nullable();

export interface ReviewRuleUpgradeResponseBody {
/** Aggregated info about all rules available for upgrade */
/**
* @deprecated Use the prebuilt rule status API instead. The field is kept
* here for backward compatibility but can be removed after one Serverless
* release.
*/
stats: RuleUpgradeStatsForReview;

/** Info about individual rules: one object per each rule available for upgrade */
rules: RuleUpgradeInfoForReview[];

/** The requested page number */
page: number;

/** The requested number of items per page */
per_page: number;

/** The total number of rules available for upgrade that match the filter criteria */
total: number;
}

export interface RuleUpgradeStatsForReview {
/** Number of installed prebuilt rules available for upgrade (stock + customized) */
/**
* @deprecated Always 0
*/
num_rules_to_upgrade_total: number;

/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVABLE) */
/**
* @deprecated Always 0
*/
num_rules_with_conflicts: number;

/** Number of installed prebuilt rules with NON_SOLVABLE upgrade conflicts */
/**
* @deprecated Always 0
*/
num_rules_with_non_solvable_conflicts: number;

/** A union of all tags of all rules available for upgrade */
/**
* @deprecated Always an empty array
*/
tags: RuleTagArray;
}

export interface RuleUpgradeInfoForReview {
id: RuleObjectId;
rule_id: RuleSignatureId;
version: RuleVersion;
current_rule: RuleResponse;
target_rule: RuleResponse;
diff: PartialRuleDiff;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export const TAGS_FIELD = 'alert.attributes.tags';
export const PARAMS_TYPE_FIELD = 'alert.attributes.params.type';
export const PARAMS_IMMUTABLE_FIELD = 'alert.attributes.params.immutable';
export const LAST_RUN_OUTCOME_FIELD = 'alert.attributes.lastRun.outcome';
export const IS_CUSTOMIZED_FIELD = 'alert.attributes.params.ruleSource.isCustomized';
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import type { RuleExecutionStatus } from '../../api/detection_engine';
import { RuleExecutionStatusEnum } from '../../api/detection_engine';
import { RuleCustomizationStatus, RuleExecutionStatusEnum } from '../../api/detection_engine';
import { prepareKQLStringParam } from '../../utils/kql';
import {
ENABLED_FIELD,
IS_CUSTOMIZED_FIELD,
LAST_RUN_OUTCOME_FIELD,
PARAMS_IMMUTABLE_FIELD,
PARAMS_TYPE_FIELD,
Expand All @@ -23,6 +24,8 @@ export const KQL_FILTER_IMMUTABLE_RULES = `${PARAMS_IMMUTABLE_FIELD}: true`;
export const KQL_FILTER_MUTABLE_RULES = `${PARAMS_IMMUTABLE_FIELD}: false`;
export const KQL_FILTER_ENABLED_RULES = `${ENABLED_FIELD}: true`;
export const KQL_FILTER_DISABLED_RULES = `${ENABLED_FIELD}: false`;
export const KQL_FILTER_CUSTOMIZED_RULES = `${IS_CUSTOMIZED_FIELD}: true`;
export const KQL_FILTER_NOT_CUSTOMIZED_RULES = `${IS_CUSTOMIZED_FIELD}: false`;

interface RulesFilterOptions {
filter: string;
Expand All @@ -32,6 +35,7 @@ interface RulesFilterOptions {
tags: string[];
excludeRuleTypes: Type[];
ruleExecutionStatus: RuleExecutionStatus;
customizationStatus: RuleCustomizationStatus;
ruleIds: string[];
}

Expand All @@ -50,6 +54,7 @@ export function convertRulesFilterToKQL({
tags,
excludeRuleTypes = [],
ruleExecutionStatus,
customizationStatus,
}: Partial<RulesFilterOptions>): string {
const kql: string[] = [];

Expand Down Expand Up @@ -85,6 +90,12 @@ export function convertRulesFilterToKQL({
kql.push(`${LAST_RUN_OUTCOME_FIELD}: "failed"`);
}

if (customizationStatus === RuleCustomizationStatus.CUSTOMIZED) {
kql.push(KQL_FILTER_CUSTOMIZED_RULES);
} else if (customizationStatus === RuleCustomizationStatus.NOT_CUSTOMIZED) {
kql.push(KQL_FILTER_NOT_CUSTOMIZED_RULES);
}

return kql.join(' AND ');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import type { ActionType, AsApiContract } from '@kbn/actions-plugin/common';
import type { ActionResult } from '@kbn/actions-plugin/server';
import { convertRulesFilterToKQL } from '../../../../common/detection_engine/rule_management/rule_filtering';
import type {
UpgradeSpecificRulesRequest,
PickVersionValues,
PerformRuleUpgradeResponseBody,
InstallSpecificRulesRequest,
PerformRuleInstallationResponseBody,
GetPrebuiltRulesStatusResponseBody,
ReviewRuleUpgradeResponseBody,
ReviewRuleInstallationResponseBody,
ReviewRuleUpgradeRequestBody,
PerformRuleUpgradeRequestBody,
} from '../../../../common/api/detection_engine/prebuilt_rules';
import type {
BulkDuplicateRules,
Expand Down Expand Up @@ -637,13 +637,16 @@ export const getPrebuiltRulesStatus = async ({
*/
export const reviewRuleUpgrade = async ({
signal,
request,
}: {
signal: AbortSignal | undefined;
request: ReviewRuleUpgradeRequestBody;
}): Promise<ReviewRuleUpgradeResponseBody> =>
KibanaServices.get().http.fetch(REVIEW_RULE_UPGRADE_URL, {
method: 'POST',
version: '1',
signal,
body: JSON.stringify(request),
});

/**
Expand Down Expand Up @@ -685,23 +688,13 @@ export const performInstallSpecificRules = async (
}),
});

export interface PerformUpgradeRequest {
rules: UpgradeSpecificRulesRequest['rules'];
pickVersion: PickVersionValues;
}

export const performUpgradeSpecificRules = async ({
rules,
pickVersion,
}: PerformUpgradeRequest): Promise<PerformRuleUpgradeResponseBody> =>
export const performUpgradeRules = async (
body: PerformRuleUpgradeRequestBody
): Promise<PerformRuleUpgradeResponseBody> =>
KibanaServices.get().http.fetch(PERFORM_RULE_UPGRADE_URL, {
method: 'POST',
version: '1',
body: JSON.stringify({
mode: 'SPECIFIC_RULES',
rules,
pick_version: pickVersion,
}),
body: JSON.stringify(body),
});

export const bootstrapPrebuiltRules = async (): Promise<BootstrapPrebuiltRulesResponse> =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { reviewRuleInstall } from '../../api';
import { REVIEW_RULE_INSTALLATION_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules/urls';
import type { ReviewRuleInstallationResponseBody } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { DEFAULT_QUERY_OPTIONS } from '../constants';
import { retryOnRateLimitedError } from './retry_on_rate_limited_error';
import { cappedExponentialBackoff } from './capped_exponential_backoff';

export const REVIEW_RULE_INSTALLATION_QUERY_KEY = ['POST', REVIEW_RULE_INSTALLATION_URL];

Expand All @@ -28,8 +26,6 @@ export const useFetchPrebuiltRulesInstallReviewQuery = (
{
...DEFAULT_QUERY_OPTIONS,
...options,
retry: retryOnRateLimitedError,
retryDelay: cappedExponentialBackoff,
}
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useCallback } from 'react';
import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import type { PrebuiltRulesStatusStats } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { useCallback } from 'react';
import type { GetPrebuiltRulesStatusResponseBody } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { GET_PREBUILT_RULES_STATUS_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { getPrebuiltRulesStatus } from '../../api';
import { DEFAULT_QUERY_OPTIONS } from '../constants';
import { GET_PREBUILT_RULES_STATUS_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules';

export const PREBUILT_RULES_STATUS_QUERY_KEY = ['GET', GET_PREBUILT_RULES_STATUS_URL];

export const useFetchPrebuiltRulesStatusQuery = (
options?: UseQueryOptions<PrebuiltRulesStatusStats>
options?: UseQueryOptions<GetPrebuiltRulesStatusResponseBody>
) => {
return useQuery<PrebuiltRulesStatusStats>(
return useQuery<GetPrebuiltRulesStatusResponseBody>(
PREBUILT_RULES_STATUS_QUERY_KEY,
async ({ signal }) => {
const response = await getPrebuiltRulesStatus({ signal });
return response.stats;
return response;
},
{
...DEFAULT_QUERY_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { reviewRuleUpgrade } from '../../api';
import { REVIEW_RULE_UPGRADE_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules/urls';
import type { ReviewRuleUpgradeResponseBody } from '../../../../../../common/api/detection_engine/prebuilt_rules';
import type {
ReviewRuleUpgradeRequestBody,
ReviewRuleUpgradeResponseBody,
} from '../../../../../../common/api/detection_engine/prebuilt_rules';
import { DEFAULT_QUERY_OPTIONS } from '../constants';
import { retryOnRateLimitedError } from './retry_on_rate_limited_error';
import { cappedExponentialBackoff } from './capped_exponential_backoff';

export const REVIEW_RULE_UPGRADE_QUERY_KEY = ['POST', REVIEW_RULE_UPGRADE_URL];

export const useFetchPrebuiltRulesUpgradeReviewQuery = (
request: ReviewRuleUpgradeRequestBody,
options?: UseQueryOptions<ReviewRuleUpgradeResponseBody>
) => {
return useQuery<ReviewRuleUpgradeResponseBody>(
REVIEW_RULE_UPGRADE_QUERY_KEY,
[...REVIEW_RULE_UPGRADE_QUERY_KEY, request],
async ({ signal }) => {
const response = await reviewRuleUpgrade({ signal });
const response = await reviewRuleUpgrade({ signal, request });
return response;
},
{
Expand Down
Loading

0 comments on commit 024e246

Please sign in to comment.