From b68afd142f7ec81b1c74180a1030015c8346bdc4 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 20 Dec 2022 15:15:31 +0000 Subject: [PATCH] replace alerts histograms with Lens --- .../components/alerts_treemap_panel/index.tsx | 75 ++++++++-- .../components/matrix_histogram/index.tsx | 13 +- .../{alerts.ts => alerts/alerts_histogram.ts} | 34 ++--- .../common/alerts/alerts_table.ts | 140 ++++++++++++++++++ .../common/alerts/alerts_treemap.ts | 137 +++++++++++++++++ .../lens_attributes/common/alerts/utils.ts | 76 ++++++++++ .../lens_attributes/common/events.ts | 6 +- .../visualization_actions/lens_embeddable.tsx | 4 +- .../components/visualization_actions/types.ts | 16 +- .../use_lens_attributes.tsx | 9 +- .../pages/rule_details/index.tsx | 3 + .../alerts_kpis/alerts_count_panel/index.tsx | 74 +++++++-- .../alerts_histogram_panel/index.test.tsx | 2 + .../alerts_histogram_panel/index.tsx | 35 +++-- .../detection_engine/chart_panels/index.tsx | 19 ++- .../detection_engine/detection_engine.tsx | 3 + .../signals_by_category.tsx | 2 + 17 files changed, 565 insertions(+), 83 deletions(-) rename x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/{alerts.ts => alerts/alerts_histogram.ts} (84%) create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_table.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_treemap.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/utils.ts diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx index c2b4e9d1e7fa42..459e54904779c9 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx @@ -26,6 +26,11 @@ import { DEFAULT_STACK_BY_FIELD0_SIZE, getAlertsRiskQuery } from '../alerts_tree import type { AlertsTreeMapAggregation } from '../alerts_treemap/types'; import { InputsModelId } from '../../store/inputs/constants'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; +import { useRefetchByRestartingSession } from '../page/use_refetch_by_session'; +import { LensEmbeddable } from '../visualization_actions/lens_embeddable'; +import { getAlertsTreemapLensAttributes as getLensAttributes } from '../visualization_actions/lens_attributes/common/alerts/alerts_treemap'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import type { Status } from '../../../../common/detection_engine/schemas/common'; const DEFAULT_HEIGHT = DEFAULT_MIN_CHART_HEIGHT + 134; // px @@ -56,6 +61,9 @@ export interface Props { stackByField1ComboboxRef?: React.RefObject>; stackByWidth?: number; title: React.ReactNode; + showBuildingBlockAlerts: boolean; + status: Status; + showOnlyThreatIndicatorAlerts: boolean; } const AlertsTreemapPanelComponent: React.FC = ({ @@ -81,6 +89,9 @@ const AlertsTreemapPanelComponent: React.FC = ({ stackByField1ComboboxRef, stackByWidth, title, + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, }: Props) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(false); @@ -146,6 +157,22 @@ const AlertsTreemapPanelComponent: React.FC = ({ to, ]); + const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled'); + const timerange = useMemo(() => ({ from, to }), [from, to]); + const { searchSessionId, refetchByRestartingSession } = useRefetchByRestartingSession({ + inputId: InputsModelId.global, + queryId: uniqueQueryId, + }); + const alertsOptions = useMemo( + () => ({ + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, + breakdownField: stackByField1, + }), + [showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, status, stackByField1] + ); + useInspectButton({ deleteQuery, loading: isLoadingAlerts, @@ -181,7 +208,9 @@ const AlertsTreemapPanelComponent: React.FC = ({ > {isPanelExpanded && ( = ({ )} - {isLoadingAlerts && isPanelExpanded ? ( - - ) : ( - <> - {alertsData != null && isPanelExpanded && ( - - )} - - )} + {isPanelExpanded ? ( + isChartEmbeddablesEnabled && getLensAttributes && timerange ? ( + + ) : isLoadingAlerts ? ( + + ) : ( + <> + {alertsData != null && isPanelExpanded && ( + + )} + + ) + ) : null} ); diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 10bb49e2df3fd0..c5e1f7f2144c41 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -33,8 +33,6 @@ import { InputsModelId } from '../../store/inputs/constants'; import { HoverVisibilityContainer } from '../hover_visibility_container'; import { HISTOGRAM_ACTIONS_BUTTON_CLASS, VisualizationActions } from '../visualization_actions'; import type { GetLensAttributes, LensAttributes } from '../visualization_actions/types'; -import { SecurityPageName } from '../../../../common/constants'; -import { useRouteSpy } from '../../utils/route/use_route_spy'; import { useQueryToggle } from '../../containers/query_toggle'; import { LensEmbeddable } from '../visualization_actions/lens_embeddable'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; @@ -74,7 +72,6 @@ const HistogramPanel = styled(Panel)<{ height?: number }>` const ChartHeight = '150px'; -// eslint-disable-next-line complexity export const MatrixHistogramComponent: React.FC = ({ chartHeight, defaultStackByOption, @@ -191,12 +188,6 @@ export const MatrixHistogramComponent: React.FC = }; const [loading, { data, inspect, totalCount, refetch }] = useMatrixHistogramCombined(matrixHistogramRequest); - const [{ pageName }] = useRouteSpy(); - - const onHostOrNetworkOrUserPage = - pageName === SecurityPageName.hosts || - pageName === SecurityPageName.network || - pageName === SecurityPageName.users; const titleWithStackByField = useMemo( () => (title != null && typeof title === 'function' ? title(selectedStackByOption) : title), @@ -282,11 +273,11 @@ export const MatrixHistogramComponent: React.FC = toggleQuery={toggleQuery} subtitle={subtitleWithCounts} inspectMultiple - showInspectButton={showInspectButton || !onHostOrNetworkOrUserPage} + showInspectButton={showInspectButton && !isChartEmbeddablesEnabled} isInspectDisabled={filterQuery === undefined} > - {onHostOrNetworkOrUserPage && (getLensAttributes || lensAttributes) && timerange && ( + {(getLensAttributes || lensAttributes) && timerange && ( ({ title: 'Alerts', @@ -53,26 +58,7 @@ export const getAlertsHistogramLensAttributes: GetLensAttributes = ( query: '', language: 'kuery', }, - filters: [ - { - meta: { - index: '.alerts-security.alerts-id', - alias: null, - negate: true, - disabled: false, - type: 'exists', - key: 'kibana.alert.building_block_type', - }, - query: { - exists: { - field: 'kibana.alert.building_block_type', - }, - }, - $state: { - store: 'appState', - }, - }, - ], + filters: buildAlertsOptionsFilters(alertsOptions), datasourceStates: { formBased: { layers: { @@ -102,10 +88,10 @@ export const getAlertsHistogramLensAttributes: GetLensAttributes = ( dataType: 'string', operationType: 'terms', scale: 'ordinal', - sourceField: `${stackByField}`, + sourceField: stackByField, isBucketed: true, params: { - size: 10, + size: 1000, orderBy: { type: 'column', columnId: 'e09e0380-0740-4105-becc-0a4ca12e3944', diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_table.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_table.ts new file mode 100644 index 00000000000000..c3cde0b93860ff --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_table.ts @@ -0,0 +1,140 @@ +/* + * 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 { GetLensAttributes } from '../../../types'; +import { buildAlertsOptionsFilters } from './utils'; + +export const getAlertsTableLensAttributes: GetLensAttributes = ( + stackByField = 'kibana.alert.rule.name', + alertsOptions = { + showOnlyThreatIndicatorAlerts: false, + showBuildingBlockAlerts: false, + } +) => ({ + title: 'Alerts', + description: '', + visualizationType: 'lnsDatatable', + state: { + visualization: { + columns: [ + { + columnId: '2881fedd-54b7-42ba-8c97-5175dec86166', + isTransposed: false, + width: 362, + }, + { + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + isTransposed: false, + }, + { + columnId: '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + isTransposed: false, + }, + ], + layerId: '4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b', + layerType: 'data', + paging: { + size: 10, + enabled: true, + }, + }, + query: { + query: '', + language: 'kuery', + }, + filters: buildAlertsOptionsFilters(alertsOptions), + datasourceStates: { + formBased: { + layers: { + '4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b': { + columns: { + '2881fedd-54b7-42ba-8c97-5175dec86166': { + label: `Top values of ${stackByField}`, + dataType: 'string', + operationType: 'terms', + scale: 'ordinal', + sourceField: stackByField, + isBucketed: true, + params: { + size: 1000, + orderBy: { + type: 'column', + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + parentFormat: { + id: 'terms', + }, + include: [], + exclude: [], + includeIsRegex: false, + excludeIsRegex: false, + }, + }, + 'f04a71a3-399f-4d32-9efc-8a005e989991': { + label: `Count of ${alertsOptions.breakdownField}`, + dataType: 'number', + operationType: 'count', + isBucketed: false, + scale: 'ratio', + sourceField: alertsOptions.breakdownField, + params: { + emptyAsNull: true, + }, + }, + '75ce269b-ee9c-4c7d-a14e-9226ba0fe059': { + label: `Top values of ${alertsOptions.breakdownField}`, + dataType: 'string', + operationType: 'terms', + scale: 'ordinal', + sourceField: alertsOptions.breakdownField, + isBucketed: true, + params: { + size: 1000, + orderBy: { + type: 'column', + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + parentFormat: { + id: 'terms', + }, + include: [], + exclude: [], + includeIsRegex: false, + excludeIsRegex: false, + }, + }, + }, + columnOrder: [ + '2881fedd-54b7-42ba-8c97-5175dec86166', + '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + 'f04a71a3-399f-4d32-9efc-8a005e989991', + ], + sampling: 1, + incompleteColumns: {}, + }, + }, + }, + textBased: { + layers: {}, + }, + }, + internalReferences: [], + adHocDataViews: {}, + }, + references: [ + { + type: 'index-pattern', + id: '{dataViewId}', + name: 'indexpattern-datasource-layer-4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b', + }, + ], +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_treemap.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_treemap.ts new file mode 100644 index 00000000000000..12c45b97df0b4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/alerts_treemap.ts @@ -0,0 +1,137 @@ +/* + * 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 { GetLensAttributes } from '../../../types'; +import { buildAlertsOptionsFilters } from './utils'; + +export const getAlertsTreemapLensAttributes: GetLensAttributes = ( + stackByField = 'kibana.alert.rule.name', + alertsOptions = { + showOnlyThreatIndicatorAlerts: false, + showBuildingBlockAlerts: false, + } +) => ({ + title: 'Alerts', + description: '', + visualizationType: 'lnsPie', + state: { + visualization: { + shape: 'treemap', + layers: [ + { + layerId: '4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b', + primaryGroups: [ + '2881fedd-54b7-42ba-8c97-5175dec86166', + '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + ], + metrics: ['f04a71a3-399f-4d32-9efc-8a005e989991'], + numberDisplay: 'value', + categoryDisplay: 'default', + legendDisplay: 'show', + nestedLegend: true, + layerType: 'data', + legendSize: 'xlarge', + legendPosition: 'left', + }, + ], + }, + query: { + query: '', + language: 'kuery', + }, + filters: buildAlertsOptionsFilters(alertsOptions), + datasourceStates: { + formBased: { + layers: { + '4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b': { + columns: { + '2881fedd-54b7-42ba-8c97-5175dec86166': { + label: `Top values of ${stackByField}`, + dataType: 'string', + operationType: 'terms', + scale: 'ordinal', + sourceField: stackByField, + isBucketed: true, + params: { + size: 1000, + orderBy: { + type: 'column', + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + parentFormat: { + id: 'terms', + }, + include: [], + exclude: [], + includeIsRegex: false, + excludeIsRegex: false, + }, + }, + '75ce269b-ee9c-4c7d-a14e-9226ba0fe059': { + label: `Top values of ${alertsOptions.breakdownField}`, + dataType: 'string', + operationType: 'terms', + scale: 'ordinal', + sourceField: alertsOptions.breakdownField, + isBucketed: true, + params: { + size: 1000, + orderBy: { + type: 'column', + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + parentFormat: { + id: 'terms', + }, + include: [], + exclude: [], + includeIsRegex: false, + excludeIsRegex: false, + }, + }, + 'f04a71a3-399f-4d32-9efc-8a005e989991': { + label: 'Maximum of kibana.alert.risk_score', + dataType: 'number', + operationType: 'max', + sourceField: 'kibana.alert.risk_score', + isBucketed: false, + scale: 'ratio', + params: { + emptyAsNull: true, + }, + }, + }, + columnOrder: [ + '2881fedd-54b7-42ba-8c97-5175dec86166', + '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + 'f04a71a3-399f-4d32-9efc-8a005e989991', + ], + sampling: 1, + incompleteColumns: {}, + }, + }, + }, + textBased: { + layers: {}, + }, + }, + internalReferences: [], + adHocDataViews: {}, + }, + references: [ + { + type: 'index-pattern', + id: '{dataViewId}', + name: 'indexpattern-datasource-layer-4aa7cf71-cf20-4e62-8ca6-ca6be6b0988b', + }, + ], +}); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/utils.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/utils.ts new file mode 100644 index 00000000000000..86caecf3138e7a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/alerts/utils.ts @@ -0,0 +1,76 @@ +/* + * 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 { AlertsOptions } from '../../../types'; + +export const buildAlertsOptionsFilters = ({ + showBuildingBlockAlerts = false, + showOnlyThreatIndicatorAlerts = false, + status, +}: AlertsOptions) => [ + ...(status + ? [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'kibana.alert.workflow_status', + params: { + query: status, + }, + }, + query: { + match_phrase: { + 'kibana.alert.workflow_status': status, + }, + }, + }, + ] + : []), + ...(showOnlyThreatIndicatorAlerts + ? [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'kibana.alert.rule.type', + params: { + query: 'threat_match', + }, + }, + query: { + match_phrase: { + 'kibana.alert.rule.type': 'threat_match', + }, + }, + }, + ] + : []), + ...(!showBuildingBlockAlerts + ? [ + { + meta: { + index: '.alerts-security.alerts-id', + alias: null, + negate: true, + disabled: false, + type: 'exists', + key: 'kibana.alert.building_block_type', + }, + query: { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + }, + ] + : []), +]; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/events.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/events.ts index caf534f44f78fd..732f157ae3a19f 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/events.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/events.ts @@ -79,11 +79,11 @@ export const getEventsHistogramLensAttributes: GetLensAttributes = ( sourceField: '___records___', }, '34919782-4546-43a5-b668-06ac934d3acd': { - label: `Top values of ${stackByField}`, // could be event.dataset or event.module + label: `Top values of ${stackByField}`, dataType: 'string', operationType: 'terms', scale: 'ordinal', - sourceField: `${stackByField}`, // could be event.dataset or event.module + sourceField: `${stackByField}`, isBucketed: true, params: { size: 10, @@ -91,7 +91,7 @@ export const getEventsHistogramLensAttributes: GetLensAttributes = ( type: 'column', columnId: 'e09e0380-0740-4105-becc-0a4ca12e3944', }, - orderDirection: 'asc', + orderDirection: 'desc', otherBucket: true, missingBucket: false, parentFormat: { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx index b834a06f4b2382..eb2e8a2513468d 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx @@ -57,6 +57,7 @@ const LensEmbeddableComponent: React.FC = ({ stackByField, timerange, inspectTitle, + alertsOptions, }) => { const { lens } = useKibana().services; const dispatch = useDispatch(); @@ -70,6 +71,7 @@ const LensEmbeddableComponent: React.FC = ({ stackByField, title: '', scopeId, + alertsOptions, }); const LensComponent = lens.EmbeddableComponent; @@ -130,8 +132,6 @@ const LensEmbeddableComponent: React.FC = ({ }); }, []); - console.log(id, searchSessionId); - return ( <> {attributes && searchSessionId ? ( diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts index f1f002449429d2..f22af1f0d3bc8c 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts @@ -6,10 +6,15 @@ */ import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import type { Status } from '../../../../common/detection_engine/schemas/common'; import type { InputsModelId } from '../../store/inputs/constants'; +import type { SourcererScopeName } from '../../store/sourcerer/model'; export type LensAttributes = TypedLensByValueInput['attributes']; -export type GetLensAttributes = (stackByField?: string) => LensAttributes; +export type GetLensAttributes = ( + stackByField?: string, + alertsOptions?: AlertsOptions +) => LensAttributes; export interface VisualizationActionsProps { className?: string; @@ -33,8 +38,10 @@ export interface LensEmbeddableComponentProps { inputsModelId?: InputsModelId.global | InputsModelId.timeline; inspectTitle?: string; lensAttributes?: LensAttributes; + scopeId?: SourcererScopeName; stackByField?: string; timerange: { from: string; to: string }; + alertsOptions?: AlertsOptions; } export enum RequestStatus { @@ -74,3 +81,10 @@ export interface Response { json?: { rawResponse?: object }; time?: number; } + +export interface AlertsOptions { + showBuildingBlockAlerts?: boolean; + showOnlyThreatIndicatorAlerts?: boolean; + status?: Status; + breakdownField?: string; +} diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx index 3d6cdecdb2d8a9..708a6e43b1d305 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx @@ -14,7 +14,7 @@ import { useDeepEqualSelector } from '../../hooks/use_selector'; import { inputsSelectors } from '../../store'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useRouteSpy } from '../../utils/route/use_route_spy'; -import type { LensAttributes, GetLensAttributes } from './types'; +import type { LensAttributes, GetLensAttributes, AlertsOptions } from './types'; import { getHostDetailsPageFilter, sourceOrDestinationIpExistsFilter, @@ -28,12 +28,14 @@ export const useLensAttributes = ({ stackByField, title, scopeId = SourcererScopeName.default, + alertsOptions, }: { lensAttributes?: LensAttributes | null; getLensAttributes?: GetLensAttributes; stackByField?: string; title?: string; scopeId?: SourcererScopeName; + alertsOptions?: AlertsOptions; }): LensAttributes | null => { const { selectedPatterns, dataViewId } = useSourcererDataView(scopeId); const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []); @@ -72,7 +74,9 @@ export const useLensAttributes = ({ } const attrs: LensAttributes = lensAttributes ?? - ((getLensAttributes && stackByField && getLensAttributes(stackByField)) as LensAttributes); + ((getLensAttributes && + stackByField && + getLensAttributes(stackByField, alertsOptions)) as LensAttributes); return { ...attrs, @@ -97,6 +101,7 @@ export const useLensAttributes = ({ lensAttributes, getLensAttributes, stackByField, + alertsOptions, title, query, filters, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx index 2fcf2d63110d25..ed600ed2b1962d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx @@ -840,6 +840,9 @@ const RuleDetailsPageComponent: React.FC = ({ defaultStackByOption={defaultRuleStackByOption} updateDateRange={updateDateRangeCallback} runtimeMappings={runtimeMappings} + showBuildingBlockAlerts={showBuildingBlockAlerts} + status={filterGroup} + showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index 8fceaece8ed2bf..bf2a79499331eb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -27,6 +27,13 @@ import { KpiPanel } from '../common/components'; import { useInspectButton } from '../common/hooks'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { FieldSelection } from '../../../../common/components/field_selection'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { InputsModelId } from '../../../../common/store/inputs/constants'; +import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable'; +import { getAlertsTableLensAttributes as getLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/alerts/alerts_table'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import type { Status } from '../../../../../common/detection_engine/schemas/common'; +import { useRefetchByRestartingSession } from '../../../../common/components/page/use_refetch_by_session'; export const DETECTIONS_ALERTS_COUNT_ID = 'detections-alerts-count'; @@ -49,7 +56,11 @@ interface AlertsCountPanelProps { stackByWidth?: number; title?: React.ReactNode; runtimeMappings?: MappingRuntimeFields; + showBuildingBlockAlerts: boolean; + status: Status; + showOnlyThreatIndicatorAlerts: boolean; } +const ChartHeight = '180px'; export const AlertsCountPanel = memo( ({ @@ -71,6 +82,9 @@ export const AlertsCountPanel = memo( stackByField1ComboboxRef, stackByWidth, title = i18n.COUNT_TABLE_TITLE, + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, }) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(false); @@ -100,14 +114,29 @@ export const AlertsCountPanel = memo( setQuerySkip(!toggleStatus); }, [toggleStatus]); const toggleQuery = useCallback( - (status: boolean) => { - setToggleStatus(status); + (newToggleStatus: boolean) => { + setToggleStatus(newToggleStatus); // toggle on = skipQuery false - setQuerySkip(!status); + setQuerySkip(!newToggleStatus); }, [setQuerySkip, setToggleStatus] ); + const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled'); + const timerange = useMemo(() => ({ from, to }), [from, to]); + const { searchSessionId, refetchByRestartingSession } = useRefetchByRestartingSession({ + inputId: InputsModelId.global, + queryId: uniqueQueryId, + }); + const alertsOptions = useMemo( + () => ({ + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, + breakdownField: stackByField1, + }), + [showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, stackByField1, status] + ); const { loading: isLoadingAlerts, data: alertsData, @@ -125,7 +154,7 @@ export const AlertsCountPanel = memo( runtimeMappings, }), indexName: signalIndexName, - skip: querySkip, + skip: querySkip || isChartEmbeddablesEnabled, queryName: ALERTS_QUERY_NAMES.COUNT, }); @@ -154,10 +183,11 @@ export const AlertsCountPanel = memo( setQuery, response, request, - refetch, + refetch: isChartEmbeddablesEnabled ? refetchByRestartingSession : refetch, uniqueQueryId, deleteQuery, loading: isLoadingAlerts, + searchSessionId, }); return ( @@ -181,7 +211,9 @@ export const AlertsCountPanel = memo( toggleQuery={toggleQuery} > ( uniqueQueryId={uniqueQueryId} /> - {toggleStatus && alertsData != null && ( - - )} + {toggleStatus ? ( + isChartEmbeddablesEnabled && getLensAttributes && timerange ? ( + + ) : alertsData != null ? ( + + ) : null + ) : null} ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx index c47353aa04b06f..58205252d01e1b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx @@ -101,6 +101,8 @@ describe('AlertsHistogramPanel', () => { signalIndexName: 'signalIndexName', setQuery: jest.fn(), updateDateRange: jest.fn(), + showBuildingBlockAlerts: false, + showOnlyThreatIndicatorAlerts: false, }; const mockSetToggle = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index 4f4a4979d0a377..80aab2cf7d621b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -52,10 +52,12 @@ import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { GROUP_BY_TOP_LABEL } from '../common/translations'; import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -import { getAlertsHistogramLensAttributes as getLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/alerts'; +import { getAlertsHistogramLensAttributes as getLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/alerts/alerts_histogram'; import { InputsModelId } from '../../../../common/store/inputs/constants'; import { useRefetchByRestartingSession } from '../../../../common/components/page/use_refetch_by_session'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { useCheckSignalIndex } from '../../../containers/detection_engine/alerts/use_check_signal_index'; +import type { Status } from '../../../../../common/detection_engine/schemas/common'; const defaultTotalAlertsObj: AlertsTotal = { value: 0, @@ -74,7 +76,7 @@ const OptionsFlexItem = styled(EuiFlexItem)` export const LEGEND_WITH_COUNTS_WIDTH = 300; // px -const ChartHeight = '150px'; +const ChartHeight = '170px'; interface AlertsHistogramPanelProps { alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; @@ -108,6 +110,9 @@ interface AlertsHistogramPanelProps { title?: React.ReactNode; updateDateRange: UpdateDateRange; runtimeMappings?: MappingRuntimeFields; + showBuildingBlockAlerts: boolean; + status?: Status; + showOnlyThreatIndicatorAlerts: boolean; } const NO_LEGEND_DATA: LegendItem[] = []; @@ -144,6 +149,9 @@ export const AlertsHistogramPanel = memo( updateDateRange, titleSize = 'm', runtimeMappings, + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, }) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(false); @@ -176,10 +184,10 @@ export const AlertsHistogramPanel = memo( setQuerySkip(!toggleStatus); }, [toggleStatus]); const toggleQuery = useCallback( - (status: boolean) => { - setToggleStatus(status); + (newToggleStatus: boolean) => { + setToggleStatus(newToggleStatus); // toggle on = skipQuery false - setQuerySkip(!status); + setQuerySkip(!newToggleStatus); }, [setQuerySkip, setToggleStatus] ); @@ -190,6 +198,14 @@ export const AlertsHistogramPanel = memo( inputId: InputsModelId.global, queryId: uniqueQueryId, }); + const alertsOptions = useMemo( + () => ({ + showBuildingBlockAlerts, + showOnlyThreatIndicatorAlerts, + status, + }), + [showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, status] + ); const { loading: isLoadingAlerts, @@ -367,7 +383,7 @@ export const AlertsHistogramPanel = memo( titleSize={titleSize} toggleStatus={toggleStatus} toggleQuery={toggleQuery} - showInspectButton={chartOptionsContextMenu == null} + showInspectButton={isChartEmbeddablesEnabled ? false : chartOptionsContextMenu == null} subtitle={!isInitialLoading && showTotalAlertsCount && totalAlerts} isInspectDisabled={isInspectDisabled} hideSubtitle @@ -407,7 +423,7 @@ export const AlertsHistogramPanel = memo( )} {headerChildren != null && headerChildren} - {chartOptionsContextMenu != null && ( + {chartOptionsContextMenu != null && !isChartEmbeddablesEnabled && ( {chartOptionsContextMenu(uniqueQueryId)} @@ -418,16 +434,17 @@ export const AlertsHistogramPanel = memo( {toggleStatus ? ( - isChartEmbeddablesEnabled && (getLensAttributes || lensAttributes) && timerange ? ( + isChartEmbeddablesEnabled && getLensAttributes && timerange ? ( ) : isInitialLoading ? ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx index 83df1b0018b1fd..50caf3fb661f73 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -28,6 +28,7 @@ import { } from '../../../components/alerts_kpis/common/config'; import { AlertsCountPanel } from '../../../components/alerts_kpis/alerts_count_panel'; import { GROUP_BY_LABEL } from '../../../components/alerts_kpis/common/translations'; +import type { Status } from '../../../../../common/detection_engine/schemas/common'; const TABLE_PANEL_HEIGHT = 330; // px const TRENT_CHART_HEIGHT = 127; // px @@ -50,6 +51,9 @@ export interface Props { runtimeMappings: MappingRuntimeFields; signalIndexName: string | null; updateDateRangeCallback: UpdateDateRange; + showBuildingBlockAlerts: boolean; + showOnlyThreatIndicatorAlerts: boolean; + filterGroup: Status; } const ChartPanelsComponent: React.FC = ({ @@ -60,6 +64,9 @@ const ChartPanelsComponent: React.FC = ({ runtimeMappings, signalIndexName, updateDateRangeCallback, + showBuildingBlockAlerts, + filterGroup, + showOnlyThreatIndicatorAlerts, }: Props) => { const { alertViewSelection, @@ -138,6 +145,7 @@ const ChartPanelsComponent: React.FC = ({ [alertViewSelection, setAlertViewSelection] ); const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled'); + return (
{alertViewSelection === 'trend' && ( @@ -158,7 +166,7 @@ const ChartPanelsComponent: React.FC = ({ panelHeight={TREND_CHART_PANEL_HEIGHT} query={query} showCountsInLegend={true} - showGroupByPlaceholder={true} + showGroupByPlaceholder={false} showTotalAlertsCount={false} stackByLabel={GROUP_BY_LABEL} title={title} @@ -166,6 +174,9 @@ const ChartPanelsComponent: React.FC = ({ signalIndexName={signalIndexName} updateDateRange={updateDateRangeCallback} runtimeMappings={runtimeMappings} + showBuildingBlockAlerts={showBuildingBlockAlerts} + status={filterGroup} + showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts} /> )} @@ -194,6 +205,9 @@ const ChartPanelsComponent: React.FC = ({ stackByField0={countTableStackBy0} stackByField1={countTableStackBy1} title={title} + showBuildingBlockAlerts={showBuildingBlockAlerts} + status={filterGroup} + showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts} /> )} @@ -225,6 +239,9 @@ const ChartPanelsComponent: React.FC = ({ title={title} riskSubAggregationField="kibana.alert.risk_score" runtimeMappings={runtimeMappings} + showBuildingBlockAlerts={showBuildingBlockAlerts} + status={filterGroup} + showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index cff7c40a0fe804..a2056b94473477 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -355,6 +355,9 @@ const DetectionEnginePageComponent: React.FC = ({ runtimeMappings={runtimeMappings} signalIndexName={signalIndexName} updateDateRangeCallback={updateDateRangeCallback} + filterGroup={filterGroup} + showBuildingBlockAlerts={showBuildingBlockAlerts} + showOnlyThreatIndicatorAlerts={showOnlyThreatIndicatorAlerts} /> diff --git a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx index 61869643860777..83882c9e7b171e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx @@ -86,6 +86,8 @@ const SignalsByCategoryComponent: React.FC = ({ title={i18n.ALERT_TREND} titleSize={onlyField == null ? 'm' : 's'} updateDateRange={updateDateRangeCallback} + showBuildingBlockAlerts={false} + showOnlyThreatIndicatorAlerts={false} /> ); };