diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts
index 0dc376b19f179..cbeb334e2aa61 100644
--- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts
+++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts
@@ -190,18 +190,37 @@ export const getBenchmarkCisName = (benchmarkId: BenchmarksCisId) => {
}
};
+const CLOUD_PROVIDER_NAMES = {
+ AWS: 'Amazon Web Services',
+ AZURE: 'Microsoft Azure',
+ GCP: 'Google Cloud Platform',
+};
+
export const getBenchmarkApplicableTo = (benchmarkId: BenchmarksCisId) => {
switch (benchmarkId) {
case 'cis_k8s':
return 'Kubernetes';
case 'cis_azure':
- return 'Microsoft Azure';
+ return CLOUD_PROVIDER_NAMES.AZURE;
case 'cis_aws':
- return 'Amazon Web Services';
+ return CLOUD_PROVIDER_NAMES.AWS;
case 'cis_eks':
return 'Amazon Elastic Kubernetes Service';
case 'cis_gcp':
- return 'Google Cloud Provider';
+ return CLOUD_PROVIDER_NAMES.GCP;
+ }
+};
+
+export const getCloudProviderNameFromAbbreviation = (cloudProvider: string) => {
+ switch (cloudProvider) {
+ case 'azure':
+ return CLOUD_PROVIDER_NAMES.AZURE;
+ case 'aws':
+ return CLOUD_PROVIDER_NAMES.AWS;
+ case 'gcp':
+ return CLOUD_PROVIDER_NAMES.GCP;
+ default:
+ return cloudProvider;
}
};
diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx
new file mode 100644
index 0000000000000..b6acdac0ee1b1
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_provider_icon.tsx
@@ -0,0 +1,47 @@
+/*
+ * 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 React from 'react';
+import { EuiIcon, EuiToolTip, IconSize } from '@elastic/eui';
+import { CSSInterpolation } from '@emotion/serialize';
+import { getCloudProviderNameFromAbbreviation } from '../../common/utils/helpers';
+import googleCloudLogo from '../assets/icons/google_cloud_logo.svg';
+
+interface Props {
+ cloudProvider: string;
+ style?: CSSInterpolation;
+ size?: IconSize;
+}
+
+const getCloudProviderIcon = (cloudProvider: string) => {
+ switch (cloudProvider) {
+ case 'azure':
+ return 'logoAzure';
+ case 'aws':
+ return 'logoAWS';
+ case 'gcp':
+ return googleCloudLogo;
+ default:
+ return undefined;
+ }
+};
+
+export const CloudProviderIcon = ({ cloudProvider, size, style }: Props) => {
+ const iconType = getCloudProviderIcon(cloudProvider);
+
+ if (!iconType) {
+ return null;
+ }
+
+ const name = getCloudProviderNameFromAbbreviation(cloudProvider);
+
+ return (
+
+
+
+ );
+};
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_severity_map.tsx
similarity index 89%
rename from x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx
rename to x-pack/plugins/cloud_security_posture/public/components/vulnerability_severity_map.tsx
index 78e5513271441..b22846b624b9c 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/components/vulnerability_severity_map.tsx
@@ -16,9 +16,9 @@ import {
} from '@elastic/eui';
import { PaletteColorStop } from '@elastic/eui/src/components/color_picker/color_palette_picker';
import { i18n } from '@kbn/i18n';
-import { getSeverityStatusColor } from '../../../common/utils/get_vulnerability_colors';
-import { VulnSeverity } from '../../../../common/types_old';
-import { SeverityStatusBadge } from '../../../components/vulnerability_badges';
+import { getSeverityStatusColor } from '../common/utils/get_vulnerability_colors';
+import { VulnSeverity } from '../../common/types_old';
+import { SeverityStatusBadge } from './vulnerability_badges';
interface Props {
total: number;
@@ -50,7 +50,7 @@ const formatPercentage = (percentage: number) => {
return `${percentage.toFixed(1)}%`;
};
-export const SeverityMap = ({ severityMap, total }: Props) => {
+export const VulnerabilitySeverityMap = ({ severityMap, total }: Props) => {
const { euiTheme } = useEuiTheme();
const severityMapPallet: PaletteColorStop[] = [];
@@ -87,10 +87,7 @@ export const SeverityMap = ({ severityMap, total }: Props) => {
width: 256px;
`}
anchorClassName={css`
- height: ${euiTheme.size.xl};
- flex-grow: 1;
- display: flex;
- align-items: center;
+ margin-left: ${euiTheme.size.xs};
`}
position="left"
title={i18n.translate('xpack.csp.vulnerabilitiesByResource.severityMap.tooltipTitle', {
@@ -102,7 +99,8 @@ export const SeverityMap = ({ severityMap, total }: Props) => {
type="fixed"
palette={severityMapPallet}
className={css`
- width: 100%;
+ width: 80px;
+ height: 6px;
`}
/>
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts
index 52361bb9b2c6b..9ad49296dd0dc 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts
@@ -19,10 +19,15 @@ export const VULNERABILITY_FIELDS = {
PACKAGE_NAME: 'package.name',
PACKAGE_VERSION: 'package.version',
PACKAGE_FIXED_VERSION: 'package.fixed_version',
+ CLOUD_ACCOUNT_NAME: 'cloud.account.name',
+ CLOUD_PROVIDER: 'cloud.provider',
+ DESCRIPTION: 'vulnerability.description',
} as const;
export const GROUPING_OPTIONS = {
RESOURCE_NAME: VULNERABILITY_FIELDS.RESOURCE_NAME,
+ CLOUD_ACCOUNT_NAME: VULNERABILITY_FIELDS.CLOUD_ACCOUNT_NAME,
+ CVE: VULNERABILITY_FIELDS.VULNERABILITY_ID,
};
export const defaultGroupingOptions: GroupOption[] = [
@@ -30,6 +35,14 @@ export const defaultGroupingOptions: GroupOption[] = [
label: GROUPING_LABELS.RESOURCE_NAME,
key: GROUPING_OPTIONS.RESOURCE_NAME,
},
+ {
+ label: GROUPING_LABELS.CLOUD_ACCOUNT_NAME,
+ key: GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME,
+ },
+ {
+ label: 'CVE',
+ key: GROUPING_OPTIONS.CVE,
+ },
];
export const getDefaultQuery = ({
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx
index fda12c4b06d41..371ab2fa038c0 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx
@@ -34,6 +34,21 @@ export interface VulnerabilitiesGroupingAggregation {
buckets?: GenericBuckets[];
};
isLoading?: boolean;
+ critical?: {
+ doc_count?: NumberOrNull;
+ };
+ high?: {
+ doc_count?: NumberOrNull;
+ };
+ medium?: {
+ doc_count?: NumberOrNull;
+ };
+ low?: {
+ doc_count?: NumberOrNull;
+ };
+ cloudProvider?: {
+ buckets?: GenericBuckets[];
+ };
}
export type VulnerabilitiesRootGroupingAggregation =
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts
deleted file mode 100644
index 3aa152d427143..0000000000000
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_by_resource.ts
+++ /dev/null
@@ -1,174 +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 { useQuery } from '@tanstack/react-query';
-import { lastValueFrom } from 'rxjs';
-import type { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common';
-import {
- SearchRequest,
- SearchResponse,
- AggregationsCardinalityAggregate,
- AggregationsMultiBucketAggregateBase,
- AggregationsSingleBucketAggregateBase,
- AggregationsStringRareTermsBucketKeys,
- AggregationsStringTermsBucketKeys,
- SortOrder,
-} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
-import {
- LATEST_VULNERABILITIES_INDEX_PATTERN,
- VULNERABILITIES_SEVERITY,
-} from '../../../../common/constants';
-
-import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants';
-import { useKibana } from '../../../common/hooks/use_kibana';
-import { showErrorToast } from '../../../common/utils/show_error_toast';
-import { FindingsBaseEsQuery } from '../../../common/types';
-
-type LatestFindingsRequest = IKibanaSearchRequest;
-type LatestFindingsResponse = IKibanaSearchResponse>;
-
-interface VulnerabilitiesAggs {
- count: AggregationsMultiBucketAggregateBase;
- total: AggregationsCardinalityAggregate;
- resources: AggregationsMultiBucketAggregateBase;
-}
-
-interface FindingsAggBucket extends AggregationsStringRareTermsBucketKeys {
- name: AggregationsMultiBucketAggregateBase;
- region: AggregationsMultiBucketAggregateBase;
- critical: AggregationsSingleBucketAggregateBase;
- high: AggregationsSingleBucketAggregateBase;
- medium: AggregationsSingleBucketAggregateBase;
- low: AggregationsSingleBucketAggregateBase;
-}
-
-interface VulnerabilitiesQuery extends FindingsBaseEsQuery {
- sortOrder: SortOrder;
- enabled: boolean;
- pageIndex: number;
- pageSize: number;
-}
-
-export const getQuery = ({
- query,
- sortOrder = 'desc',
- pageIndex,
- pageSize,
-}: VulnerabilitiesQuery) => ({
- index: LATEST_VULNERABILITIES_INDEX_PATTERN,
- query,
- aggs: {
- total: { cardinality: { field: 'resource.id' } },
- resources: {
- terms: {
- field: 'resource.id',
- size: MAX_FINDINGS_TO_LOAD * 3,
- // in case there are more resources then size, ensuring resources with more vulnerabilities
- // will be included first, and then vulnerabilities with critical and high severity
- order: [{ _count: sortOrder }, { critical: 'desc' }, { high: 'desc' }, { medium: 'desc' }],
- },
- aggs: {
- vulnerabilitiesCountBucketSort: {
- bucket_sort: {
- sort: [{ _count: { order: sortOrder } }],
- from: pageIndex * pageSize,
- size: pageSize,
- },
- },
- name: {
- terms: { field: 'resource.name', size: 1 },
- },
- region: {
- terms: { field: 'cloud.region', size: 1 },
- },
- critical: {
- filter: {
- term: {
- 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.CRITICAL },
- },
- },
- },
- high: {
- filter: {
- term: {
- 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.HIGH },
- },
- },
- },
- medium: {
- filter: {
- term: {
- 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.MEDIUM },
- },
- },
- },
- low: {
- filter: {
- term: { 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.LOW } },
- },
- },
- },
- },
- },
- size: 0,
-});
-const getFirstKey = (
- buckets: AggregationsMultiBucketAggregateBase['buckets']
-) => {
- return !!Array.isArray(buckets) && !!buckets.length ? (buckets[0].key as string) : '';
-};
-const createVulnerabilitiesByResource = (resource: FindingsAggBucket) => ({
- resource: {
- id: resource.key,
- name: getFirstKey(resource.name.buckets),
- },
- cloud: {
- region: getFirstKey(resource.region.buckets),
- },
- vulnerabilities_count: resource.doc_count,
- severity_map: {
- critical: resource.critical.doc_count,
- high: resource.high.doc_count,
- medium: resource.medium.doc_count,
- low: resource.low.doc_count,
- },
-});
-
-export const useLatestVulnerabilitiesByResource = (options: VulnerabilitiesQuery) => {
- const {
- data,
- notifications: { toasts },
- } = useKibana().services;
- return useQuery(
- [LATEST_VULNERABILITIES_INDEX_PATTERN, 'resource', options],
- async () => {
- const {
- rawResponse: { hits, aggregations },
- } = await lastValueFrom(
- data.search.search({
- params: getQuery(options),
- })
- );
-
- if (!aggregations) throw new Error('Failed to aggregate by resource');
-
- if (!Array.isArray(aggregations.resources.buckets))
- throw new Error('Failed to group by, missing resource id');
-
- return {
- page: aggregations.resources.buckets.map(createVulnerabilitiesByResource),
- total: aggregations.total.value,
- total_vulnerabilities: hits.total as number,
- };
- },
- {
- staleTime: 5000,
- keepPreviousData: true,
- enabled: options.enabled,
- onError: (err: Error) => showErrorToast(toasts, err),
- }
- );
-};
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx
index 45fdc5c71a342..51d314f570a34 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx
@@ -16,7 +16,10 @@ import {
import { useMemo } from 'react';
import { LOCAL_STORAGE_VULNERABILITIES_GROUPING_KEY } from '../../../common/constants';
import { useDataViewContext } from '../../../common/contexts/data_view_context';
-import { LATEST_VULNERABILITIES_RETENTION_POLICY } from '../../../../common/constants';
+import {
+ LATEST_VULNERABILITIES_RETENTION_POLICY,
+ VULNERABILITIES_SEVERITY,
+} from '../../../../common/constants';
import {
VulnerabilitiesGroupingAggregation,
VulnerabilitiesRootGroupingAggregation,
@@ -48,12 +51,45 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => {
field,
},
},
+ critical: {
+ filter: {
+ term: {
+ 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.CRITICAL },
+ },
+ },
+ },
+ high: {
+ filter: {
+ term: {
+ 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.HIGH },
+ },
+ },
+ },
+ medium: {
+ filter: {
+ term: {
+ 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.MEDIUM },
+ },
+ },
+ },
+ low: {
+ filter: {
+ term: { 'vulnerability.severity': { value: VULNERABILITIES_SEVERITY.LOW } },
+ },
+ },
},
];
switch (field) {
case GROUPING_OPTIONS.RESOURCE_NAME:
return [...aggMetrics, getTermAggregation('resourceId', VULNERABILITY_FIELDS.RESOURCE_ID)];
+ case GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME:
+ return [
+ ...aggMetrics,
+ getTermAggregation('cloudProvider', VULNERABILITY_FIELDS.CLOUD_PROVIDER),
+ ];
+ case GROUPING_OPTIONS.CVE:
+ return [...aggMetrics, getTermAggregation('description', VULNERABILITY_FIELDS.DESCRIPTION)];
}
return aggMetrics;
};
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx
index 82626fd684513..7de747b458b06 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx
@@ -16,12 +16,20 @@ import {
import { css } from '@emotion/react';
import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src';
import React from 'react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { getCloudProviderNameFromAbbreviation } from '../../../common/utils/helpers';
import { VulnerabilitiesGroupingAggregation } from './hooks/use_grouped_vulnerabilities';
import { GROUPING_OPTIONS } from './constants';
import { VULNERABILITIES_GROUPING_COUNTER } from './test_subjects';
import { NULL_GROUPING_MESSAGES, NULL_GROUPING_UNIT, VULNERABILITIES } from './translations';
import { getAbbreviatedNumber } from '../../common/utils/get_abbreviated_number';
-import { LoadingGroup, NullGroup } from '../../components/cloud_security_grouping';
+import {
+ firstNonNullValue,
+ LoadingGroup,
+ NullGroup,
+} from '../../components/cloud_security_grouping';
+import { VulnerabilitySeverityMap } from '../../components/vulnerability_severity_map';
+import { CloudProviderIcon } from '../../components/cloud_provider_icon';
export const groupPanelRenderer: GroupPanelRenderer = (
selectedGroup,
@@ -37,6 +45,12 @@ export const groupPanelRenderer: GroupPanelRenderer
);
+ const cloudProvider = firstNonNullValue(bucket.cloudProvider?.buckets?.[0]?.key);
+ const description = firstNonNullValue(bucket.description?.buckets?.[0]?.key);
+ const cloudProviderName = cloudProvider
+ ? getCloudProviderNameFromAbbreviation(cloudProvider)
+ : '';
+
switch (selectedGroup) {
case GROUPING_OPTIONS.RESOURCE_NAME:
return nullGroupMessage ? (
@@ -66,6 +80,32 @@ export const groupPanelRenderer: GroupPanelRenderer
);
+ case GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME:
+ return nullGroupMessage ? (
+ renderNullGroup(NULL_GROUPING_MESSAGES.CLOUD_ACCOUNT_NAME)
+ ) : (
+
+ {cloudProvider && (
+
+
+
+ )}
+
+
+
+
+ {bucket.key_as_string}
+
+
+
+
+ {cloudProviderName}
+
+
+
+
+
+ );
default:
return nullGroupMessage ? (
renderNullGroup(NULL_GROUPING_MESSAGES.DEFAULT)
@@ -78,6 +118,20 @@ export const groupPanelRenderer: GroupPanelRenderer{bucket.key_as_string}
+ {description && (
+
+
+
+ {description}
+
+
+
+ )}
@@ -109,6 +163,35 @@ const VulnerabilitiesCountComponent = ({
const VulnerabilitiesCount = React.memo(VulnerabilitiesCountComponent);
+const SeverityStatsComponent = ({
+ bucket,
+}: {
+ bucket: RawBucket;
+}) => {
+ const severityMap = {
+ critical: bucket.critical?.doc_count ?? 0,
+ high: bucket.high?.doc_count ?? 0,
+ medium: bucket.medium?.doc_count ?? 0,
+ low: bucket.low?.doc_count ?? 0,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+const SeverityStats = React.memo(SeverityStatsComponent);
+
export const groupStatsRenderer = (
selectedGroup: string,
bucket: RawBucket
@@ -118,6 +201,10 @@ export const groupStatsRenderer = (
title: VULNERABILITIES,
renderer: ,
},
+ {
+ title: '',
+ renderer: ,
+ },
];
return defaultBadges;
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts
index 65ca61056f612..f7d88f396f5c6 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts
@@ -40,17 +40,26 @@ export const NULL_GROUPING_MESSAGES = {
RESOURCE_NAME: i18n.translate('xpack.csp.vulnerabilities.grouping.resource.nullGroupTitle', {
defaultMessage: 'No resource',
}),
+ CLOUD_ACCOUNT_NAME: i18n.translate(
+ 'xpack.csp.vulnerabilities.grouping.cloudAccount.nullGroupTitle',
+ {
+ defaultMessage: 'No cloud account',
+ }
+ ),
DEFAULT: i18n.translate('xpack.csp.vulnerabilities.grouping.default.nullGroupTitle', {
defaultMessage: 'No grouping',
}),
};
export const GROUPING_LABELS = {
- RESOURCE_NAME: i18n.translate('xpack.csp.findings.latestFindings.groupByResource', {
+ RESOURCE_NAME: i18n.translate('xpack.csp.vulnerabilities.groupBy.resource', {
defaultMessage: 'Resource',
}),
+ CLOUD_ACCOUNT_NAME: i18n.translate('xpack.csp.vulnerabilities.groupBy.cloudAccount', {
+ defaultMessage: 'Cloud account',
+ }),
};
-export const groupingTitle = i18n.translate('xpack.csp.vulnerabilities.latestFindings.groupBy', {
+export const groupingTitle = i18n.translate('xpack.csp.vulnerabilities.groupBy', {
defaultMessage: 'Group vulnerabilities by',
});
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/vulnerabilities_pathname_handler.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/vulnerabilities_pathname_handler.ts
deleted file mode 100644
index 0d9e0b660f1f8..0000000000000
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/vulnerabilities_pathname_handler.ts
+++ /dev/null
@@ -1,24 +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 { EuiComboBoxOptionOption } from '@elastic/eui';
-import { findingsNavigation } from '../../../common/navigation/constants';
-import { FindingsGroupByKind } from '../../../common/types';
-
-export const vulnerabilitiesPathnameHandler = (
- opts: Array>
-) => {
- const [firstOption] = opts;
-
- switch (firstOption?.value) {
- case 'resource':
- return findingsNavigation.vulnerabilities_by_resource.path;
- case 'default':
- default:
- return findingsNavigation.vulnerabilities.path;
- }
-};
diff --git a/x-pack/test/cloud_security_posture_functional/mocks/vulnerabilities_latest_mock.ts b/x-pack/test/cloud_security_posture_functional/mocks/vulnerabilities_latest_mock.ts
index f0ffd4aebb388..301c328993c8b 100644
--- a/x-pack/test/cloud_security_posture_functional/mocks/vulnerabilities_latest_mock.ts
+++ b/x-pack/test/cloud_security_posture_functional/mocks/vulnerabilities_latest_mock.ts
@@ -182,10 +182,10 @@ export const vulnerabilitiesLatestMock = [
},
},
cloud: {
- provider: 'aws',
+ provider: 'gcp',
region: 'eu-west-1',
account: {
- name: 'elastic-security-cloud-security-dev',
+ name: 'elastic-security-cloud-security-gcp',
id: '704479110758',
},
},
diff --git a/x-pack/test/cloud_security_posture_functional/pages/vulnerabilities_grouping.ts b/x-pack/test/cloud_security_posture_functional/pages/vulnerabilities_grouping.ts
index 8e569d27b8a4d..db0702ab289e8 100644
--- a/x-pack/test/cloud_security_posture_functional/pages/vulnerabilities_grouping.ts
+++ b/x-pack/test/cloud_security_posture_functional/pages/vulnerabilities_grouping.ts
@@ -19,6 +19,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const resourceName1 = 'name-ng-1-Node';
const resourceName2 = 'othername-june12-8-8-0-1';
+ const cloudAccountName1 = 'elastic-security-cloud-security-dev';
+ const cloudAccountName2 = 'elastic-security-cloud-security-gcp';
+
+ const cloudProviderName1 = 'Amazon Web Services';
+ const cloudProviderName2 = 'Google Cloud Platform';
+
describe('Vulnerabilities Page - Grouping', function () {
this.tags(['cloud_security_posture_findings_grouping']);
let findings: typeof pageObjects.findings;
@@ -45,11 +51,88 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
describe('Default Grouping', async () => {
- it('groups vulnerabilities by resource and sort by compliance score desc', async () => {
- const groupSelector = await findings.groupSelector();
+ it('groups vulnerabilities by cloud account and sort by number of vulnerabilities desc', async () => {
+ const groupSelector = findings.groupSelector();
await groupSelector.openDropDown();
- await groupSelector.setValue('Resource');
+ await groupSelector.setValue('Cloud account');
+
+ const grouping = await findings.findingsGrouping();
+
+ const order = [
+ {
+ cloudAccountName: cloudAccountName1,
+ cloudProviderName: cloudProviderName1,
+ findingsCount: '1',
+ },
+ {
+ cloudAccountName: cloudAccountName2,
+ cloudProviderName: cloudProviderName2,
+ findingsCount: '1',
+ },
+ ];
+
+ await asyncForEach(
+ order,
+ async ({ cloudAccountName, cloudProviderName, findingsCount }, index) => {
+ const groupRow = await grouping.getRowAtIndex(index);
+ expect(await groupRow.getVisibleText()).to.contain(cloudAccountName);
+ expect(await groupRow.getVisibleText()).to.contain(cloudProviderName);
+
+ expect(
+ await (
+ await groupRow.findByTestSubject('vulnerabilities_grouping_counter')
+ ).getVisibleText()
+ ).to.be(findingsCount);
+ }
+ );
+
+ const groupCount = await grouping.getGroupCount();
+ expect(groupCount).to.be('2 groups');
+
+ const unitCount = await grouping.getUnitCount();
+ expect(unitCount).to.be('2 vulnerabilities');
+ });
+ it('groups vulnerabilities by CVE and sort by number of vulnerabilities desc', async () => {
+ const groupSelector = findings.groupSelector();
+ await groupSelector.openDropDown();
+ await groupSelector.setValue('CVE');
+
+ const grouping = await findings.findingsGrouping();
+
+ const order = [
+ {
+ name: vulnerabilitiesLatestMock[0].vulnerability.id,
+ description: vulnerabilitiesLatestMock[0].vulnerability.description,
+ findingsCount: '1',
+ },
+ {
+ name: vulnerabilitiesLatestMock[1].vulnerability.id,
+ description: vulnerabilitiesLatestMock[1].vulnerability.description,
+ findingsCount: '1',
+ },
+ ];
+
+ await asyncForEach(order, async ({ name, description, findingsCount }, index) => {
+ const groupRow = await grouping.getRowAtIndex(index);
+ expect(await groupRow.getVisibleText()).to.contain(name);
+ expect(await groupRow.getVisibleText()).to.contain(description);
+ expect(
+ await (
+ await groupRow.findByTestSubject('vulnerabilities_grouping_counter')
+ ).getVisibleText()
+ ).to.be(findingsCount);
+ });
+
+ const groupCount = await grouping.getGroupCount();
+ expect(groupCount).to.be('2 groups');
+
+ const unitCount = await grouping.getUnitCount();
+ expect(unitCount).to.be('2 vulnerabilities');
+ });
+ it('groups vulnerabilities by resource and sort by number of vulnerabilities desc', async () => {
+ const groupSelector = findings.groupSelector();
+ await groupSelector.setValue('Resource');
const grouping = await findings.findingsGrouping();
const resourceOrder = [