mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[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:
parent
87f6c1d4b0
commit
41c34e09c3
48 changed files with 519 additions and 512 deletions
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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>;
|
||||
|
|
@ -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');
|
|
@ -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');
|
|
@ -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');
|
||||
|
|
@ -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: {
|
|
@ -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');
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
});
|
|
@ -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({
|
|
@ -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');
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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 });
|
||||
};
|
|
@ -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`,
|
||||
});
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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 };
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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({
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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 });
|
||||
});
|
|
@ -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);
|
||||
},
|
||||
});
|
|
@ -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`,
|
||||
});
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
});
|
|
@ -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 };
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue