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 cbeb334e2aa61..6222b457e7ad6 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -196,6 +196,15 @@ const CLOUD_PROVIDER_NAMES = { GCP: 'Google Cloud Platform', }; +export const CLOUD_PROVIDERS = { + AWS: 'aws', + AZURE: 'azure', + GCP: 'gcp', +}; + +/** + * Returns the cloud provider name or benchmark applicable name for the given benchmark id + */ export const getBenchmarkApplicableTo = (benchmarkId: BenchmarksCisId) => { switch (benchmarkId) { case 'cis_k8s': @@ -205,7 +214,7 @@ export const getBenchmarkApplicableTo = (benchmarkId: BenchmarksCisId) => { case 'cis_aws': return CLOUD_PROVIDER_NAMES.AWS; case 'cis_eks': - return 'Amazon Elastic Kubernetes Service'; + return 'Amazon Elastic Kubernetes Service (EKS)'; case 'cis_gcp': return CLOUD_PROVIDER_NAMES.GCP; } diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index bd266c98b8015..735a28a8ed0e1 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -230,3 +230,10 @@ export const DETECTION_ENGINE_RULES_KEY = 'detection_engine_rules'; export const DETECTION_ENGINE_ALERTS_KEY = 'detection_engine_alerts'; export const DEFAULT_GROUPING_TABLE_HEIGHT = 512; + +export const FINDINGS_GROUPING_OPTIONS = { + RESOURCE_NAME: 'resource.name', + RULE_NAME: 'rule.name', + CLOUD_ACCOUNT_NAME: 'cloud.account.name', + ORCHESTRATOR_CLUSTER_NAME: 'orchestrator.cluster.name', +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts index fbeeeb32a0c2e..4b98057fc10c4 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_findings.ts @@ -57,7 +57,7 @@ const useNavigate = (pathname: string, dataViewId = SECURITY_DEFAULT_DATA_VIEW_I const { services } = useKibana(); return useCallback( - (filterParams: NavFilter = {}) => { + (filterParams: NavFilter = {}, groupBy?: string[]) => { const filters = Object.entries(filterParams).map(([key, filterValue]) => createFilter(key, filterValue, dataViewId) ); @@ -68,10 +68,11 @@ const useNavigate = (pathname: string, dataViewId = SECURITY_DEFAULT_DATA_VIEW_I // Set query language from user's preference query: services.data.query.queryString.getDefaultQuery(), filters, + ...(groupBy && { groupBy }), }), }); }, - [pathname, history, services.data.query.queryString, dataViewId] + [history, pathname, services.data.query.queryString, dataViewId] ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx new file mode 100644 index 0000000000000..1973620c8d9d8 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx @@ -0,0 +1,69 @@ +/* + * 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 { render, fireEvent } from '@testing-library/react'; +import { AccountsEvaluatedWidget } from './accounts_evaluated_widget'; +import { BenchmarkData } from '../../common/types_old'; +import { TestProvider } from '../test/test_provider'; + +const mockNavToFindings = jest.fn(); +jest.mock('../common/hooks/use_navigate_findings', () => ({ + useNavigateFindings: () => mockNavToFindings, +})); + +describe('AccountsEvaluatedWidget', () => { + const benchmarkAssets = [ + { meta: { benchmarkId: 'cis_aws', assetCount: 10 } }, + { meta: { benchmarkId: 'cis_k8s', assetCount: 20 } }, + ] as BenchmarkData[]; + + it('renders the component with benchmark data correctly', () => { + const { getByText } = render( + + + + ); + + expect(getByText('10')).toBeInTheDocument(); + expect(getByText('20')).toBeInTheDocument(); + }); + + it('calls navToFindingsByCloudProvider when a benchmark with provider is clicked', () => { + const { getByText } = render( + + + + ); + + fireEvent.click(getByText('10')); + + expect(mockNavToFindings).toHaveBeenCalledWith( + { + 'cloud.provider': 'aws', + }, + ['cloud.account.name'] + ); + }); + + it('calls navToFindingsByCisBenchmark when a benchmark with benchmarkId is clicked', () => { + const { getByText } = render( + + + + ); + + fireEvent.click(getByText('20')); + + expect(mockNavToFindings).toHaveBeenCalledWith( + { + 'rule.benchmark.id': 'cis_k8s', + }, + ['orchestrator.cluster.name'] + ); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index a9b118be899ab..aeb734005a65b 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -7,38 +7,40 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; +import { CLOUD_PROVIDERS, getBenchmarkApplicableTo } from '../../common/utils/helpers'; import { CIS_AWS, CIS_GCP, CIS_AZURE, CIS_K8S, CIS_EKS } from '../../common/constants'; import { CISBenchmarkIcon } from './cis_benchmark_icon'; import { CompactFormattedNumber } from './compact_formatted_number'; import { useNavigateFindings } from '../common/hooks/use_navigate_findings'; import { BenchmarkData } from '../../common/types_old'; +import { FINDINGS_GROUPING_OPTIONS } from '../common/constants'; // order in array will determine order of appearance in the dashboard const benchmarks = [ { type: CIS_AWS, - name: 'Amazon Web Services (AWS)', - provider: 'aws', + name: getBenchmarkApplicableTo(CIS_AWS), + provider: CLOUD_PROVIDERS.AWS, }, { type: CIS_GCP, - name: 'Google Cloud Platform (GCP)', - provider: 'gcp', + name: getBenchmarkApplicableTo(CIS_GCP), + provider: CLOUD_PROVIDERS.GCP, }, { type: CIS_AZURE, - name: 'Azure', - provider: 'azure', + name: getBenchmarkApplicableTo(CIS_AZURE), + provider: CLOUD_PROVIDERS.AZURE, }, { type: CIS_K8S, - name: 'Kubernetes', - benchmarkId: 'cis_k8s', + name: getBenchmarkApplicableTo(CIS_K8S), + benchmarkId: CIS_K8S, }, { type: CIS_EKS, - name: 'Amazon Elastic Kubernetes Service (EKS)', - benchmarkId: 'cis_eks', + name: getBenchmarkApplicableTo(CIS_EKS), + benchmarkId: CIS_EKS, }, ]; @@ -59,11 +61,13 @@ export const AccountsEvaluatedWidget = ({ const navToFindings = useNavigateFindings(); const navToFindingsByCloudProvider = (provider: string) => { - navToFindings({ 'cloud.provider': provider }); + navToFindings({ 'cloud.provider': provider }, [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]); }; const navToFindingsByCisBenchmark = (cisBenchmark: string) => { - navToFindings({ 'rule.benchmark.id': cisBenchmark }); + navToFindings({ 'rule.benchmark.id': cisBenchmark }, [ + FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + ]); }; const benchmarkElements = benchmarks.map((benchmark) => { @@ -75,10 +79,10 @@ export const AccountsEvaluatedWidget = ({ key={benchmark.type} onClick={() => { if (benchmark.provider) { - navToFindingsByCloudProvider(benchmark.provider); + return navToFindingsByCloudProvider(benchmark.provider); } if (benchmark.benchmarkId) { - navToFindingsByCisBenchmark(benchmark.benchmarkId); + return navToFindingsByCisBenchmark(benchmark.benchmarkId); } }} css={css` diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx index dc140fef121b3..ac9b48ddfdbfe 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx @@ -17,23 +17,32 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; import { getBenchmarkIdQuery } from './benchmarks_section'; import { BenchmarkData } from '../../../../common/types_old'; import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings'; import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; + +interface BenchmarkInfo { + name: string; + assetType: string; + handleClick: () => void; +} + export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) => { const navToFindings = useNavigateFindings(); - const handleBenchmarkClick = () => { - return navToFindings(getBenchmarkIdQuery(benchmark)); - }; + const handleClickCloudProvider = () => + navToFindings(getBenchmarkIdQuery(benchmark), [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]); + + const handleClickCluster = () => + navToFindings(getBenchmarkIdQuery(benchmark), [ + FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + ]); - const getBenchmarkInfo = ( - benchmarkId: string, - cloudAssetCount: number - ): { name: string; assetType: string } => { - const benchmarks: Record = { + const getBenchmarkInfo = (benchmarkId: string, cloudAssetCount: number): BenchmarkInfo => { + const benchmarks: Record = { cis_gcp: { name: i18n.translate( 'xpack.csp.dashboard.benchmarkSection.benchmarkName.cisGcpBenchmarkName', @@ -48,6 +57,7 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) values: { count: cloudAssetCount }, } ), + handleClick: handleClickCloudProvider, }, cis_aws: { name: i18n.translate( @@ -63,6 +73,7 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) values: { count: cloudAssetCount }, } ), + handleClick: handleClickCloudProvider, }, cis_azure: { name: i18n.translate( @@ -78,6 +89,7 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) values: { count: cloudAssetCount }, } ), + handleClick: handleClickCloudProvider, }, cis_k8s: { name: i18n.translate( @@ -93,6 +105,7 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) values: { count: cloudAssetCount }, } ), + handleClick: handleClickCluster, }, cis_eks: { name: i18n.translate( @@ -108,6 +121,7 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) values: { count: cloudAssetCount }, } ), + handleClick: handleClickCluster, }, }; return benchmarks[benchmarkId]; @@ -149,14 +163,14 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData }) } > - +
{benchmarkInfo.name}
- + {benchmarkInfo.assetType} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts index 3d8200a144bd5..1292da8601357 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { GroupOption } from '@kbn/securitysolution-grouping'; +import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; import { FindingsBaseURLQuery } from '../../../common/types'; import { CloudSecurityDefaultColumn } from '../../../components/cloud_security_data_table'; @@ -16,13 +17,6 @@ export const FINDINGS_UNIT = (totalCount: number) => defaultMessage: `{totalCount, plural, =1 {finding} other {findings}}`, }); -export const GROUPING_OPTIONS = { - RESOURCE_NAME: 'resource.name', - RULE_NAME: 'rule.name', - CLOUD_ACCOUNT_NAME: 'cloud.account.name', - ORCHESTRATOR_CLUSTER_NAME: 'orchestrator.cluster.name', -}; - export const NULL_GROUPING_UNIT = i18n.translate('xpack.csp.findings.grouping.nullGroupUnit', { defaultMessage: 'findings', }); @@ -51,25 +45,25 @@ export const defaultGroupingOptions: GroupOption[] = [ label: i18n.translate('xpack.csp.findings.latestFindings.groupByResource', { defaultMessage: 'Resource', }), - key: GROUPING_OPTIONS.RESOURCE_NAME, + key: FINDINGS_GROUPING_OPTIONS.RESOURCE_NAME, }, { label: i18n.translate('xpack.csp.findings.latestFindings.groupByRuleName', { defaultMessage: 'Rule name', }), - key: GROUPING_OPTIONS.RULE_NAME, + key: FINDINGS_GROUPING_OPTIONS.RULE_NAME, }, { label: i18n.translate('xpack.csp.findings.latestFindings.groupByCloudAccount', { defaultMessage: 'Cloud account', }), - key: GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME, + key: FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME, }, { label: i18n.translate('xpack.csp.findings.latestFindings.groupByKubernetesCluster', { defaultMessage: 'Kubernetes cluster', }), - key: GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + key: FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, }, ]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index fe8536eaf0f69..6dbc78cbf0857 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -17,6 +17,7 @@ import { css } from '@emotion/react'; import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src'; import React from 'react'; import { i18n } from '@kbn/i18n'; +import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; import { NullGroup, LoadingGroup, @@ -26,7 +27,7 @@ import { getAbbreviatedNumber } from '../../../common/utils/get_abbreviated_numb import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; import { ComplianceScoreBar } from '../../../components/compliance_score_bar'; import { FindingsGroupingAggregation } from './use_grouped_findings'; -import { GROUPING_OPTIONS, NULL_GROUPING_MESSAGES, NULL_GROUPING_UNIT } from './constants'; +import { NULL_GROUPING_MESSAGES, NULL_GROUPING_UNIT } from './constants'; import { FINDINGS_GROUPING_COUNTER } from '../test_subjects'; export const groupPanelRenderer: GroupPanelRenderer = ( @@ -45,7 +46,7 @@ export const groupPanelRenderer: GroupPanelRenderer ); switch (selectedGroup) { - case GROUPING_OPTIONS.RESOURCE_NAME: + case FINDINGS_GROUPING_OPTIONS.RESOURCE_NAME: return nullGroupMessage ? ( renderNullGroup(NULL_GROUPING_MESSAGES.RESOURCE_NAME) ) : ( @@ -78,7 +79,7 @@ export const groupPanelRenderer: GroupPanelRenderer ); - case GROUPING_OPTIONS.RULE_NAME: + case FINDINGS_GROUPING_OPTIONS.RULE_NAME: return nullGroupMessage ? ( renderNullGroup(NULL_GROUPING_MESSAGES.RULE_NAME) ) : ( @@ -100,7 +101,7 @@ export const groupPanelRenderer: GroupPanelRenderer ); - case GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: + case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: return nullGroupMessage ? ( renderNullGroup(NULL_GROUPING_MESSAGES.CLOUD_ACCOUNT_NAME) ) : ( @@ -129,7 +130,7 @@ export const groupPanelRenderer: GroupPanelRenderer ); - case GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: + case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: return nullGroupMessage ? ( renderNullGroup(NULL_GROUPING_MESSAGES.ORCHESTRATOR_CLUSTER_NAME) ) : ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx index 74efd68b5378b..81df07731c5dd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -15,7 +15,10 @@ import { } from '@kbn/securitysolution-grouping/src'; import { useMemo } from 'react'; import { buildEsQuery, Filter } from '@kbn/es-query'; -import { LOCAL_STORAGE_FINDINGS_GROUPING_KEY } from '../../../common/constants'; +import { + FINDINGS_GROUPING_OPTIONS, + LOCAL_STORAGE_FINDINGS_GROUPING_KEY, +} from '../../../common/constants'; import { useDataViewContext } from '../../../common/contexts/data_view_context'; import { Evaluation } from '../../../../common/types_old'; import { LATEST_FINDINGS_RETENTION_POLICY } from '../../../../common/constants'; @@ -24,13 +27,7 @@ import { FindingsRootGroupingAggregation, useGroupedFindings, } from './use_grouped_findings'; -import { - FINDINGS_UNIT, - groupingTitle, - defaultGroupingOptions, - getDefaultQuery, - GROUPING_OPTIONS, -} from './constants'; +import { FINDINGS_UNIT, groupingTitle, defaultGroupingOptions, getDefaultQuery } from './constants'; import { useCloudSecurityGrouping } from '../../../components/cloud_security_grouping'; import { getFilters } from '../utils/get_filters'; import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api'; @@ -80,26 +77,26 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { ]; switch (field) { - case GROUPING_OPTIONS.RESOURCE_NAME: + case FINDINGS_GROUPING_OPTIONS.RESOURCE_NAME: return [ ...aggMetrics, getTermAggregation('resourceName', 'resource.id'), getTermAggregation('resourceSubType', 'resource.sub_type'), getTermAggregation('resourceType', 'resource.type'), ]; - case GROUPING_OPTIONS.RULE_NAME: + case FINDINGS_GROUPING_OPTIONS.RULE_NAME: return [ ...aggMetrics, getTermAggregation('benchmarkName', 'rule.benchmark.name'), getTermAggregation('benchmarkVersion', 'rule.benchmark.version'), ]; - case GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: + case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: return [ ...aggMetrics, getTermAggregation('benchmarkName', 'rule.benchmark.name'), getTermAggregation('benchmarkId', 'rule.benchmark.id'), ]; - case GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: + case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: return [ ...aggMetrics, getTermAggregation('benchmarkName', 'rule.benchmark.name'),