[Security Solution] DetectionRulesClient: various refactorings (#184954)

**Partially addresses: https://github.com/elastic/kibana/issues/184364**

## Summary
This PR contains various smaller-scale refactorings for the recently
added `DetectionsRuleClient`.

**Changes**:
- Renamed `DetectionRulesClient` containing directory from
`rule_management` to `detection_rules_client`
- Moved `DetectionRulesClient` methods into the
`detection_rules_client/methods` dir
- Moved the TS interface of `DetectionRulesClient` into a separate file
`detection_rules_client_interface.ts`
- Simplified `importRule` method parameters
- Added memoization to `getDetectionRulesClient`

---------

Co-authored-by: Georgii Gorbachev <banderror@gmail.com>
This commit is contained in:
Nikita Indik 2024-06-08 01:21:58 +02:00 committed by GitHub
parent 87f6c1d4b0
commit 41c34e09c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 519 additions and 512 deletions

View file

@ -9,7 +9,7 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/consta
import { initPromisePool } from '../../../../../utils/promise_pool';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset';
import type { IDetectionRulesClient } from '../../../rule_management/logic/rule_management/detection_rules_client';
import type { IDetectionRulesClient } from '../../../rule_management/logic/detection_rules_client/detection_rules_client_interface';
export const createPrebuiltRules = (
detectionRulesClient: IDetectionRulesClient,

View file

@ -9,7 +9,7 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/consta
import { initPromisePool } from '../../../../../utils/promise_pool';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset';
import type { IDetectionRulesClient } from '../../../rule_management/logic/rule_management/detection_rules_client';
import type { IDetectionRulesClient } from '../../../rule_management/logic/detection_rules_client/detection_rules_client_interface';
/**
* Upgrades existing prebuilt rules given a set of rules and output index.

View file

@ -38,7 +38,7 @@ import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/
import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock';
import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock';
import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
import { detectionRulesClientMock } from '../../rule_management/logic/rule_management/__mocks__/detection_rules_client';
import { detectionRulesClientMock } from '../../rule_management/logic/detection_rules_client/__mocks__/detection_rules_client';
export const createMockClients = () => {
const core = coreMock.createRequestHandlerContext();

View file

@ -37,13 +37,13 @@ import {
CreateRuleExceptionsRequestParams,
} from '../../../../../../common/api/detection_engine/rule_exceptions';
import { readRules } from '../../../rule_management/logic/rule_management/read_rules';
import { readRules } from '../../../rule_management/logic/detection_rules_client/read_rules';
import { checkDefaultRuleExceptionListReferences } from '../../../rule_management/logic/exceptions/check_for_default_rule_exception_list';
import type { RuleParams } from '../../../rule_schema';
import type { SecuritySolutionPluginRouter } from '../../../../../types';
import { buildSiemResponse } from '../../../routes/utils';
import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation';
import type { IDetectionRulesClient } from '../../../rule_management/logic/rule_management/detection_rules_client';
import type { IDetectionRulesClient } from '../../../rule_management/logic/detection_rules_client/detection_rules_client_interface';
export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) => {
router.versioned

View file

@ -11,7 +11,7 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../../common/con
import type { PromisePoolOutcome } from '../../../../../../utils/promise_pool';
import { initPromisePool } from '../../../../../../utils/promise_pool';
import type { RuleAlertType } from '../../../../rule_schema';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { findRules } from '../../../logic/search/find_rules';
import { MAX_RULES_TO_PROCESS_TOTAL } from './route';

View file

@ -30,10 +30,10 @@ import {
getBulkDisableRuleActionSchemaMock,
} from '../../../../../../../common/api/detection_engine/rule_management/mocks';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
jest.mock('../../../../../machine_learning/authz');
jest.mock('../../../logic/rule_management/read_rules', () => ({ readRules: jest.fn() }));
jest.mock('../../../logic/detection_rules_client/read_rules', () => ({ readRules: jest.fn() }));
describe('Perform bulk action route', () => {
const readRulesMock = readRules as jest.Mock;

View file

@ -16,7 +16,7 @@ import {
} from '../../../../../../../common/api/detection_engine/rule_management';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getDuplicates } from './get_duplicates';
import { transformValidateBulkError } from '../../../utils/validate';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';

View file

@ -24,7 +24,7 @@ import {
createBulkErrorObject,
transformBulkError,
} from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getIdBulkError } from '../../../utils/utils';
import { transformValidateBulkError } from '../../../utils/validate';
import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation';

View file

@ -19,7 +19,7 @@ import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { transformBulkError, buildSiemResponse } from '../../../../routes/utils';
import { getIdBulkError } from '../../../utils/utils';
import { transformValidateBulkError } from '../../../utils/validate';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation';
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list';

View file

@ -24,7 +24,7 @@ import {
buildSiemResponse,
createBulkErrorObject,
} from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation';
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list';

View file

@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list';
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate';

View file

@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getIdError, transform } from '../../../utils/utils';
export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => {

View file

@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list';
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
import { getIdError } from '../../../utils/utils';

View file

@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { getIdError, transform } from '../../../utils/utils';
export const readRuleRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {

View file

@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation';
import { buildSiemResponse } from '../../../../routes/utils';
import { readRules } from '../../../logic/rule_management/read_rules';
import { readRules } from '../../../logic/detection_rules_client/read_rules';
import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list';
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
import { getIdError } from '../../../utils/utils';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import type { IDetectionRulesClient } from '../detection_rules_client';
import type { IDetectionRulesClient } from '../detection_rules_client_interface';
export type DetectionRulesClientMock = jest.Mocked<IDetectionRulesClient>;

View file

@ -16,7 +16,7 @@ import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constant
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');

View file

@ -16,7 +16,7 @@ import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constant
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');

View file

@ -8,7 +8,7 @@
import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks';
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');

View file

@ -13,7 +13,7 @@ import { getQueryRuleParams } from '../../../rule_schema/mocks';
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');
@ -50,7 +50,7 @@ describe('DetectionRulesClient.importRule', () => {
await detectionRulesClient.importRule({
ruleToImport,
overwriteRules: true,
options: { allowMissingConnectorSecrets },
allowMissingConnectorSecrets,
});
expect(rulesClient.create).toHaveBeenCalledWith(
@ -78,7 +78,7 @@ describe('DetectionRulesClient.importRule', () => {
detectionRulesClient.importRule({
ruleToImport,
overwriteRules: true,
options: { allowMissingConnectorSecrets },
allowMissingConnectorSecrets,
})
).rejects.toThrow('mocked MLAuth error');
@ -92,7 +92,7 @@ describe('DetectionRulesClient.importRule', () => {
await detectionRulesClient.importRule({
ruleToImport,
overwriteRules: true,
options: { allowMissingConnectorSecrets },
allowMissingConnectorSecrets,
});
expect(rulesClient.update).toHaveBeenCalledWith(
@ -133,7 +133,7 @@ describe('DetectionRulesClient.importRule', () => {
timestamp_override: undefined,
},
overwriteRules: true,
options: { allowMissingConnectorSecrets },
allowMissingConnectorSecrets,
});
expect(rulesClient.create).not.toHaveBeenCalled();
@ -154,7 +154,7 @@ describe('DetectionRulesClient.importRule', () => {
detectionRulesClient.importRule({
ruleToImport,
overwriteRules: false,
options: { allowMissingConnectorSecrets },
allowMissingConnectorSecrets,
})
).rejects.toMatchObject({
error: {

View file

@ -17,7 +17,7 @@ import { readRules } from './read_rules';
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');

View file

@ -0,0 +1,78 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType } from '../../../rule_schema';
import type {
IDetectionRulesClient,
CreateCustomRuleArgs,
CreatePrebuiltRuleArgs,
UpdateRuleArgs,
PatchRuleArgs,
DeleteRuleArgs,
UpgradePrebuiltRuleArgs,
ImportRuleArgs,
} from './detection_rules_client_interface';
import { createCustomRule } from './methods/create_custom_rule';
import { createPrebuiltRule } from './methods/create_prebuilt_rule';
import { updateRule } from './methods/update_rule';
import { patchRule } from './methods/patch_rule';
import { deleteRule } from './methods/delete_rule';
import { upgradePrebuiltRule } from './methods/upgrade_prebuilt_rule';
import { importRule } from './methods/import_rule';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
export const createDetectionRulesClient = (
rulesClient: RulesClient,
mlAuthz: MlAuthz
): IDetectionRulesClient => ({
async createCustomRule(args: CreateCustomRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.createCustomRule', async () => {
return createCustomRule(rulesClient, args, mlAuthz);
});
},
async createPrebuiltRule(args: CreatePrebuiltRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.createPrebuiltRule', async () => {
return createPrebuiltRule(rulesClient, args, mlAuthz);
});
},
async updateRule(args: UpdateRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.updateRule', async () => {
return updateRule(rulesClient, args, mlAuthz);
});
},
async patchRule(args: PatchRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.patchRule', async () => {
return patchRule(rulesClient, args, mlAuthz);
});
},
async deleteRule(args: DeleteRuleArgs): Promise<void> {
return withSecuritySpan('DetectionRulesClient.deleteRule', async () => {
return deleteRule(rulesClient, args);
});
},
async upgradePrebuiltRule(args: UpgradePrebuiltRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.upgradePrebuiltRule', async () => {
return upgradePrebuiltRule(rulesClient, args, mlAuthz);
});
},
async importRule(args: ImportRuleArgs): Promise<RuleAlertType> {
return withSecuritySpan('DetectionRulesClient.importRule', async () => {
return importRule(rulesClient, args, mlAuthz);
});
},
});

View file

@ -13,10 +13,11 @@ import {
getCreateMachineLearningRulesSchemaMock,
getCreateRulesSchemaMock,
} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks';
import { updateRule } from './update_rule';
import { readRules } from './read_rules';
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');
@ -25,10 +26,13 @@ jest.mock('./read_rules');
describe('DetectionRulesClient.updateRule', () => {
let rulesClient: ReturnType<typeof rulesClientMock.create>;
let detectionRulesClient: IDetectionRulesClient;
const mlAuthz = (buildMlAuthz as jest.Mock)();
beforeEach(() => {
rulesClient = rulesClientMock.create();
detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz);
});
it('calls the rulesClient with expected params', async () => {
@ -37,7 +41,7 @@ describe('DetectionRulesClient.updateRule', () => {
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.update).toHaveBeenCalledWith(
expect.objectContaining({
@ -58,7 +62,7 @@ describe('DetectionRulesClient.updateRule', () => {
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
const rule = await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
const rule = await detectionRulesClient.updateRule({ ruleUpdate });
expect(rule.enabled).toBe(true);
});
@ -69,7 +73,7 @@ describe('DetectionRulesClient.updateRule', () => {
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams()));
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.update).toHaveBeenCalledWith(
expect.objectContaining({
@ -92,7 +96,7 @@ describe('DetectionRulesClient.updateRule', () => {
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams()));
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.update).toHaveBeenCalledWith(
expect.objectContaining({
@ -118,7 +122,7 @@ describe('DetectionRulesClient.updateRule', () => {
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.disable).toHaveBeenCalledWith(
expect.objectContaining({
@ -139,7 +143,7 @@ describe('DetectionRulesClient.updateRule', () => {
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.enable).toHaveBeenCalledWith(
expect.objectContaining({
@ -158,7 +162,7 @@ describe('DetectionRulesClient.updateRule', () => {
enabled: true,
};
await expect(updateRule(rulesClient, { ruleUpdate }, mlAuthz)).rejects.toThrow(
await expect(detectionRulesClient.updateRule({ ruleUpdate })).rejects.toThrow(
'mocked MLAuth error'
);
@ -184,7 +188,7 @@ describe('DetectionRulesClient.updateRule', () => {
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.update).toHaveBeenCalledWith(
expect.objectContaining({
@ -222,7 +226,7 @@ describe('DetectionRulesClient.updateRule', () => {
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
(readRules as jest.Mock).mockResolvedValueOnce(existingRule);
await updateRule(rulesClient, { ruleUpdate }, mlAuthz);
await detectionRulesClient.updateRule({ ruleUpdate });
expect(rulesClient.update).toHaveBeenCalledWith(
expect.objectContaining({

View file

@ -20,7 +20,7 @@ import { getEqlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks
import { buildMlAuthz } from '../../../../machine_learning/authz';
import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
jest.mock('../../../../machine_learning/authz');
jest.mock('../../../../machine_learning/validation');

View file

@ -0,0 +1,58 @@
/*
* 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 { RuleCreateProps } from '../../../../../../common/api/detection_engine/model/rule_schema';
import type { PrebuiltRuleAsset } from '../../../prebuilt_rules';
import type {
RuleUpdateProps,
RulePatchProps,
RuleObjectId,
RuleToImport,
} from '../../../../../../common/api/detection_engine';
import type { RuleAlertType } from '../../../rule_schema';
export interface IDetectionRulesClient {
createCustomRule: (createCustomRulePayload: CreateCustomRuleArgs) => Promise<RuleAlertType>;
createPrebuiltRule: (createPrebuiltRulePayload: CreatePrebuiltRuleArgs) => Promise<RuleAlertType>;
updateRule: (updateRulePayload: UpdateRuleArgs) => Promise<RuleAlertType>;
patchRule: (patchRulePayload: PatchRuleArgs) => Promise<RuleAlertType>;
deleteRule: (deleteRulePayload: DeleteRuleArgs) => Promise<void>;
upgradePrebuiltRule: (
upgradePrebuiltRulePayload: UpgradePrebuiltRuleArgs
) => Promise<RuleAlertType>;
importRule: (importRulePayload: ImportRuleArgs) => Promise<RuleAlertType>;
}
export interface CreateCustomRuleArgs {
params: RuleCreateProps;
}
export interface CreatePrebuiltRuleArgs {
ruleAsset: PrebuiltRuleAsset;
}
export interface UpdateRuleArgs {
ruleUpdate: RuleUpdateProps;
}
export interface PatchRuleArgs {
nextParams: RulePatchProps;
}
export interface DeleteRuleArgs {
ruleId: RuleObjectId;
}
export interface UpgradePrebuiltRuleArgs {
ruleAsset: PrebuiltRuleAsset;
}
export interface ImportRuleArgs {
ruleToImport: RuleToImport;
overwriteRules?: boolean;
allowMissingConnectorSecrets?: boolean;
}

View file

@ -0,0 +1,30 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { CreateCustomRuleArgs } from '../detection_rules_client_interface';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
import { convertCreateAPIToInternalSchema } from '../../../normalization/rule_converters';
import { validateMlAuth } from '../utils';
export const createCustomRule = async (
rulesClient: RulesClient,
args: CreateCustomRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { params } = args;
await validateMlAuth(mlAuthz, params.type);
const internalRule = convertCreateAPIToInternalSchema(params, { immutable: false });
const rule = await rulesClient.create<RuleParams>({
data: internalRule,
});
return rule;
};

View file

@ -0,0 +1,35 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { CreatePrebuiltRuleArgs } from '../detection_rules_client_interface';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
import { convertCreateAPIToInternalSchema } from '../../../normalization/rule_converters';
import { validateMlAuth } from '../utils';
export const createPrebuiltRule = async (
rulesClient: RulesClient,
args: CreatePrebuiltRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { ruleAsset } = args;
await validateMlAuth(mlAuthz, ruleAsset.type);
const internalRule = convertCreateAPIToInternalSchema(ruleAsset, {
immutable: true,
defaultEnabled: false,
});
const rule = await rulesClient.create<RuleParams>({
data: internalRule,
});
return rule;
};

View file

@ -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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { DeleteRuleArgs } from '../detection_rules_client_interface';
export const deleteRule = async (rulesClient: RulesClient, args: DeleteRuleArgs): Promise<void> => {
const { ruleId } = args;
await rulesClient.delete({ id: ruleId });
};

View file

@ -0,0 +1,63 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { ImportRuleArgs } from '../detection_rules_client_interface';
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
import { createBulkErrorObject } from '../../../../routes/utils';
import {
convertCreateAPIToInternalSchema,
convertUpdateAPIToInternalSchema,
} from '../../../normalization/rule_converters';
import { validateMlAuth } from '../utils';
import { readRules } from '../read_rules';
export const importRule = async (
rulesClient: RulesClient,
importRulePayload: ImportRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { ruleToImport, overwriteRules, allowMissingConnectorSecrets } = importRulePayload;
await validateMlAuth(mlAuthz, ruleToImport.type);
const existingRule = await readRules({
rulesClient,
ruleId: ruleToImport.rule_id,
id: undefined,
});
if (!existingRule) {
const internalRule = convertCreateAPIToInternalSchema(ruleToImport, {
immutable: false,
});
return rulesClient.create<RuleParams>({
data: internalRule,
allowMissingConnectorSecrets,
});
} else if (existingRule && overwriteRules) {
const newInternalRule = convertUpdateAPIToInternalSchema({
existingRule,
ruleUpdate: ruleToImport,
});
return rulesClient.update({
id: existingRule.id,
data: newInternalRule,
});
} else {
throw createBulkErrorObject({
ruleId: existingRule.params.ruleId,
statusCode: 409,
message: `rule_id: "${existingRule.params.ruleId}" already exists`,
});
}
};

View file

@ -0,0 +1,54 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { PatchRuleArgs } from '../detection_rules_client_interface';
import type { RuleAlertType } from '../../../../rule_schema';
import { getIdError } from '../../../utils/utils';
import { convertPatchAPIToInternalSchema } from '../../../normalization/rule_converters';
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from '../utils';
import { readRules } from '../read_rules';
export const patchRule = async (
rulesClient: RulesClient,
args: PatchRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { nextParams } = args;
const { rule_id: ruleId, id } = nextParams;
const existingRule = await readRules({
rulesClient,
ruleId,
id,
});
if (existingRule == null) {
const error = getIdError({ id, ruleId });
throw new ClientError(error.message, error.statusCode);
}
await validateMlAuth(mlAuthz, nextParams.type ?? existingRule.params.type);
const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule);
const update = await rulesClient.update({
id: existingRule.id,
data: patchedRule,
});
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, nextParams.enabled);
if (nextParams.enabled != null) {
return { ...update, enabled: nextParams.enabled };
} else {
return update;
}
};

View file

@ -0,0 +1,53 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { RuleAlertType } from '../../../../rule_schema';
import type { UpdateRuleArgs } from '../detection_rules_client_interface';
import { getIdError } from '../../../utils/utils';
import { convertUpdateAPIToInternalSchema } from '../../../normalization/rule_converters';
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from '../utils';
import { readRules } from '../read_rules';
export const updateRule = async (
rulesClient: RulesClient,
args: UpdateRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { ruleUpdate } = args;
const { rule_id: ruleId, id } = ruleUpdate;
await validateMlAuth(mlAuthz, ruleUpdate.type);
const existingRule = await readRules({
rulesClient,
ruleId,
id,
});
if (existingRule == null) {
const error = getIdError({ id, ruleId });
throw new ClientError(error.message, error.statusCode);
}
const newInternalRule = convertUpdateAPIToInternalSchema({
existingRule,
ruleUpdate,
});
const update = await rulesClient.update({
id: existingRule.id,
data: newInternalRule,
});
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleUpdate.enabled);
return { ...update, enabled: ruleUpdate.enabled ?? existingRule.enabled };
};

View file

@ -0,0 +1,84 @@
/*
* 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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
import type { UpgradePrebuiltRuleArgs } from '../detection_rules_client_interface';
import {
convertPatchAPIToInternalSchema,
convertCreateAPIToInternalSchema,
} from '../../../normalization/rule_converters';
import { transformAlertToRuleAction } from '../../../../../../../common/detection_engine/transform_actions';
import { validateMlAuth, ClientError } from '../utils';
import { readRules } from '../read_rules';
export const upgradePrebuiltRule = async (
rulesClient: RulesClient,
upgradePrebuiltRulePayload: UpgradePrebuiltRuleArgs,
mlAuthz: MlAuthz
): Promise<RuleAlertType> => {
const { ruleAsset } = upgradePrebuiltRulePayload;
await validateMlAuth(mlAuthz, ruleAsset.type);
const existingRule = await readRules({
rulesClient,
ruleId: ruleAsset.rule_id,
id: undefined,
});
if (!existingRule) {
throw new ClientError(`Failed to find rule ${ruleAsset.rule_id}`, 500);
}
if (ruleAsset.type !== existingRule.params.type) {
// If we're trying to change the type of a prepackaged rule, we need to delete the old one
// and replace it with the new rule, keeping the enabled setting, actions, throttle, id,
// and exception lists from the old rule
await rulesClient.delete({ id: existingRule.id });
const internalRule = convertCreateAPIToInternalSchema(
{
...ruleAsset,
enabled: existingRule.enabled,
exceptions_list: existingRule.params.exceptionsList,
actions: existingRule.actions.map(transformAlertToRuleAction),
timeline_id: existingRule.params.timelineId,
timeline_title: existingRule.params.timelineTitle,
},
{ immutable: true, defaultEnabled: existingRule.enabled }
);
return rulesClient.create<RuleParams>({
data: internalRule,
options: { id: existingRule.id },
});
}
// Else, simply patch it.
const patchedRule = convertPatchAPIToInternalSchema(ruleAsset, existingRule);
await rulesClient.update({
id: existingRule.id,
data: patchedRule,
});
const updatedRule = await readRules({
rulesClient,
ruleId: ruleAsset.rule_id,
id: undefined,
});
if (!updatedRule) {
throw new ClientError(`Rule ${ruleAsset.rule_id} not found after upgrade`, 500);
}
return updatedRule;
};

View file

@ -16,7 +16,7 @@ import type { RuleToImport } from '../../../../../../common/api/detection_engine
import type { ImportRuleResponse } from '../../../routes/utils';
import { createBulkErrorObject } from '../../../routes/utils';
import { checkRuleExceptionReferences } from './check_rule_exception_references';
import type { IDetectionRulesClient } from '../rule_management/detection_rules_client';
import type { IDetectionRulesClient } from '../detection_rules_client/detection_rules_client_interface';
export type PromiseFromStreams = RuleToImport | Error;
export interface RuleExceptionsPromiseFromStreams {
@ -94,9 +94,7 @@ export const importRules = async ({
exceptions_list: [...exceptions],
},
overwriteRules,
options: {
allowMissingConnectorSecrets,
},
allowMissingConnectorSecrets,
});
resolve({

View file

@ -1,36 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { RuleCreateProps } from '../../../../../../common/api/detection_engine';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import { convertCreateAPIToInternalSchema } from '../../normalization/rule_converters';
import { validateMlAuth } from './utils';
export interface CreateCustomRuleProps {
params: RuleCreateProps;
}
export const createCustomRule = async (
rulesClient: RulesClient,
createCustomRulePayload: CreateCustomRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.createCustomRule', async () => {
const { params } = createCustomRulePayload;
await validateMlAuth(mlAuthz, params.type);
const internalRule = convertCreateAPIToInternalSchema(params, { immutable: false });
const rule = await rulesClient.create<RuleParams>({
data: internalRule,
});
return rule;
});

View file

@ -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 type { RulesClient } from '@kbn/alerting-plugin/server';
import type { PrebuiltRuleAsset } from '../../../prebuilt_rules';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import { convertCreateAPIToInternalSchema } from '../../normalization/rule_converters';
import { validateMlAuth } from './utils';
export interface CreatePrebuiltRuleProps {
ruleAsset: PrebuiltRuleAsset;
}
export const createPrebuiltRule = async (
rulesClient: RulesClient,
createPrebuiltRulePayload: CreatePrebuiltRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.createPrebuiltRule', async () => {
const { ruleAsset } = createPrebuiltRulePayload;
await validateMlAuth(mlAuthz, ruleAsset.type);
const internalRule = convertCreateAPIToInternalSchema(ruleAsset, {
immutable: true,
defaultEnabled: false,
});
const rule = await rulesClient.create<RuleParams>({
data: internalRule,
});
return rule;
});

View file

@ -1,23 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { RuleObjectId } from '../../../../../../common/api/detection_engine';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
export interface DeleteRuleProps {
ruleId: RuleObjectId;
}
export const deleteRule = async (
rulesClient: RulesClient,
deleteRulePayload: DeleteRuleProps
): Promise<void> =>
withSecuritySpan('DetectionRulesClient.deleteRule', async () => {
const { ruleId } = deleteRulePayload;
await rulesClient.delete({ id: ruleId });
});

View file

@ -1,80 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType } from '../../../rule_schema';
import type { CreateCustomRuleProps } from './create_custom_rule';
import type { CreatePrebuiltRuleProps } from './create_prebuilt_rule';
import type { UpdateRuleProps } from './update_rule';
import type { PatchRuleProps } from './patch_rule';
import type { DeleteRuleProps } from './delete_rule';
import type { UpgradePrebuiltRuleProps } from './upgrade_prebuilt_rule';
import type { ImportRuleProps } from './import_rule';
import { createCustomRule } from './create_custom_rule';
import { createPrebuiltRule } from './create_prebuilt_rule';
import { updateRule } from './update_rule';
import { patchRule } from './patch_rule';
import { deleteRule } from './delete_rule';
import { upgradePrebuiltRule } from './upgrade_prebuilt_rule';
import { importRule } from './import_rule';
export interface IDetectionRulesClient {
createCustomRule: (createCustomRulePayload: CreateCustomRuleProps) => Promise<RuleAlertType>;
createPrebuiltRule: (
createPrebuiltRulePayload: CreatePrebuiltRuleProps
) => Promise<RuleAlertType>;
updateRule: (updateRulePayload: UpdateRuleProps) => Promise<RuleAlertType>;
patchRule: (patchRulePayload: PatchRuleProps) => Promise<RuleAlertType>;
deleteRule: (deleteRulePayload: DeleteRuleProps) => Promise<void>;
upgradePrebuiltRule: (
upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps
) => Promise<RuleAlertType>;
importRule: (importRulePayload: ImportRuleProps) => Promise<RuleAlertType>;
}
export const createDetectionRulesClient = (
rulesClient: RulesClient,
mlAuthz: MlAuthz
): IDetectionRulesClient => ({
createCustomRule: async (
createCustomRulePayload: CreateCustomRuleProps
): Promise<RuleAlertType> => {
return createCustomRule(rulesClient, createCustomRulePayload, mlAuthz);
},
createPrebuiltRule: async (
createPrebuiltRulePayload: CreatePrebuiltRuleProps
): Promise<RuleAlertType> => {
return createPrebuiltRule(rulesClient, createPrebuiltRulePayload, mlAuthz);
},
updateRule: async (updateRulePayload: UpdateRuleProps): Promise<RuleAlertType> => {
return updateRule(rulesClient, updateRulePayload, mlAuthz);
},
patchRule: async (patchRulePayload: PatchRuleProps): Promise<RuleAlertType> => {
return patchRule(rulesClient, patchRulePayload, mlAuthz);
},
deleteRule: async (deleteRulePayload: DeleteRuleProps): Promise<void> => {
return deleteRule(rulesClient, deleteRulePayload);
},
upgradePrebuiltRule: async (
upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps
): Promise<RuleAlertType> => {
return upgradePrebuiltRule(rulesClient, upgradePrebuiltRulePayload, mlAuthz);
},
importRule: async (importRulePayload: ImportRuleProps): Promise<RuleAlertType> => {
return importRule(rulesClient, importRulePayload, mlAuthz);
},
});

View file

@ -1,75 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import type { RuleToImport } from '../../../../../../common/api/detection_engine';
import { createBulkErrorObject } from '../../../routes/utils';
import {
convertCreateAPIToInternalSchema,
convertUpdateAPIToInternalSchema,
} from '../../normalization/rule_converters';
import { validateMlAuth } from './utils';
import { readRules } from './read_rules';
interface ImportRuleOptions {
allowMissingConnectorSecrets?: boolean;
}
export interface ImportRuleProps {
ruleToImport: RuleToImport;
overwriteRules?: boolean;
options: ImportRuleOptions;
}
export const importRule = async (
rulesClient: RulesClient,
importRulePayload: ImportRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.importRule', async () => {
const { ruleToImport, overwriteRules, options } = importRulePayload;
await validateMlAuth(mlAuthz, ruleToImport.type);
const existingRule = await readRules({
rulesClient,
ruleId: ruleToImport.rule_id,
id: undefined,
});
if (!existingRule) {
const internalRule = convertCreateAPIToInternalSchema(ruleToImport, {
immutable: false,
});
return rulesClient.create<RuleParams>({
data: internalRule,
allowMissingConnectorSecrets: options.allowMissingConnectorSecrets,
});
} else if (existingRule && overwriteRules) {
const newInternalRule = convertUpdateAPIToInternalSchema({
existingRule,
ruleUpdate: ruleToImport,
});
return rulesClient.update({
id: existingRule.id,
data: newInternalRule,
});
} else {
throw createBulkErrorObject({
ruleId: existingRule.params.ruleId,
statusCode: 409,
message: `rule_id: "${existingRule.params.ruleId}" already exists`,
});
}
});

View file

@ -1,60 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { PatchRuleRequestBody } from '../../../../../../common/api/detection_engine';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import { getIdError } from '../../utils/utils';
import { convertPatchAPIToInternalSchema } from '../../normalization/rule_converters';
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from './utils';
import { readRules } from './read_rules';
export interface PatchRuleProps {
nextParams: PatchRuleRequestBody;
}
export const patchRule = async (
rulesClient: RulesClient,
patchRulePayload: PatchRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.patchRule', async () => {
const { nextParams } = patchRulePayload;
const { rule_id: ruleId, id } = nextParams;
const existingRule = await readRules({
rulesClient,
ruleId,
id,
});
if (existingRule == null) {
const error = getIdError({ id, ruleId });
throw new ClientError(error.message, error.statusCode);
}
await validateMlAuth(mlAuthz, nextParams.type ?? existingRule.params.type);
const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule);
const update = await rulesClient.update({
id: existingRule.id,
data: patchedRule,
});
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, nextParams.enabled);
if (nextParams.enabled != null) {
return { ...update, enabled: nextParams.enabled };
} else {
return update;
}
});

View file

@ -1,59 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { RuleUpdateProps } from '../../../../../../common/api/detection_engine';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import { getIdError } from '../../utils/utils';
import { convertUpdateAPIToInternalSchema } from '../../normalization/rule_converters';
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from './utils';
import { readRules } from './read_rules';
export interface UpdateRuleProps {
ruleUpdate: RuleUpdateProps;
}
export const updateRule = async (
rulesClient: RulesClient,
updateRulePayload: UpdateRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.updateRule', async () => {
const { ruleUpdate } = updateRulePayload;
const { rule_id: ruleId, id } = ruleUpdate;
await validateMlAuth(mlAuthz, ruleUpdate.type);
const existingRule = await readRules({
rulesClient,
ruleId,
id,
});
if (existingRule == null) {
const error = getIdError({ id, ruleId });
throw new ClientError(error.message, error.statusCode);
}
const newInternalRule = convertUpdateAPIToInternalSchema({
existingRule,
ruleUpdate,
});
const update = await rulesClient.update({
id: existingRule.id,
data: newInternalRule,
});
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleUpdate.enabled);
return { ...update, enabled: ruleUpdate.enabled ?? existingRule.enabled };
});

View file

@ -1,90 +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 { RulesClient } from '@kbn/alerting-plugin/server';
import type { PrebuiltRuleAsset } from '../../../prebuilt_rules';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { RuleAlertType, RuleParams } from '../../../rule_schema';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import {
convertPatchAPIToInternalSchema,
convertCreateAPIToInternalSchema,
} from '../../normalization/rule_converters';
import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions';
import { validateMlAuth, ClientError } from './utils';
import { readRules } from './read_rules';
export interface UpgradePrebuiltRuleProps {
ruleAsset: PrebuiltRuleAsset;
}
export const upgradePrebuiltRule = async (
rulesClient: RulesClient,
upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps,
mlAuthz: MlAuthz
): Promise<RuleAlertType> =>
withSecuritySpan('DetectionRulesClient.upgradePrebuiltRule', async () => {
const { ruleAsset } = upgradePrebuiltRulePayload;
await validateMlAuth(mlAuthz, ruleAsset.type);
const existingRule = await readRules({
rulesClient,
ruleId: ruleAsset.rule_id,
id: undefined,
});
if (!existingRule) {
throw new ClientError(`Failed to find rule ${ruleAsset.rule_id}`, 500);
}
if (ruleAsset.type !== existingRule.params.type) {
// If we're trying to change the type of a prepackaged rule, we need to delete the old one
// and replace it with the new rule, keeping the enabled setting, actions, throttle, id,
// and exception lists from the old rule
await rulesClient.delete({ id: existingRule.id });
const internalRule = convertCreateAPIToInternalSchema(
{
...ruleAsset,
enabled: existingRule.enabled,
exceptions_list: existingRule.params.exceptionsList,
actions: existingRule.actions.map(transformAlertToRuleAction),
timeline_id: existingRule.params.timelineId,
timeline_title: existingRule.params.timelineTitle,
},
{ immutable: true, defaultEnabled: existingRule.enabled }
);
return rulesClient.create<RuleParams>({
data: internalRule,
options: { id: existingRule.id },
});
}
// Else, simply patch it.
const patchedRule = convertPatchAPIToInternalSchema(ruleAsset, existingRule);
await rulesClient.update({
id: existingRule.id,
data: patchedRule,
});
const updatedRule = await readRules({
rulesClient,
ruleId: ruleAsset.rule_id,
id: undefined,
});
if (!updatedRule) {
throw new ClientError(`Rule ${ruleAsset.rule_id} not found after upgrade`, 500);
}
return updatedRule;
});

View file

@ -11,7 +11,7 @@ import type {
RuleObjectId,
RuleResponse,
} from '../../../../../../../common/api/detection_engine/model/rule_schema';
import { readRules } from '../../../../rule_management/logic/rule_management/read_rules';
import { readRules } from '../../../../rule_management/logic/detection_rules_client/read_rules';
import { transform } from '../../../../rule_management/utils/utils';
// TODO: https://github.com/elastic/kibana/issues/125642 Move to rule_management into a DetectionRulesClient

View file

@ -28,7 +28,7 @@ import type { EndpointAppContextService } from './endpoint/endpoint_app_context_
import { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client';
import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client';
import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality';
import { createDetectionRulesClient } from './lib/detection_engine/rule_management/logic/rule_management/detection_rules_client';
import { createDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client';
import { buildMlAuthz } from './lib/machine_learning/authz';
export interface IRequestContextFactory {
@ -114,7 +114,7 @@ export class RequestContextFactory implements IRequestContextFactory {
getAuditLogger,
getDetectionRulesClient: () => {
getDetectionRulesClient: memoize(() => {
const mlAuthz = buildMlAuthz({
license: licensing.license,
ml: plugins.ml,
@ -126,7 +126,7 @@ export class RequestContextFactory implements IRequestContextFactory {
startPlugins.alerting.getRulesClientWithRequest(request),
mlAuthz
);
},
}),
getDetectionEngineHealthClient: memoize(() =>
ruleMonitoringService.createDetectionEngineHealthClient({

View file

@ -33,7 +33,7 @@ import type { EndpointInternalFleetServicesInterface } from './endpoint/services
import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client';
import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client';
import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality';
import type { IDetectionRulesClient } from './lib/detection_engine/rule_management/logic/rule_management/detection_rules_client';
import type { IDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface';
export { AppClient };
export interface SecuritySolutionApiRequestHandlerContext {