From 75fd94c14a69e429f905bad8e28827aed2882846 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 6 Dec 2021 18:49:42 -0500 Subject: [PATCH] adds ml rule preview and fixes refresh error --- .../server/rule_data_client/rule_data_client.ts | 4 ++++ .../rule_registry/server/rule_data_client/types.ts | 1 + .../components/rules/rule_preview/helpers.ts | 5 +++++ .../components/rules/rule_preview/index.tsx | 6 ++++++ .../rules/rule_preview/preview_histogram.tsx | 1 + .../rules/rule_preview/use_preview_histogram.tsx | 12 ++++++++++-- .../rules/rule_preview/use_preview_route.tsx | 10 ++++++++++ .../components/rules/step_define_rule/index.tsx | 14 ++++++++++---- .../pages/detection_engine/rules/create/helpers.ts | 6 ++++++ .../routes/rules/preview_rules_route.ts | 7 ++++++- 10 files changed, 59 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts index d7ec6ea41ac8fa..e0ff117e74a409 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_client/rule_data_client.ts @@ -54,6 +54,10 @@ export class RuleDataClient implements IRuleDataClient { return this.options.indexInfo.kibanaVersion; } + public indexNameWithNamespace(namespace: string): string { + return this.options.indexInfo.getPrimaryAlias(namespace); + } + private get writeEnabled(): boolean { return this._isWriteEnabled; } diff --git a/x-pack/plugins/rule_registry/server/rule_data_client/types.ts b/x-pack/plugins/rule_registry/server/rule_data_client/types.ts index 5ddbd0035526d3..5fab32eb388680 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_client/types.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_client/types.ts @@ -14,6 +14,7 @@ import { TechnicalRuleDataFieldName } from '../../common/technical_rule_data_fie export interface IRuleDataClient { indexName: string; + indexNameWithNamespace(namespace: string): string; kibanaVersion: string; isWriteEnabled(): boolean; getReader(options?: { namespace?: string }): IRuleDataReader; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts index 70fac3feb0a1c1..86e75a26dc11f5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts @@ -208,6 +208,7 @@ export const getIsRulePreviewDisabled = ({ index, threatIndex, threatMapping, + machineLearningJobId, }: { ruleType: Type; isQueryBarValid: boolean; @@ -215,6 +216,7 @@ export const getIsRulePreviewDisabled = ({ index: string[]; threatIndex: string[]; threatMapping: ThreatMapping; + machineLearningJobId: string[]; }) => { if (!isQueryBarValid || index.length === 0) return true; if (ruleType === 'threat_match') { @@ -227,5 +229,8 @@ export const getIsRulePreviewDisabled = ({ ) return true; } + if (ruleType === 'machine_learning') { + return machineLearningJobId.length === 0; + } return false; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx index 0677184a05ef21..a5ed9b9e5bed42 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/index.tsx @@ -36,6 +36,8 @@ export interface RulePreviewProps { threatMapping: ThreatMapping; threatQuery: FieldValueQueryBar; threshold: FieldValueThreshold; + machineLearningJobId: string[]; + anomalyThreshold: number; } const Select = styled(EuiSelect)` @@ -57,6 +59,8 @@ const RulePreviewComponent: React.FC = ({ threatQuery, threatMapping, threshold, + machineLearningJobId, + anomalyThreshold, }) => { const { spaces } = useKibana().services; const [spaceId, setSpaceId] = useState(''); @@ -84,6 +88,8 @@ const RulePreviewComponent: React.FC = ({ ruleType, threatMapping, threshold, + machineLearningJobId, + anomalyThreshold, }); // Resets the timeFrame to default when rule type is changed because not all time frames are supported by all rule types diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx index 1e44ddad383070..5452501ec65c7a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx @@ -70,6 +70,7 @@ export const PreviewHistogram = ({ threshold: isThresholdRule ? threshold : undefined, query, index, + ruleType, }); const previousPreviewId = usePrevious(previewId); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx index 0a36cd842b1576..74b4abd265c158 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_histogram.tsx @@ -5,6 +5,7 @@ * 2.0. */ import { useMemo } from 'react'; +import { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { useMatrixHistogram } from '../../../../common/containers/matrix_histogram'; import { MatrixHistogramType } from '../../../../../common/search_strategy'; import { convertToBuildEsQuery } from '../../../../common/lib/keury'; @@ -23,6 +24,7 @@ interface PreviewHistogramParams { threshold?: FieldValueThreshold; query: FieldValueQueryBar; index: string[]; + ruleType: Type; } export const usePreviewHistogram = ({ @@ -33,6 +35,7 @@ export const usePreviewHistogram = ({ threshold, query, index, + ruleType, }: PreviewHistogramParams) => { const { uiSettings } = useKibana().services; const { @@ -53,6 +56,11 @@ export const usePreviewHistogram = ({ filters, }); + const stackByField = useMemo(() => { + const stackByDefault = ruleType === 'machine_learning' ? 'host.name' : 'event.category'; + return threshold?.field[0] ?? stackByDefault; + }, [threshold, ruleType]); + const matrixHistogramRequest = useMemo(() => { return { endDate, @@ -60,13 +68,13 @@ export const usePreviewHistogram = ({ filterQuery, histogramType: MatrixHistogramType.preview, indexNames: [`${DEFAULT_PREVIEW_INDEX}-${spaceId}`], - stackByField: threshold?.field[0] ?? 'event.category', + stackByField, startDate, threshold, includeMissingData: false, skip: error != null, }; - }, [startDate, endDate, filterQuery, spaceId, error, threshold]); + }, [startDate, endDate, filterQuery, spaceId, error, threshold, stackByField]); return useMatrixHistogram(matrixHistogramRequest); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx index 1bac73a3e9c4db..3bf8f284f02478 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/use_preview_route.tsx @@ -24,6 +24,8 @@ interface PreviewRouteParams { timeFrame: Unit; threatMapping: ThreatMapping; threshold: FieldValueThreshold; + machineLearningJobId: string[]; + anomalyThreshold: number; } export const usePreviewRoute = ({ @@ -36,6 +38,8 @@ export const usePreviewRoute = ({ ruleType, threatMapping, threshold, + machineLearningJobId, + anomalyThreshold, }: PreviewRouteParams) => { const [isRequestTriggered, setIsRequestTriggered] = useState(false); @@ -70,6 +74,8 @@ export const usePreviewRoute = ({ ruleType, threatMapping, threshold, + machineLearningJobId, + anomalyThreshold, ]); useEffect(() => { @@ -84,6 +90,8 @@ export const usePreviewRoute = ({ threatQuery, timeFrame, threshold, + machineLearningJobId, + anomalyThreshold, }) ); } @@ -99,6 +107,8 @@ export const usePreviewRoute = ({ threatQuery, timeFrame, threshold, + machineLearningJobId, + anomalyThreshold, ]); return { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index b1a91dd702fbd3..69d4d400f12a4d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -162,6 +162,8 @@ const StepDefineRuleComponent: FC = ({ threatQueryBar: formThreatQuery, threshold: formThreshold, threatMapping: formThreatMapping, + machineLearningJobId: formMachineLearningJobId, + anomalyThreshold: formAnomalyThreshold, }, ] = useFormData({ form, @@ -176,6 +178,8 @@ const StepDefineRuleComponent: FC = ({ 'threshold.cardinality.value', 'threatIndex', 'threatMapping', + 'machineLearningJobId', + 'anomalyThreshold', ], }); @@ -183,11 +187,10 @@ const StepDefineRuleComponent: FC = ({ const [isThreatQueryBarValid, setIsThreatQueryBarValid] = useState(false); const index = formIndex || initialState.index; const threatIndex = formThreatIndex || initialState.threatIndex; + const machineLearningJobId = formMachineLearningJobId ?? initialState.machineLearningJobId; + const anomalyThreshold = formAnomalyThreshold ?? initialState.anomalyThreshold; const ruleType = formRuleType || initialState.ruleType; - const isPreviewRouteEnabled = useMemo( - () => ruleType !== 'machine_learning' && ruleType !== 'threat_match', - [ruleType] - ); + const isPreviewRouteEnabled = useMemo(() => ruleType !== 'threat_match', [ruleType]); const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(index); const aggregatableFields = Object.entries(browserFields).reduce( (groupAcc, [groupName, groupValue]) => { @@ -513,6 +516,7 @@ const StepDefineRuleComponent: FC = ({ index, threatIndex, threatMapping: formThreatMapping, + machineLearningJobId, })} query={formQuery} ruleType={ruleType} @@ -520,6 +524,8 @@ const StepDefineRuleComponent: FC = ({ threatQuery={formThreatQuery} threatMapping={formThreatMapping} threshold={formThreshold} + machineLearningJobId={machineLearningJobId} + anomalyThreshold={anomalyThreshold} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts index 53ec4985dc690f..e18886166f4702 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts @@ -403,6 +403,8 @@ export const formatPreviewRule = ({ threatMapping, timeFrame, threshold, + machineLearningJobId, + anomalyThreshold, }: { index: string[]; threatIndex: string[]; @@ -412,6 +414,8 @@ export const formatPreviewRule = ({ threatMapping: ThreatMapping; timeFrame: Unit; threshold: FieldValueThreshold; + machineLearningJobId: string[]; + anomalyThreshold: number; }): CreateRulesSchema => { const defineStepData = { ...stepDefineDefaultValue, @@ -422,6 +426,8 @@ export const formatPreviewRule = ({ threatQueryBar: threatQuery, threatMapping, threshold, + machineLearningJobId, + anomalyThreshold, }; const aboutStepData = { ...stepAboutDefaultValue, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 65ccbc315c93f9..785f4932d094ef 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -89,7 +89,7 @@ export const previewRulesRoute = async ( return response.ok({ body: { errors: ['Invalid invocation count'] } }); } - if (request.body.type === 'threat_match' || request.body.type === 'machine_learning') { + if (request.body.type === 'threat_match') { return response.ok({ body: { errors: ['Preview for rule type not supported'] } }); } @@ -261,6 +261,11 @@ export const previewRulesRoute = async ( ) .map((item) => item.message); + // Refreshes alias to ensure index is able to be read before returning + await context.core.elasticsearch.client.asInternalUser.indices.refresh({ + index: previewRuleDataClient.indexNameWithNamespace(spaceId), + }); + return response.ok({ body: { previewId,