Skip to content

Commit

Permalink
[8.12] [Security Solution] Fix rule export for a large number of rules (
Browse files Browse the repository at this point in the history
#175979) (#176211)

# Backport

This will backport the following commits from `main` to `8.12`:
- [[Security Solution] Fix rule export for a large number of rules
(#175979)](#175979)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Maxim
Palenov","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-02-05T11:33:16Z","message":"[Security
Solution] Fix rule export for a large number of rules
(#175979)\n\n**Fixes:**
https://github.com/elastic/kibana/issues/170015\r\n\r\n##
Summary\r\n\r\nThis PR fixes inability to export a large number of
rules.\r\n\r\n## Details\r\n\r\nThe problem appears as 500 server error
shown by UI in attempt to export a large number of rules (1K or more).
In fact it boils down to `too_many_clauses` ES error. Server side
fetches rules by passing each `ruleId` in ES query. When the number of
`ruleIds` exceeds the limit `too_many_clauses` error is returned. The
limit is set via `indices.query.bool.max_clause_count` and the value is
[calculated
dynamically](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-settings.html)
with the minimum value `1024`.\r\n\r\nThe fix makes sure rules are
processed in chunks by 1024 to prevent `too_many_clauses` error and
guarantee it works in different environment.\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common
scenarios","sha":"576fe37b16db4fb6e1224387d5485ddb8fed6787","branchLabelMapping":{"^v8.13.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","impact:high","Team:Detections
and Resp","Team: SecuritySolution","Team:Detection Rule
Management","Feature:Rule
Import/Export","v8.13.0","v8.12.2"],"title":"[Security Solution] Fix
rule export for a large number of
rules","number":175979,"url":"https://github.com/elastic/kibana/pull/175979","mergeCommit":{"message":"[Security
Solution] Fix rule export for a large number of rules
(#175979)\n\n**Fixes:**
https://github.com/elastic/kibana/issues/170015\r\n\r\n##
Summary\r\n\r\nThis PR fixes inability to export a large number of
rules.\r\n\r\n## Details\r\n\r\nThe problem appears as 500 server error
shown by UI in attempt to export a large number of rules (1K or more).
In fact it boils down to `too_many_clauses` ES error. Server side
fetches rules by passing each `ruleId` in ES query. When the number of
`ruleIds` exceeds the limit `too_many_clauses` error is returned. The
limit is set via `indices.query.bool.max_clause_count` and the value is
[calculated
dynamically](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-settings.html)
with the minimum value `1024`.\r\n\r\nThe fix makes sure rules are
processed in chunks by 1024 to prevent `too_many_clauses` error and
guarantee it works in different environment.\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common
scenarios","sha":"576fe37b16db4fb6e1224387d5485ddb8fed6787"}},"sourceBranch":"main","suggestedTargetBranches":["8.12"],"targetPullRequestStates":[{"branch":"main","label":"v8.13.0","branchLabelMappingKey":"^v8.13.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/175979","number":175979,"mergeCommit":{"message":"[Security
Solution] Fix rule export for a large number of rules
(#175979)\n\n**Fixes:**
https://github.com/elastic/kibana/issues/170015\r\n\r\n##
Summary\r\n\r\nThis PR fixes inability to export a large number of
rules.\r\n\r\n## Details\r\n\r\nThe problem appears as 500 server error
shown by UI in attempt to export a large number of rules (1K or more).
In fact it boils down to `too_many_clauses` ES error. Server side
fetches rules by passing each `ruleId` in ES query. When the number of
`ruleIds` exceeds the limit `too_many_clauses` error is returned. The
limit is set via `indices.query.bool.max_clause_count` and the value is
[calculated
dynamically](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-settings.html)
with the minimum value `1024`.\r\n\r\nThe fix makes sure rules are
processed in chunks by 1024 to prevent `too_many_clauses` error and
guarantee it works in different environment.\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common
scenarios","sha":"576fe37b16db4fb6e1224387d5485ddb8fed6787"}},{"branch":"8.12","label":"v8.12.2","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Maxim Palenov <[email protected]>
  • Loading branch information
kibanamachine and maximpn authored Feb 5, 2024
1 parent c97a4a4 commit 43a6eb6
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 701 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,10 @@ export const nonRuleAlert = () => ({
alertTypeId: 'something',
});

export const getRuleMock = <T extends RuleParams>(params: T): SanitizedRule<T> => ({
export const getRuleMock = <T extends RuleParams>(
params: T,
rewrites?: Partial<SanitizedRule<T>>
): SanitizedRule<T> => ({
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
name: 'Detect Root/Admin Users',
tags: [],
Expand All @@ -377,6 +380,7 @@ export const getRuleMock = <T extends RuleParams>(params: T): SanitizedRule<T> =
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
},
revision: 0,
...rewrites,
});

export const resolveRuleMock = <T extends RuleParams>(params: T): ResolvedSanitizedRule<T> => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,7 @@ export const performBulkActionRoute = (
const exported = await getExportByObjectIds(
rulesClient,
exceptionsClient,
savedObjectsClient,
rules.map(({ params }) => ({ rule_id: params.ruleId })),
logger,
rules.map(({ params }) => params.ruleId),
exporter,
request,
actionsClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ export const exportRulesRoute = (
const exceptionsClient = (await context.lists)?.getExceptionListClient();
const actionsClient = (await context.actions)?.getActionsClient();

const {
getExporter,
getClient,
client: savedObjectsClient,
} = (await context.core).savedObjects;
const { getExporter, getClient } = (await context.core).savedObjects;

const client = getClient({ includedHiddenTypes: ['action'] });
const actionsExporter = getExporter(client);
Expand Down Expand Up @@ -83,18 +79,14 @@ export const exportRulesRoute = (
? await getExportByObjectIds(
rulesClient,
exceptionsClient,
savedObjectsClient,
request.body.objects,
logger,
request.body.objects.map((obj) => obj.rule_id),
actionsExporter,
request,
actionsClient
)
: await getExportAll(
rulesClient,
exceptionsClient,
savedObjectsClient,
logger,
actionsExporter,
request,
actionsClient
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { RuleResponse } from '../../../../../../common/api/detection_engine/model/rule_schema';

export type ExportableRule = Omit<RuleResponse, 'execution_summary'>;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { FindHit } from '../../../routes/__mocks__/request_responses';
import {
getRuleMock,
getFindResultWithSingleHit,
getEmptySavedObjectsResponse,
} from '../../../routes/__mocks__/request_responses';
import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks';
import { getExportAll } from './get_export_all';
Expand All @@ -22,8 +21,6 @@ import {

import { getQueryRuleParams } from '../../../rule_schema/mocks';
import { getExceptionListClientMock } from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client.mock';
import type { loggingSystemMock } from '@kbn/core/server/mocks';
import { requestContextMock } from '../../../routes/__mocks__/request_context';
import { savedObjectsExporterMock } from '@kbn/core-saved-objects-import-export-server-mocks';
import { mockRouter } from '@kbn/core-http-router-server-mocks';
import { Readable } from 'stream';
Expand Down Expand Up @@ -54,13 +51,11 @@ const connectors = [
},
];
describe('getExportAll', () => {
let logger: ReturnType<typeof loggingSystemMock.createLogger>;
const { clients } = requestContextMock.createTools();
const exporterMock = savedObjectsExporterMock.create();
const requestMock = mockRouter.createKibanaRequest();
const actionsClient = actionsClientMock.create();

beforeEach(async () => {
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse());
actionsClient.getAll.mockImplementation(async () => {
return connectors;
});
Expand All @@ -85,8 +80,6 @@ describe('getExportAll', () => {
const exports = await getExportAll(
rulesClient,
exceptionsClient,
clients.savedObjectsClient,
logger,
exporterMock,
requestMock,
actionsClient
Expand Down Expand Up @@ -172,8 +165,6 @@ describe('getExportAll', () => {
const exports = await getExportAll(
rulesClient,
exceptionsClient,
clients.savedObjectsClient,
logger,
exporterMock,
requestMock,
actionsClient
Expand Down Expand Up @@ -258,8 +249,6 @@ describe('getExportAll', () => {
const exports = await getExportAll(
rulesClient,
exceptionsClient,
clients.savedObjectsClient,
logger,
exporterMockWithConnector as never,
requestMock,
actionsClient
Expand Down Expand Up @@ -401,8 +390,6 @@ describe('getExportAll', () => {
const exports = await getExportAll(
rulesClient,
exceptionsClient,
clients.savedObjectsClient,
logger,
exporterMockWithConnector as never,
requestMock,
actionsClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@

import { transformDataToNdjson } from '@kbn/securitysolution-utils';

import type { ISavedObjectsExporter, KibanaRequest, Logger } from '@kbn/core/server';
import type { ISavedObjectsExporter, KibanaRequest } from '@kbn/core/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { RulesClient, RuleExecutorServices } from '@kbn/alerting-plugin/server';
import type { RulesClient } from '@kbn/alerting-plugin/server';
import type { ActionsClient } from '@kbn/actions-plugin/server';
import { getNonPackagedRules } from '../search/get_existing_prepackaged_rules';
import { getExportDetailsNdjson } from './get_export_details_ndjson';
import { transformAlertsToRules, transformRuleToExportableFormat } from '../../utils/utils';
import { transformAlertsToRules } from '../../utils/utils';
import { getRuleExceptionsForExport } from './get_export_rule_exceptions';
import { getRuleActionConnectorsForExport } from './get_export_rule_action_connectors';
import { transformRuleToExportableFormat } from './transform_rule_to_exportable_format';

export const getExportAll = async (
rulesClient: RulesClient,
exceptionsClient: ExceptionListClient | undefined,
savedObjectsClient: RuleExecutorServices['savedObjectsClient'],
logger: Logger,
actionsExporter: ISavedObjectsExporter,
request: KibanaRequest,
actionsClient: ActionsClient
Expand Down
Loading

0 comments on commit 43a6eb6

Please sign in to comment.