[RAC][Security Solution] Refactor persistence and security rule generic types (#114022)

* Refactor persistence and security rule generic types

* Remove unused import, fix unit tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Marshall Main 2021-10-12 14:43:50 -04:00 committed by GitHub
parent 6a55f87da0
commit 9e908f6caa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 255 additions and 297 deletions

View file

@ -32,7 +32,7 @@ export {
LifecycleAlertServices,
createLifecycleExecutor,
} from './utils/create_lifecycle_executor';
export { createPersistenceRuleTypeFactory } from './utils/create_persistence_rule_type_factory';
export { createPersistenceRuleTypeWrapper } from './utils/create_persistence_rule_type_wrapper';
export * from './utils/persistence_types';
export type { AlertsClient } from './alert_data_client/alerts_client';

View file

@ -7,9 +7,9 @@
import { ALERT_INSTANCE_ID, VERSION } from '@kbn/rule-data-utils';
import { getCommonAlertFields } from './get_common_alert_fields';
import { CreatePersistenceRuleTypeFactory } from './persistence_types';
import { CreatePersistenceRuleTypeWrapper } from './persistence_types';
export const createPersistenceRuleTypeFactory: CreatePersistenceRuleTypeFactory =
export const createPersistenceRuleTypeWrapper: CreatePersistenceRuleTypeWrapper =
({ logger, ruleDataClient }) =>
(type) => {
return {

View file

@ -8,43 +8,59 @@
import { ApiResponse } from '@elastic/elasticsearch';
import { BulkResponse } from '@elastic/elasticsearch/api/types';
import { Logger } from '@kbn/logging';
import { ESSearchRequest } from 'src/core/types/elasticsearch';
import {
AlertExecutorOptions,
AlertInstanceContext,
AlertInstanceState,
AlertType,
AlertTypeParams,
AlertTypeState,
} from '../../../alerting/server';
import { WithoutReservedActionGroups } from '../../../alerting/common';
import { IRuleDataClient } from '../rule_data_client';
import { AlertTypeWithExecutor } from '../types';
export type PersistenceAlertService<
TState extends AlertInstanceState = never,
TContext extends AlertInstanceContext = never,
TActionGroupIds extends string = never
> = (
export type PersistenceAlertService = (
alerts: Array<{
id: string;
fields: Record<string, unknown>;
}>,
refresh: boolean | 'wait_for'
) => Promise<ApiResponse<BulkResponse, unknown>>;
) => Promise<ApiResponse<BulkResponse, unknown> | undefined>;
export type PersistenceAlertQueryService = (
query: ESSearchRequest
) => Promise<Array<Record<string, unknown>>>;
export interface PersistenceServices<TAlertInstanceContext extends AlertInstanceContext = {}> {
alertWithPersistence: PersistenceAlertService<TAlertInstanceContext>;
export interface PersistenceServices {
alertWithPersistence: PersistenceAlertService;
}
export type CreatePersistenceRuleTypeFactory = (options: {
export type PersistenceAlertType<
TParams extends AlertTypeParams,
TState extends AlertTypeState,
TInstanceContext extends AlertInstanceContext = {},
TActionGroupIds extends string = never
> = Omit<
AlertType<TParams, TParams, TState, AlertInstanceState, TInstanceContext, TActionGroupIds>,
'executor'
> & {
executor: (
options: AlertExecutorOptions<
TParams,
TState,
AlertInstanceState,
TInstanceContext,
WithoutReservedActionGroups<TActionGroupIds, never>
> & {
services: PersistenceServices;
}
) => Promise<TState | void>;
};
export type CreatePersistenceRuleTypeWrapper = (options: {
ruleDataClient: IRuleDataClient;
logger: Logger;
}) => <
TState extends AlertTypeState,
TParams extends AlertTypeParams,
TServices extends PersistenceServices<TAlertInstanceContext>,
TAlertInstanceContext extends AlertInstanceContext = {}
TState extends AlertTypeState,
TInstanceContext extends AlertInstanceContext = {},
TActionGroupIds extends string = never
>(
type: AlertTypeWithExecutor<TState, TParams, TAlertInstanceContext, TServices>
) => AlertTypeWithExecutor<TState, TParams, TAlertInstanceContext, TServices>;
type: PersistenceAlertType<TParams, TState, TInstanceContext, TActionGroupIds>
) => AlertType<TParams, TParams, TState, AlertInstanceState, TInstanceContext, TActionGroupIds>;

View file

@ -14,7 +14,7 @@ import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils';
import { ListArray } from '@kbn/securitysolution-io-ts-list-types';
import { toError } from '@kbn/securitysolution-list-api';
import { createPersistenceRuleTypeFactory } from '../../../../../rule_registry/server';
import { createPersistenceRuleTypeWrapper } from '../../../../../rule_registry/server';
import { buildRuleMessageFactory } from './factories/build_rule_message_factory';
import {
checkPrivilegesFromEsClient,
@ -22,10 +22,14 @@ import {
getRuleRangeTuples,
hasReadIndexPrivileges,
hasTimestampFields,
isMachineLearningParams,
isEqlParams,
isQueryParams,
isSavedQueryParams,
isThreatParams,
isThresholdParams,
} from '../signals/utils';
import { DEFAULT_MAX_SIGNALS, DEFAULT_SEARCH_AFTER_PAGE_SIZE } from '../../../../common/constants';
import { CreateSecurityRuleTypeFactory } from './types';
import { CreateSecurityRuleTypeWrapper } from './types';
import { getListClient } from './utils/get_list_client';
import {
NotificationRuleTypeParams,
@ -37,13 +41,14 @@ import { bulkCreateFactory, wrapHitsFactory, wrapSequencesFactory } from './fact
import { RuleExecutionLogClient } from '../rule_execution_log/rule_execution_log_client';
import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas';
import { scheduleThrottledNotificationActions } from '../notifications/schedule_throttle_notification_actions';
import { AlertAttributes } from '../signals/types';
/* eslint-disable complexity */
export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory =
export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
({ lists, logger, config, ruleDataClient, eventLogService }) =>
(type) => {
const { alertIgnoreFields: ignoreFields, alertMergeStrategy: mergeStrategy } = config;
const persistenceRuleType = createPersistenceRuleTypeFactory({ ruleDataClient, logger });
const persistenceRuleType = createPersistenceRuleTypeWrapper({ ruleDataClient, logger });
return persistenceRuleType({
...type,
async executor(options) {
@ -69,7 +74,10 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory =
eventLogService,
underlyingClient: config.ruleExecutionLog.underlyingClient,
});
const ruleSO = await savedObjectsClient.get('alert', alertId);
const ruleSO = await savedObjectsClient.get<AlertAttributes<typeof params>>(
'alert',
alertId
);
const {
actions,
@ -107,7 +115,16 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory =
// move this collection of lines into a function in utils
// so that we can use it in create rules route, bulk, etc.
try {
if (!isMachineLearningParams(params)) {
// Typescript 4.1.3 can't figure out that `!isMachineLearningParams(params)` also excludes the only rule type
// of rule params that doesn't include `params.index`, but Typescript 4.3.5 does compute the stricter type correctly.
// When we update Typescript to >= 4.3.5, we can replace this logic with `!isMachineLearningParams(params)` again.
if (
isEqlParams(params) ||
isThresholdParams(params) ||
isQueryParams(params) ||
isSavedQueryParams(params) ||
isThreatParams(params)
) {
const index = params.index;
const hasTimestampOverride = !!timestampOverride;
@ -254,7 +271,7 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory =
createdSignals,
createdSignalsCount: createdSignals.length,
errors: result.errors.concat(runResult.errors),
lastLookbackDate: runResult.lastLookbackDate,
lastLookbackDate: runResult.lastLookBackDate,
searchAfterTimes: result.searchAfterTimes.concat(runResult.searchAfterTimes),
state: runState,
success: result.success && runResult.success,

View file

@ -12,6 +12,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe
import { createEqlAlertType } from './create_eql_alert_type';
import { createRuleTypeMocks } from '../__mocks__/rule_type';
import { getEqlRuleParams } from '../../schemas/rule_schemas.mock';
import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper';
import { createMockConfig } from '../../routes/__mocks__';
jest.mock('../../rule_execution_log/rule_execution_log_client');
@ -23,15 +24,20 @@ describe('Event correlation alerts', () => {
query: 'any where false',
};
const { services, dependencies, executor } = createRuleTypeMocks('eql', params);
const eqlAlertType = createEqlAlertType({
experimentalFeatures: allowedExperimentalValues,
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const eqlAlertType = securityRuleTypeWrapper(
createEqlAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(eqlAlertType);
services.scopedClusterClient.asCurrentUser.search.mockReturnValue(
elasticsearchClientMock.createSuccessTransportRequestPromise({

View file

@ -6,24 +6,16 @@
*/
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
import { PersistenceServices } from '../../../../../../rule_registry/server';
import { EQL_RULE_TYPE_ID } from '../../../../../common/constants';
import { eqlRuleParams, EqlRuleParams } from '../../schemas/rule_schemas';
import { EqlRuleParams, eqlRuleParams } from '../../schemas/rule_schemas';
import { eqlExecutor } from '../../signals/executors/eql';
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
import { CreateRuleOptions } from '../types';
import { CreateRuleOptions, SecurityAlertType } from '../types';
export const createEqlAlertType = (createOptions: CreateRuleOptions) => {
const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } =
createOptions;
const createSecurityRuleType = createSecurityRuleTypeFactory({
lists,
logger,
config,
ruleDataClient,
eventLogService,
});
return createSecurityRuleType<EqlRuleParams, {}, PersistenceServices, {}>({
export const createEqlAlertType = (
createOptions: CreateRuleOptions
): SecurityAlertType<EqlRuleParams, {}, {}, 'default'> => {
const { experimentalFeatures, logger, version } = createOptions;
return {
id: EQL_RULE_TYPE_ID,
name: 'Event Correlation Rule',
validate: {
@ -83,5 +75,5 @@ export const createEqlAlertType = (createOptions: CreateRuleOptions) => {
});
return { ...result, state };
},
});
};
};

View file

@ -16,7 +16,6 @@ import { BuildRuleMessage } from '../../signals/rule_messages';
import { errorAggregator, makeFloatString } from '../../signals/utils';
import { RefreshTypes } from '../../types';
import { PersistenceAlertService } from '../../../../../../rule_registry/server';
import { AlertInstanceContext } from '../../../../../../alerting/common';
export interface GenericBulkCreateResponse<T> {
success: boolean;
@ -27,9 +26,9 @@ export interface GenericBulkCreateResponse<T> {
}
export const bulkCreateFactory =
<TContext extends AlertInstanceContext>(
(
logger: Logger,
alertWithPersistence: PersistenceAlertService<TContext>,
alertWithPersistence: PersistenceAlertService,
buildRuleMessage: BuildRuleMessage,
refreshForBulkCreate: RefreshTypes
) =>
@ -61,6 +60,19 @@ export const bulkCreateFactory =
`individual bulk process time took: ${makeFloatString(end - start)} milliseconds`
)
);
if (response == null) {
return {
errors: [
'alertWithPersistence returned undefined response. Alerts as Data write flag may be disabled.',
],
success: false,
bulkCreateDuration: makeFloatString(end - start),
createdItemsCount: 0,
createdItems: [],
};
}
logger.debug(
buildRuleMessage(`took property says bulk took: ${response.body.took} milliseconds`)
);

View file

@ -16,6 +16,7 @@ import { createIndicatorMatchAlertType } from './create_indicator_match_alert_ty
import { sampleDocNoSortId } from '../../signals/__mocks__/es_results';
import { CountResponse } from 'kibana/server';
import { RuleParams } from '../../schemas/rule_schemas';
import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper';
import { createMockConfig } from '../../routes/__mocks__';
jest.mock('../utils/get_list_client', () => ({
@ -50,18 +51,23 @@ describe('Indicator Match Alerts', () => {
to: 'now',
type: 'threat_match',
};
const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params);
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
});
it('does not send an alert when no events found', async () => {
const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params);
const indicatorMatchAlertType = createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const indicatorMatchAlertType = securityRuleTypeWrapper(
createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(indicatorMatchAlertType);
@ -92,16 +98,13 @@ describe('Indicator Match Alerts', () => {
});
it('does not send an alert when no enrichments are found', async () => {
const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params);
const indicatorMatchAlertType = createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const indicatorMatchAlertType = securityRuleTypeWrapper(
createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(indicatorMatchAlertType);
@ -130,16 +133,13 @@ describe('Indicator Match Alerts', () => {
});
it('sends an alert when enrichments are found', async () => {
const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params);
const indicatorMatchAlertType = createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const indicatorMatchAlertType = securityRuleTypeWrapper(
createIndicatorMatchAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(indicatorMatchAlertType);

View file

@ -6,24 +6,16 @@
*/
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
import { PersistenceServices } from '../../../../../../rule_registry/server';
import { INDICATOR_RULE_TYPE_ID } from '../../../../../common/constants';
import { threatRuleParams, ThreatRuleParams } from '../../schemas/rule_schemas';
import { ThreatRuleParams, threatRuleParams } from '../../schemas/rule_schemas';
import { threatMatchExecutor } from '../../signals/executors/threat_match';
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
import { CreateRuleOptions } from '../types';
import { CreateRuleOptions, SecurityAlertType } from '../types';
export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions) => {
const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } =
createOptions;
const createSecurityRuleType = createSecurityRuleTypeFactory({
lists,
logger,
config,
ruleDataClient,
eventLogService,
});
return createSecurityRuleType<ThreatRuleParams, {}, PersistenceServices, {}>({
export const createIndicatorMatchAlertType = (
createOptions: CreateRuleOptions
): SecurityAlertType<ThreatRuleParams, {}, {}, 'default'> => {
const { experimentalFeatures, logger, version } = createOptions;
return {
id: INDICATOR_RULE_TYPE_ID,
name: 'Indicator Match Rule',
validate: {
@ -86,5 +78,5 @@ export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions)
});
return { ...result, state };
},
});
};
};

View file

@ -1,71 +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 { schema } from '@kbn/config-schema';
import { KibanaRequest, Logger } from 'src/core/server';
import { SavedObject } from 'src/core/types';
import { buildEsQuery, IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { createPersistenceRuleTypeFactory } from '../../../../../rule_registry/server';
import { ML_RULE_TYPE_ID } from '../../../../common/constants';
import { SecurityRuleRegistry } from '../../../plugin';
const createSecurityMlRuleType = createPersistenceRuleTypeFactory<SecurityRuleRegistry>();
import {
AlertInstanceContext,
AlertInstanceState,
AlertServices,
} from '../../../../../alerting/server';
import { ListClient } from '../../../../../lists/server';
import { isJobStarted } from '../../../../common/machine_learning/helpers';
import { ExceptionListItemSchema } from '../../../../common/shared_imports';
import { SetupPlugins } from '../../../plugin';
import { RefreshTypes } from '../types';
import { bulkCreateMlSignals } from '../signals/bulk_create_ml_signals';
import { filterEventsAgainstList } from '../signals/filters/filter_events_against_list';
import { findMlSignals } from '../signals/find_ml_signals';
import { BuildRuleMessage } from '../signals/rule_messages';
import { RuleStatusService } from '../signals/rule_status_service';
import { MachineLearningRuleAttributes } from '../signals/types';
import { createErrorsFromShard, createSearchAfterReturnType, mergeReturns } from '../signals/utils';
export const mlAlertType = createSecurityMlRuleType({
id: ML_RULE_TYPE_ID,
name: 'Machine Learning Rule',
validate: {
params: schema.object({
indexPatterns: schema.arrayOf(schema.string()),
customQuery: schema.string(),
}),
},
actionGroups: [
{
id: 'default',
name: 'Default',
},
],
defaultActionGroupId: 'default',
actionVariables: {
context: [{ name: 'server', description: 'the server' }],
},
minimumLicenseRequired: 'basic',
isExportable: false,
producer: 'security-solution',
async executor({
services: { alertWithPersistence, findAlerts },
params: { indexPatterns, customQuery },
}) {
return {
lastChecked: new Date(),
};
},
});
*/

View file

@ -14,6 +14,7 @@ import { createRuleTypeMocks } from '../__mocks__/rule_type';
import { createMlAlertType } from './create_ml_alert_type';
import { RuleParams } from '../../schemas/rule_schemas';
import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper';
import { createMockConfig } from '../../routes/__mocks__';
jest.mock('../../signals/bulk_create_ml_signals');
@ -94,16 +95,21 @@ describe('Machine Learning Alerts', () => {
},
]);
const { dependencies, executor } = createRuleTypeMocks('machine_learning', params);
const mlAlertType = createMlAlertType({
experimentalFeatures: allowedExperimentalValues,
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ml: mlMock,
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const mlAlertType = securityRuleTypeWrapper(
createMlAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
ml: mlMock,
version: '1.0.0',
})
);
dependencies.alerting.registerType(mlAlertType);

View file

@ -6,23 +6,16 @@
*/
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
import { PersistenceServices } from '../../../../../../rule_registry/server';
import { ML_RULE_TYPE_ID } from '../../../../../common/constants';
import { machineLearningRuleParams, MachineLearningRuleParams } from '../../schemas/rule_schemas';
import { MachineLearningRuleParams, machineLearningRuleParams } from '../../schemas/rule_schemas';
import { mlExecutor } from '../../signals/executors/ml';
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
import { CreateRuleOptions } from '../types';
import { CreateRuleOptions, SecurityAlertType } from '../types';
export const createMlAlertType = (createOptions: CreateRuleOptions) => {
const { lists, logger, config, ml, ruleDataClient, eventLogService } = createOptions;
const createSecurityRuleType = createSecurityRuleTypeFactory({
lists,
logger,
config,
ruleDataClient,
eventLogService,
});
return createSecurityRuleType<MachineLearningRuleParams, {}, PersistenceServices, {}>({
export const createMlAlertType = (
createOptions: CreateRuleOptions
): SecurityAlertType<MachineLearningRuleParams, {}, {}, 'default'> => {
const { logger, ml } = createOptions;
return {
id: ML_RULE_TYPE_ID,
name: 'Machine Learning Rule',
validate: {
@ -81,5 +74,5 @@ export const createMlAlertType = (createOptions: CreateRuleOptions) => {
});
return { ...result, state };
},
});
};
};

View file

@ -14,6 +14,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe
import { sampleDocNoSortId } from '../../signals/__mocks__/es_results';
import { createQueryAlertType } from './create_query_alert_type';
import { createRuleTypeMocks } from '../__mocks__/rule_type';
import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper';
import { createMockConfig } from '../../routes/__mocks__';
jest.mock('../utils/get_list_client', () => ({
@ -26,17 +27,22 @@ jest.mock('../utils/get_list_client', () => ({
jest.mock('../../rule_execution_log/rule_execution_log_client');
describe('Custom Query Alerts', () => {
const { services, dependencies, executor } = createRuleTypeMocks();
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
});
it('does not send an alert when no events found', async () => {
const { services, dependencies, executor } = createRuleTypeMocks();
const queryAlertType = createQueryAlertType({
experimentalFeatures: allowedExperimentalValues,
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const queryAlertType = securityRuleTypeWrapper(
createQueryAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(queryAlertType);
@ -74,16 +80,13 @@ describe('Custom Query Alerts', () => {
});
it('sends a properly formatted alert when events are found', async () => {
const { services, dependencies, executor } = createRuleTypeMocks();
const queryAlertType = createQueryAlertType({
experimentalFeatures: allowedExperimentalValues,
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
const queryAlertType = securityRuleTypeWrapper(
createQueryAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(queryAlertType);

View file

@ -6,24 +6,16 @@
*/
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
import { PersistenceServices } from '../../../../../../rule_registry/server';
import { QUERY_RULE_TYPE_ID } from '../../../../../common/constants';
import { queryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas';
import { QueryRuleParams, queryRuleParams } from '../../schemas/rule_schemas';
import { queryExecutor } from '../../signals/executors/query';
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
import { CreateRuleOptions } from '../types';
import { CreateRuleOptions, SecurityAlertType } from '../types';
export const createQueryAlertType = (createOptions: CreateRuleOptions) => {
const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } =
createOptions;
const createSecurityRuleType = createSecurityRuleTypeFactory({
lists,
logger,
config,
ruleDataClient,
eventLogService,
});
return createSecurityRuleType<QueryRuleParams, {}, PersistenceServices, {}>({
export const createQueryAlertType = (
createOptions: CreateRuleOptions
): SecurityAlertType<QueryRuleParams, {}, {}, 'default'> => {
const { experimentalFeatures, logger, version } = createOptions;
return {
id: QUERY_RULE_TYPE_ID,
name: 'Custom Query Rule',
validate: {
@ -86,5 +78,5 @@ export const createQueryAlertType = (createOptions: CreateRuleOptions) => {
});
return { ...result, state };
},
});
};
};

View file

@ -9,6 +9,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe
import { createThresholdAlertType } from './create_threshold_alert_type';
import { createRuleTypeMocks } from '../__mocks__/rule_type';
import { getThresholdRuleParams } from '../../schemas/rule_schemas.mock';
import { createSecurityRuleTypeWrapper } from '../create_security_rule_type_wrapper';
import { createMockConfig } from '../../routes/__mocks__';
jest.mock('../../rule_execution_log/rule_execution_log_client');
@ -17,16 +18,21 @@ describe('Threshold Alerts', () => {
it('does not send an alert when no events found', async () => {
const params = getThresholdRuleParams();
const { dependencies, executor } = createRuleTypeMocks('threshold', params);
const thresholdAlertTpe = createThresholdAlertType({
experimentalFeatures: allowedExperimentalValues,
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: dependencies.lists,
logger: dependencies.logger,
config: createMockConfig(),
ruleDataClient: dependencies.ruleDataClient,
eventLogService: dependencies.eventLogService,
version: '1.0.0',
});
dependencies.alerting.registerType(thresholdAlertTpe);
const thresholdAlertType = securityRuleTypeWrapper(
createThresholdAlertType({
experimentalFeatures: allowedExperimentalValues,
logger: dependencies.logger,
version: '1.0.0',
})
);
dependencies.alerting.registerType(thresholdAlertType);
await executor({ params });
expect(dependencies.ruleDataClient.getWriter).not.toBeCalled();

View file

@ -6,26 +6,17 @@
*/
import { validateNonExact } from '@kbn/securitysolution-io-ts-utils';
import { PersistenceServices } from '../../../../../../rule_registry/server';
import { THRESHOLD_RULE_TYPE_ID } from '../../../../../common/constants';
import { thresholdRuleParams, ThresholdRuleParams } from '../../schemas/rule_schemas';
import { thresholdExecutor } from '../../signals/executors/threshold';
import { ThresholdAlertState } from '../../signals/types';
import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory';
import { CreateRuleOptions } from '../types';
import { CreateRuleOptions, SecurityAlertType } from '../types';
export const createThresholdAlertType = (createOptions: CreateRuleOptions) => {
const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } =
createOptions;
const createSecurityRuleType = createSecurityRuleTypeFactory({
lists,
logger,
config,
ruleDataClient,
eventLogService,
});
return createSecurityRuleType<ThresholdRuleParams, {}, PersistenceServices, ThresholdAlertState>({
export const createThresholdAlertType = (
createOptions: CreateRuleOptions
): SecurityAlertType<ThresholdRuleParams, ThresholdAlertState, {}, 'default'> => {
const { experimentalFeatures, logger, version } = createOptions;
return {
id: THRESHOLD_RULE_TYPE_ID,
name: 'Threshold Rule',
validate: {
@ -63,8 +54,6 @@ export const createThresholdAlertType = (createOptions: CreateRuleOptions) => {
state,
} = execOptions;
// console.log(JSON.stringify(state));
const result = await thresholdExecutor({
buildRuleMessage,
bulkCreate,
@ -82,5 +71,5 @@ export const createThresholdAlertType = (createOptions: CreateRuleOptions) => {
return result;
},
});
};
};

View file

@ -11,28 +11,30 @@ import { SearchHit } from '@elastic/elasticsearch/api/types';
import { Logger } from '@kbn/logging';
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import { AlertExecutorOptions, AlertType } from '../../../../../alerting/server';
import { SavedObject } from '../../../../../../../src/core/server';
import {
AlertInstanceContext,
AlertInstanceState,
AlertTypeParams,
AlertTypeState,
WithoutReservedActionGroups,
} from '../../../../../alerting/common';
import { AlertType } from '../../../../../alerting/server';
import { ListClient } from '../../../../../lists/server';
import { TechnicalRuleFieldMap } from '../../../../../rule_registry/common/assets/field_maps/technical_rule_field_map';
import { TypeOfFieldMap } from '../../../../../rule_registry/common/field_map';
import {
AlertTypeWithExecutor,
PersistenceServices,
IRuleDataClient,
} from '../../../../../rule_registry/server';
import { PersistenceServices, IRuleDataClient } from '../../../../../rule_registry/server';
import { BaseHit } from '../../../../common/detection_engine/types';
import { ConfigType } from '../../../config';
import { SetupPlugins } from '../../../plugin';
import { RuleParams } from '../schemas/rule_schemas';
import { BuildRuleMessage } from '../signals/rule_messages';
import { AlertAttributes, BulkCreate, WrapHits, WrapSequences } from '../signals/types';
import {
AlertAttributes,
BulkCreate,
SearchAfterAndBulkCreateReturnType,
WrapHits,
WrapSequences,
} from '../signals/types';
import { AlertsFieldMap, RulesFieldMap } from './field_maps';
import { ExperimentalFeatures } from '../../../../common/experimental_features';
import { IEventLogService } from '../../../../../event_log/server';
@ -50,12 +52,6 @@ export interface SecurityAlertTypeReturnValue<TState extends AlertTypeState> {
warningMessages: string[];
}
type SimpleAlertType<
TState extends AlertTypeState,
TParams extends AlertTypeParams = {},
TAlertInstanceContext extends AlertInstanceContext = {}
> = AlertType<TParams, TParams, TState, AlertInstanceState, TAlertInstanceContext, string, string>;
export interface RunOpts<TParams extends RuleParams> {
buildRuleMessage: BuildRuleMessage;
bulkCreate: BulkCreate;
@ -72,44 +68,42 @@ export interface RunOpts<TParams extends RuleParams> {
wrapSequences: WrapSequences;
}
export type SecurityAlertTypeExecutor<
TState extends AlertTypeState,
TServices extends PersistenceServices<TAlertInstanceContext>,
export type SecurityAlertType<
TParams extends RuleParams,
TAlertInstanceContext extends AlertInstanceContext = {}
> = (
options: Parameters<SimpleAlertType<TState, TParams, TAlertInstanceContext>['executor']>[0] & {
runOpts: RunOpts<TParams>;
} & { services: TServices }
) => Promise<SecurityAlertTypeReturnValue<TState>>;
type SecurityAlertTypeWithExecutor<
TState extends AlertTypeState,
TServices extends PersistenceServices<TAlertInstanceContext>,
TParams extends RuleParams,
TAlertInstanceContext extends AlertInstanceContext = {}
TInstanceContext extends AlertInstanceContext = {},
TActionGroupIds extends string = never
> = Omit<
AlertType<TParams, TParams, TState, AlertInstanceState, TAlertInstanceContext, string, string>,
AlertType<TParams, TParams, TState, AlertInstanceState, TInstanceContext, TActionGroupIds>,
'executor'
> & {
executor: SecurityAlertTypeExecutor<TState, TServices, TParams, TAlertInstanceContext>;
executor: (
options: AlertExecutorOptions<
TParams,
TState,
AlertInstanceState,
TInstanceContext,
WithoutReservedActionGroups<TActionGroupIds, never>
> & {
services: PersistenceServices;
runOpts: RunOpts<TParams>;
}
) => Promise<SearchAfterAndBulkCreateReturnType & { state: TState }>;
};
export type CreateSecurityRuleTypeFactory = (options: {
export type CreateSecurityRuleTypeWrapper = (options: {
lists: SetupPlugins['lists'];
logger: Logger;
config: ConfigType;
ruleDataClient: IRuleDataClient;
eventLogService: IEventLogService;
}) => <
TParams extends RuleParams & { index?: string[] | undefined },
TAlertInstanceContext extends AlertInstanceContext,
TServices extends PersistenceServices<TAlertInstanceContext>,
TState extends AlertTypeState
TParams extends RuleParams,
TState extends AlertTypeState,
TInstanceContext extends AlertInstanceContext = {}
>(
type: SecurityAlertTypeWithExecutor<TState, TServices, TParams, TAlertInstanceContext>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => AlertTypeWithExecutor<TState, TParams, TAlertInstanceContext, any>;
type: SecurityAlertType<TParams, TState, TInstanceContext, 'default'>
) => AlertType<TParams, TParams, TState, AlertInstanceState, TInstanceContext, 'default'>;
export type RACAlertSignal = TypeOfFieldMap<AlertsFieldMap> & TypeOfFieldMap<RulesFieldMap>;
export type RACAlert = Exclude<
@ -124,11 +118,7 @@ export type WrappedRACAlert = BaseHit<RACAlert>;
export interface CreateRuleOptions {
experimentalFeatures: ExperimentalFeatures;
lists: SetupPlugins['lists'];
logger: Logger;
config: ConfigType;
ml?: SetupPlugins['ml'];
ruleDataClient: IRuleDataClient;
version: string;
eventLogService: IEventLogService;
}

View file

@ -109,6 +109,7 @@ import { ctiFieldMap } from './lib/detection_engine/rule_types/field_maps/cti';
import { legacyRulesNotificationAlertType } from './lib/detection_engine/notifications/legacy_rules_notification_alert_type';
// eslint-disable-next-line no-restricted-imports
import { legacyIsNotificationAlertExecutor } from './lib/detection_engine/notifications/legacy_types';
import { createSecurityRuleTypeWrapper } from './lib/detection_engine/rule_types/create_security_rule_type_wrapper';
import { IEventLogClientService, IEventLogService } from '../../event_log/server';
import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider';
@ -268,20 +269,34 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
// Register rule types via rule-registry
const createRuleOptions: CreateRuleOptions = {
experimentalFeatures,
lists: plugins.lists,
logger: this.logger,
config: this.config,
ml: plugins.ml,
ruleDataClient,
eventLogService,
version: this.context.env.packageInfo.version,
};
this.setupPlugins.alerting.registerType(createEqlAlertType(createRuleOptions));
this.setupPlugins.alerting.registerType(createIndicatorMatchAlertType(createRuleOptions));
this.setupPlugins.alerting.registerType(createMlAlertType(createRuleOptions));
this.setupPlugins.alerting.registerType(createQueryAlertType(createRuleOptions));
this.setupPlugins.alerting.registerType(createThresholdAlertType(createRuleOptions));
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({
lists: plugins.lists,
logger: this.logger,
config: this.config,
ruleDataClient,
eventLogService,
});
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createEqlAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createIndicatorMatchAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createMlAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createQueryAlertType(createRuleOptions))
);
this.setupPlugins.alerting.registerType(
securityRuleTypeWrapper(createThresholdAlertType(createRuleOptions))
);
}
// TODO We need to get the endpoint routes inside of initRoutes