diff --git a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_data_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_data_table.tsx
index 4029338a0988..59a4decdd52d 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_data_table.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_data_table.tsx
@@ -97,10 +97,14 @@ const customCellRenderer = (rows: DataTableRecord[]): CustomCellRenderer => ({
return ;
},
[ASSET_FIELDS.ASSET_CRITICALITY]: ({ rowIndex }: EuiDataGridCellValueElementProps) => {
- const criticality = rows[rowIndex].flattened[
- ASSET_FIELDS.ASSET_CRITICALITY
- ] as CriticalityLevelWithUnassigned;
- return ;
+ const criticality = rows[rowIndex].flattened[ASSET_FIELDS.ASSET_CRITICALITY] as
+ | CriticalityLevelWithUnassigned
+ | 'deleted';
+ return (
+
+ );
},
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_table_section.tsx b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_table_section.tsx
index 92e94082c456..1b9bf66a4042 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_table_section.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/asset_inventory_table_section.tsx
@@ -7,7 +7,7 @@
import React, { useState, useEffect } from 'react';
import type { Filter } from '@kbn/es-query';
import type { AssetInventoryURLStateResult } from '../hooks/use_asset_inventory_url_state/use_asset_inventory_url_state';
-import { DEFAULT_TABLE_SECTION_HEIGHT } from '../constants';
+import { ASSET_FIELDS, DEFAULT_TABLE_SECTION_HEIGHT } from '../constants';
import { GroupWrapper } from './grouping/asset_inventory_grouping';
import { useAssetInventoryGrouping } from './grouping/use_asset_inventory_grouping';
import { AssetInventoryDataTable } from './asset_inventory_data_table';
@@ -82,9 +82,9 @@ const GroupWithURLPagination = ({
(
+ renderChildComponent={(currentGroupFilters) => (
{
+ const query = filter?.query;
+ if (query?.bool?.should?.[0]?.bool?.must_not?.exists?.field === ASSET_FIELDS.ASSET_CRITICALITY) {
+ return {
+ meta: filter?.meta ?? { alias: null, disabled: false, negate: false },
+ query: {
+ bool: {
+ filter: {
+ bool: {
+ should: [
+ { term: { [ASSET_FIELDS.ASSET_CRITICALITY]: 'deleted' } },
+ { bool: { must_not: { exists: { field: ASSET_FIELDS.ASSET_CRITICALITY } } } },
+ ],
+ minimum_should_match: 1,
+ },
+ },
+ },
+ },
+ };
+ }
+ return query?.match_phrase || query?.bool?.should || query?.bool?.filter ? filter : null;
+};
+
+const filterTypeGuard = (filter: Filter | null): filter is Filter => filter !== null;
+
+const mergeCurrentAndParentFilters = (
+ currentGroupFilters: Filter[],
+ parentGroupFilters: string | undefined
+) => {
+ return [...currentGroupFilters, ...(parentGroupFilters ? JSON.parse(parentGroupFilters) : [])];
+};
+
const GroupContent = ({
currentGroupFilters,
state,
@@ -122,16 +160,21 @@ const GroupContent = ({
}: GroupContentProps) => {
if (groupingLevel < selectedGroupOptions.length) {
const nextGroupingLevel = groupingLevel + 1;
+
+ const newParentGroupFilters = mergeCurrentAndParentFilters(
+ currentGroupFilters,
+ parentGroupFilters
+ )
+ .map(groupFilterMap)
+ .filter(filterTypeGuard);
+
return (
);
@@ -162,10 +205,12 @@ const GroupWithLocalPagination = ({
const [subgroupPageIndex, setSubgroupPageIndex] = useState(0);
const [subgroupPageSize, setSubgroupPageSize] = useState(10);
+ const groupFilters = parentGroupFilters ? JSON.parse(parentGroupFilters) : [];
+
const { groupData, grouping, isFetching } = useAssetInventoryGrouping({
state: { ...state, pageIndex: subgroupPageIndex, pageSize: subgroupPageSize },
selectedGroup,
- groupFilters: parentGroupFilters ? JSON.parse(parentGroupFilters) : [],
+ groupFilters,
});
/**
@@ -180,13 +225,14 @@ const GroupWithLocalPagination = ({
(
+ renderChildComponent={(currentGroupFilters) => (
)}
activePageIndex={subgroupPageIndex}
@@ -207,6 +253,13 @@ interface DataTableWithLocalPagination {
parentGroupFilters?: string;
}
+const getDataGridFilter = (filter: Filter | null) => {
+ if (!filter) return null;
+ return {
+ ...(filter?.query ?? {}),
+ };
+};
+
const DataTableWithLocalPagination = ({
state,
currentGroupFilters,
@@ -215,12 +268,11 @@ const DataTableWithLocalPagination = ({
const [tablePageIndex, setTablePageIndex] = useState(0);
const [tablePageSize, setTablePageSize] = useState(10);
- const combinedFilters = [
- ...currentGroupFilters,
- ...(parentGroupFilters ? JSON.parse(parentGroupFilters) : []),
- ]
- .map(({ query }) => (query?.match_phrase || query?.bool?.should ? query : null))
- .filter(Boolean);
+ const combinedFilters = mergeCurrentAndParentFilters(currentGroupFilters, parentGroupFilters)
+ .map(groupFilterMap)
+ .filter(filterTypeGuard)
+ .map(getDataGridFilter)
+ .filter((filter): filter is NonNullable => Boolean(filter));
const newState: AssetInventoryURLStateResult = {
...state,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/use_asset_inventory_grouping.ts b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/use_asset_inventory_grouping.ts
index 1cdecad2c7c4..67692131c22b 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/use_asset_inventory_grouping.ts
+++ b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/use_asset_inventory_grouping.ts
@@ -22,6 +22,8 @@ import {
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
+import dedent from 'dedent';
+import type { MappingRuntimeFieldType } from '@elastic/elasticsearch/lib/api/types';
import { useDataViewContext } from '../../hooks/data_view_context';
import type {
AssetInventoryURLStateResult,
@@ -159,24 +161,81 @@ export const useAssetInventoryGrouping = ({
// This is recommended by the grouping component to cover an edge case where `selectedGroup` has multiple values
const uniqueValue = useMemo(() => `${selectedGroup}-${uuid.v4()}`, [selectedGroup]);
- const groupingQuery = getGroupingQuery({
- additionalFilters: query ? [query, additionalFilters] : [additionalFilters],
- groupByField: currentSelectedGroup,
- uniqueValue,
- pageNumber: pageIndex * pageSize,
- size: pageSize,
- sort: [{ groupByField: { order: 'desc' } }],
- statsAggregations: getAggregationsByGroupField(currentSelectedGroup),
- rootAggregations: [
- {
- ...(!isNoneGroup([currentSelectedGroup]) && {
- nullGroupItems: {
- missing: { field: currentSelectedGroup },
+ const groupingQuery = useMemo(
+ () => ({
+ ...getGroupingQuery({
+ additionalFilters: query ? [query, additionalFilters] : [additionalFilters],
+ groupByField: currentSelectedGroup,
+ uniqueValue,
+ pageNumber: pageIndex * pageSize,
+ size: pageSize,
+ sort: [{ groupByField: { order: 'desc' } }],
+ statsAggregations: getAggregationsByGroupField(currentSelectedGroup),
+ rootAggregations: [
+ {
+ ...(!isNoneGroup([currentSelectedGroup]) && {
+ nullGroupItems:
+ currentSelectedGroup === ASSET_FIELDS.ASSET_CRITICALITY
+ ? {
+ filter: {
+ bool: {
+ should: [
+ { term: { [ASSET_FIELDS.ASSET_CRITICALITY]: 'deleted' } },
+ {
+ bool: {
+ must_not: { exists: { field: ASSET_FIELDS.ASSET_CRITICALITY } },
+ },
+ },
+ ],
+ minimum_should_match: 1,
+ },
+ },
+ }
+ : { missing: { field: currentSelectedGroup } },
+ }),
},
- }),
+ ],
+ }),
+ runtime_mappings: {
+ groupByField: {
+ type: 'keyword' as MappingRuntimeFieldType,
+ script: {
+ source: dedent(`
+ def groupValues = [];
+ if (doc.containsKey(params['selectedGroup']) && !doc[params['selectedGroup']].empty) {
+ groupValues = doc[params['selectedGroup']];
+ }
+ // If selectedGroup is 'asset.criticality', treat 'deleted' as undefined group
+ boolean treatAsUndefined = false;
+ int count = groupValues.size();
+ if (params['selectedGroup'] == 'asset.criticality') {
+ boolean isDeleted = false;
+ for (def v : groupValues) {
+ if (v == 'deleted') {
+ isDeleted = true;
+ break;
+ }
+ }
+ treatAsUndefined = (count == 0 || count > 100 || isDeleted);
+ } else {
+ treatAsUndefined = (count == 0 || count > 100);
+ }
+ if (treatAsUndefined) {
+ emit(params['uniqueValue']);
+ } else {
+ emit(groupValues.join(params['uniqueValue']));
+ }
+ `),
+ params: {
+ selectedGroup: currentSelectedGroup,
+ uniqueValue,
+ },
+ },
+ },
},
- ],
- });
+ }),
+ [currentSelectedGroup, uniqueValue, additionalFilters, query, pageIndex, pageSize]
+ );
const { data, isFetching } = useFetchGroupedData({
query: groupingQuery,
diff --git a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/utils/asset_inventory_group_renderer.tsx b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/utils/asset_inventory_group_renderer.tsx
index 07b19d4e73b6..4bbd3144a0ed 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/utils/asset_inventory_group_renderer.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/asset_inventory/components/grouping/utils/asset_inventory_group_renderer.tsx
@@ -69,6 +69,13 @@ export const groupPanelRenderer: GroupPanelRenderer =
switch (selectedGroup) {
case ASSET_GROUPING_OPTIONS.ASSET_CRITICALITY:
+ const rawCriticalityLevel = firstNonNullValue(bucket.assetCriticality?.buckets?.[0]?.key) as
+ | CriticalityLevelWithUnassigned
+ | 'deleted';
+
+ const criticalityLevel =
+ rawCriticalityLevel === 'deleted' ? 'unassigned' : rawCriticalityLevel;
+
return nullGroupMessage ? (
@@ -84,13 +91,7 @@ export const groupPanelRenderer: GroupPanelRenderer =
-
+
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory/asset_inventory_data_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory/asset_inventory_data_client.ts
index ebb46ec784f1..aed5e6aebc19 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory/asset_inventory_data_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/asset_inventory/asset_inventory_data_client.ts
@@ -165,14 +165,18 @@ export class AssetInventoryDataClient {
}
}
- await installDataView(
- secSolutionContext.getSpaceId(),
- secSolutionContext.getDataViewsService(),
- ASSET_INVENTORY_DATA_VIEW_NAME,
- ASSET_INVENTORY_INDEX_PATTERN,
- ASSET_INVENTORY_DATA_VIEW_ID_PREFIX,
- logger
- );
+ try {
+ await installDataView(
+ secSolutionContext.getSpaceId(),
+ secSolutionContext.getDataViewsService(),
+ ASSET_INVENTORY_DATA_VIEW_NAME,
+ ASSET_INVENTORY_INDEX_PATTERN,
+ ASSET_INVENTORY_DATA_VIEW_ID_PREFIX,
+ logger
+ );
+ } catch (error) {
+ logger.error(`Error installing asset inventory data view: ${error.message}`);
+ }
logger.debug(`Enabled asset inventory`);