[8.x] [Security Solution][Detection Engine] Simplify rule preview route (#216384) (#216732)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution][Detection Engine] Simplify rule preview route
(#216384)](https://github.com/elastic/kibana/pull/216384)

<!--- Backport version: 9.6.6 -->

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

<!--BACKPORT [{"author":{"name":"Marshall
Main","email":"55718608+marshallmain@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-04-01T21:31:58Z","message":"[Security
Solution][Detection Engine] Simplify rule preview route (#216384)\n\n##
Summary\n\nRefactors `runExecutors` to take a `SecurityAlertType`
instead of an\nalerting framework `RuleType` and moves the logic to
convert the\n`SecurityAlertType` into a `RuleType` into `runExecutors`.
This makes\nthe signature of `runExecutors` much simpler and reduces a
lot of the\nduplication in the `switch`
below.","sha":"74722fd11b646a883c47c75b4d2ef88ebf5f457d","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Detection
Engine","backport:version","v9.1.0","v8.19.0"],"title":"[Security
Solution][Detection Engine] Simplify rule preview
route","number":216384,"url":"https://github.com/elastic/kibana/pull/216384","mergeCommit":{"message":"[Security
Solution][Detection Engine] Simplify rule preview route (#216384)\n\n##
Summary\n\nRefactors `runExecutors` to take a `SecurityAlertType`
instead of an\nalerting framework `RuleType` and moves the logic to
convert the\n`SecurityAlertType` into a `RuleType` into `runExecutors`.
This makes\nthe signature of `runExecutors` much simpler and reduces a
lot of the\nduplication in the `switch`
below.","sha":"74722fd11b646a883c47c75b4d2ef88ebf5f457d"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/216384","number":216384,"mergeCommit":{"message":"[Security
Solution][Detection Engine] Simplify rule preview route (#216384)\n\n##
Summary\n\nRefactors `runExecutors` to take a `SecurityAlertType`
instead of an\nalerting framework `RuleType` and moves the logic to
convert the\n`SecurityAlertType` into a `RuleType` into `runExecutors`.
This makes\nthe signature of `runExecutors` much simpler and reduces a
lot of the\nduplication in the `switch`
below.","sha":"74722fd11b646a883c47c75b4d2ef88ebf5f457d"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Marshall Main <55718608+marshallmain@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-04-02 01:22:09 +02:00 committed by GitHub
parent 4f8e104a6e
commit 6cc646b1d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,8 +17,6 @@ import type {
RuleTypeState,
} from '@kbn/alerting-plugin/common';
import { parseDuration, DISABLE_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common';
import type { ExecutorType } from '@kbn/alerting-plugin/server/types';
import type { Alert } from '@kbn/alerting-plugin/server';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import {
DEFAULT_PREVIEW_INDEX,
@ -51,7 +49,10 @@ import type { RuleExecutionContext, StatusChangeArgs } from '../../../rule_monit
import type { ConfigType } from '../../../../../config';
import { alertInstanceFactoryStub } from './alert_instance_factory_stub';
import type { CreateSecurityRuleTypeWrapperProps } from '../../../rule_types/types';
import type {
CreateSecurityRuleTypeWrapperProps,
SecurityAlertType,
} from '../../../rule_types/types';
import {
createEqlAlertType,
createEsqlAlertType,
@ -197,45 +198,11 @@ export const previewRulesRoute = (
isPreview: true,
});
const runExecutors = async <
TParams extends RuleParams,
TState extends RuleTypeState,
TInstanceState extends AlertInstanceState,
TInstanceContext extends AlertInstanceContext,
TActionGroupIds extends string = ''
>(
executor: ExecutorType<
TParams,
TState,
TInstanceState,
TInstanceContext,
TActionGroupIds
>,
ruleTypeId: string,
ruleTypeName: string,
params: TParams,
shouldWriteAlerts: () => boolean,
alertFactory: {
create: (
id: string
) => Pick<
Alert<TInstanceState, TInstanceContext, TActionGroupIds>,
| 'getState'
| 'replaceState'
| 'scheduleActions'
| 'setContext'
| 'getContext'
| 'hasContext'
| 'getUuid'
| 'getStart'
>;
alertLimit: {
getValue: () => number;
setLimitReached: () => void;
};
done: () => { getRecoveredAlerts: () => [] };
}
const runExecutors = async <TParams extends RuleParams, TState extends RuleTypeState>(
securityRuleType: SecurityAlertType<TParams, TState>,
params: TParams
) => {
const ruleType = previewRuleTypeWrapper(securityRuleType);
let statePreview = runState as TState;
let loggedRequests = [];
@ -260,8 +227,8 @@ export const previewRulesRoute = (
consumer: SERVER_APP_ID,
enabled: true,
revision: 0,
ruleTypeId,
ruleTypeName,
ruleTypeId: ruleType.id,
ruleTypeName: ruleType.name,
updatedAt: new Date(),
updatedBy: username ?? 'preview-updated-by',
muteAll: false,
@ -282,16 +249,29 @@ export const previewRulesRoute = (
while (invocationCount > 0 && !isAborted) {
invocationStartTime = moment();
({ state: statePreview, loggedRequests } = (await executor({
({ state: statePreview, loggedRequests } = (await ruleType.executor({
executionId: uuidv4(),
params,
previousStartedAt,
rule,
services: {
shouldWriteAlerts,
shouldWriteAlerts: () => true,
shouldStopExecution: () => false,
alertsClient: null,
alertFactory,
alertFactory: {
create: alertInstanceFactoryStub<
TParams,
TState,
AlertInstanceState,
AlertInstanceContext,
'default'
>,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
},
savedObjectsClient: coreContext.savedObjects.client,
scopedClusterClient: wrapScopedClusterClient({
abortController,
@ -350,161 +330,45 @@ export const previewRulesRoute = (
switch (previewRuleParams.type) {
case 'query':
const queryAlertType = previewRuleTypeWrapper(
createQueryAlertType({
id: QUERY_RULE_TYPE_ID,
name: 'Custom Query Rule',
})
);
await runExecutors(
queryAlertType.executor,
queryAlertType.id,
queryAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const queryAlertType = createQueryAlertType({
id: QUERY_RULE_TYPE_ID,
name: 'Custom Query Rule',
});
await runExecutors(queryAlertType, previewRuleParams);
break;
case 'saved_query':
const savedQueryAlertType = previewRuleTypeWrapper(
createQueryAlertType({
id: SAVED_QUERY_RULE_TYPE_ID,
name: 'Saved Query Rule',
})
);
await runExecutors(
savedQueryAlertType.executor,
savedQueryAlertType.id,
savedQueryAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const savedQueryAlertType = createQueryAlertType({
id: SAVED_QUERY_RULE_TYPE_ID,
name: 'Saved Query Rule',
});
await runExecutors(savedQueryAlertType, previewRuleParams);
break;
case 'threshold':
const thresholdAlertType = previewRuleTypeWrapper(createThresholdAlertType());
await runExecutors(
thresholdAlertType.executor,
thresholdAlertType.id,
thresholdAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const thresholdAlertType = createThresholdAlertType();
await runExecutors(thresholdAlertType, previewRuleParams);
break;
case 'threat_match':
const threatMatchAlertType = previewRuleTypeWrapper(createIndicatorMatchAlertType());
await runExecutors(
threatMatchAlertType.executor,
threatMatchAlertType.id,
threatMatchAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const threatMatchAlertType = createIndicatorMatchAlertType();
await runExecutors(threatMatchAlertType, previewRuleParams);
break;
case 'eql':
const eqlAlertType = previewRuleTypeWrapper(createEqlAlertType());
await runExecutors(
eqlAlertType.executor,
eqlAlertType.id,
eqlAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const eqlAlertType = createEqlAlertType();
await runExecutors(eqlAlertType, previewRuleParams);
break;
case 'esql':
if (config.experimentalFeatures.esqlRulesDisabled) {
throw Error('ES|QL rule type is not supported');
}
const esqlAlertType = previewRuleTypeWrapper(createEsqlAlertType());
await runExecutors(
esqlAlertType.executor,
esqlAlertType.id,
esqlAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const esqlAlertType = createEsqlAlertType();
await runExecutors(esqlAlertType, previewRuleParams);
break;
case 'machine_learning':
const mlAlertType = previewRuleTypeWrapper(createMlAlertType(ml));
await runExecutors(
mlAlertType.executor,
mlAlertType.id,
mlAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const mlAlertType = createMlAlertType(ml);
await runExecutors(mlAlertType, previewRuleParams);
break;
case 'new_terms':
const newTermsAlertType = previewRuleTypeWrapper(createNewTermsAlertType());
await runExecutors(
newTermsAlertType.executor,
newTermsAlertType.id,
newTermsAlertType.name,
previewRuleParams,
() => true,
{
create: alertInstanceFactoryStub,
alertLimit: {
getValue: () => 1000,
setLimitReached: () => {},
},
done: () => ({ getRecoveredAlerts: () => [] }),
}
);
const newTermsAlertType = createNewTermsAlertType();
await runExecutors(newTermsAlertType, previewRuleParams);
break;
default:
assertUnreachable(previewRuleParams);