diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.test.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.test.ts
new file mode 100644
index 000000000000..dffef3338072
--- /dev/null
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.test.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react';
+import { useActiveNamespace } from './use_active_namespace';
+
+import { LOCAL_STORAGE_NAMESPACE_KEY } from '../constants';
+
+describe('useActiveNamespace', () => {
+ it('should return the active namespace from local storage for CSPM', () => {
+ const { result } = renderHook(() => useActiveNamespace({ postureType: 'cspm' }));
+ expect(result.current).toEqual({
+ activeNamespace: 'default',
+ updateActiveNamespace: expect.any(Function),
+ });
+ });
+
+ it('should update the active namespace and local storage when updateActiveNamespace is called with CSPM', async () => {
+ const postureType = 'cspm';
+ const CSPM_NAMESPACE_LOCAL_STORAGE_KEY = `${LOCAL_STORAGE_NAMESPACE_KEY}:${postureType}`;
+ const { result } = renderHook(() => useActiveNamespace({ postureType }));
+ const newNamespace = 'test-namespace';
+
+ act(() => {
+ result.current.updateActiveNamespace(newNamespace);
+ });
+
+ expect(result.current.activeNamespace).toBe(newNamespace);
+ expect(localStorage.getItem(CSPM_NAMESPACE_LOCAL_STORAGE_KEY)).toBe(
+ JSON.stringify(newNamespace)
+ );
+ });
+
+ it('should return the active namespace from local storage for KSPM', () => {
+ const { result } = renderHook(() => useActiveNamespace({ postureType: 'kspm' }));
+ expect(result.current).toEqual({
+ activeNamespace: 'default',
+ updateActiveNamespace: expect.any(Function),
+ });
+ });
+ it('should update the active namespace and local storage when updateActiveNamespace is called with KSPM', async () => {
+ const postureType = 'kspm';
+ const KSPM_NAMESPACE_LOCAL_STORAGE_KEY = `${LOCAL_STORAGE_NAMESPACE_KEY}:${postureType}`;
+ const { result } = renderHook(() => useActiveNamespace({ postureType }));
+ const newNamespace = 'test-namespace';
+
+ act(() => {
+ result.current.updateActiveNamespace(newNamespace);
+ });
+
+ expect(result.current.activeNamespace).toBe(newNamespace);
+ expect(localStorage.getItem(KSPM_NAMESPACE_LOCAL_STORAGE_KEY)).toBe(
+ JSON.stringify(newNamespace)
+ );
+ });
+});
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.ts
index f4b1e6d41f2f..ba898093b865 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.ts
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/hooks/use_active_namespace.ts
@@ -9,7 +9,7 @@ import { useState, useCallback } from 'react';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import { LOCAL_STORAGE_NAMESPACE_KEY, DEFAULT_NAMESPACE } from '../constants';
-export const useActiveNamespace = ({ postureType }: { postureType?: 'cspm' | 'kspm' }) => {
+export const useActiveNamespace = ({ postureType }: { postureType: 'cspm' | 'kspm' }) => {
const [localStorageActiveNamespace, localStorageSetActiveNamespace] = useLocalStorage(
`${LOCAL_STORAGE_NAMESPACE_KEY}:${postureType}`,
DEFAULT_NAMESPACE
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx
index 470a04e40248..3812bb1b88d1 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx
@@ -51,6 +51,29 @@ describe('AccountsEvaluatedWidget', () => {
);
});
+ it('calls navToFindingsByCloudProvider when a benchmark with provider and namespace is clicked', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ fireEvent.click(getByText('10'));
+
+ expect(mockNavToFindings).toHaveBeenCalledWith(
+ {
+ 'data_stream.namespace': 'test-namespace',
+ 'cloud.provider': 'aws',
+ 'rule.benchmark.posture_type': 'cspm',
+ },
+ ['cloud.account.id']
+ );
+ });
+
it('calls navToFindingsByCisBenchmark when a benchmark with benchmarkId is clicked', () => {
const { getByText } = render(
@@ -67,4 +90,26 @@ describe('AccountsEvaluatedWidget', () => {
['orchestrator.cluster.id']
);
});
+
+ it('calls navToFindingsByCisBenchmark when a benchmark with benchmarkId and namespace is clicked', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ fireEvent.click(getByText('20'));
+
+ expect(mockNavToFindings).toHaveBeenCalledWith(
+ {
+ 'rule.benchmark.id': 'cis_k8s',
+ 'data_stream.namespace': 'test-namespace',
+ },
+ ['orchestrator.cluster.id']
+ );
+ });
});
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx
index 80716efd9869..4b8208ed4b0d 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx
@@ -46,9 +46,11 @@ const benchmarks = [
];
export const AccountsEvaluatedWidget = ({
+ activeNamespace,
benchmarkAssets,
benchmarkAbbreviateAbove = 999,
}: {
+ activeNamespace?: string;
benchmarkAssets: BenchmarkData[];
/** numbers higher than the value of this field will be abbreviated using compact notation and have a tooltip displaying the full value */
benchmarkAbbreviateAbove?: number;
@@ -63,15 +65,27 @@ export const AccountsEvaluatedWidget = ({
const navToFindingsByCloudProvider = (provider: string) => {
navToFindings(
- { 'cloud.provider': provider, 'rule.benchmark.posture_type': CSPM_POLICY_TEMPLATE },
+ activeNamespace
+ ? {
+ [`${FINDINGS_GROUPING_OPTIONS.NAMESPACE}`]: activeNamespace,
+ 'cloud.provider': provider,
+ 'rule.benchmark.posture_type': CSPM_POLICY_TEMPLATE,
+ }
+ : { 'cloud.provider': provider, 'rule.benchmark.posture_type': CSPM_POLICY_TEMPLATE },
[FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID]
);
};
const navToFindingsByCisBenchmark = (cisBenchmark: string) => {
- navToFindings({ 'rule.benchmark.id': cisBenchmark }, [
- FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID,
- ]);
+ navToFindings(
+ activeNamespace
+ ? {
+ [`${FINDINGS_GROUPING_OPTIONS.NAMESPACE}`]: activeNamespace,
+ 'rule.benchmark.id': cisBenchmark,
+ }
+ : { 'rule.benchmark.id': cisBenchmark },
+ [FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID]
+ );
};
const benchmarkElements = benchmarks.map((benchmark) => {
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
index 02a4aaf4f12e..50f9c30c2fab 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
@@ -131,11 +131,13 @@ const IntegrationPostureDashboard = ({
notInstalledConfig,
isIntegrationInstalled,
dashboardType,
+ activeNamespace,
}: {
complianceData: ComplianceDashboardDataV2 | undefined;
notInstalledConfig: CspNoDataPageProps;
isIntegrationInstalled?: boolean;
dashboardType: PosturePolicyTemplate;
+ activeNamespace?: string;
}) => {
const noFindings = !complianceData || complianceData.stats.totalFindings === 0;
@@ -183,9 +185,17 @@ const IntegrationPostureDashboard = ({
// there are findings, displays dashboard even if integration is not installed
return (
<>
-
+
-
+
>
);
@@ -244,7 +254,7 @@ const TabContent = ({
activeNamespace,
}: {
selectedPostureTypeTab: PosturePolicyTemplate;
- activeNamespace: string;
+ activeNamespace?: string;
}) => {
const { data: getSetupStatus } = useCspSetupStatusApi({
refetchInterval: (data) => {
@@ -310,6 +320,7 @@ const TabContent = ({
complianceData={getDashboardData.data}
notInstalledConfig={getNotInstalledConfig(policyTemplate, integrationLink)}
isIntegrationInstalled={setupStatus !== 'not-installed'}
+ activeNamespace={activeNamespace}
/>
@@ -319,6 +330,7 @@ const TabContent = ({
complianceData={getDashboardData.data}
notInstalledConfig={getNotInstalledConfig(policyTemplate, integrationLink)}
isIntegrationInstalled={setupStatus !== 'not-installed'}
+ activeNamespace={activeNamespace}
/>
@@ -353,7 +365,7 @@ export const ComplianceDashboard = () => {
}, [location.pathname]);
const { activeNamespace, updateActiveNamespace } = useActiveNamespace({
- postureType: currentTabUrlState,
+ postureType: currentTabUrlState || POSTURE_TYPE_CSPM,
});
const getCspmDashboardData = useCspmStatsApi(
@@ -423,7 +435,7 @@ export const ComplianceDashboard = () => {
content: (
),
},
@@ -439,7 +451,7 @@ export const ComplianceDashboard = () => {
content: (
),
},
@@ -453,6 +465,7 @@ export const ComplianceDashboard = () => {
history,
services.data.query.queryString,
services.data.query.filterManager,
+ cloudSecurityNamespaceSupportEnabled,
]);
// if there is more than one namespace, show the namespace selector in the header
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.test.tsx
new file mode 100644
index 000000000000..d22a527c691a
--- /dev/null
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.test.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 } from '@testing-library/react';
+import { TestProvider } from '../../../test/test_provider';
+import { BenchmarkDetailsBox } from './benchmark_details_box';
+import { getBenchmarkMockData } from '../mock';
+
+const mockNavToFindings = jest.fn();
+jest.mock('@kbn/cloud-security-posture/src/hooks/use_navigate_findings', () => ({
+ useNavigateFindings: () => mockNavToFindings,
+}));
+
+describe('BenchmarkDetailsBox', () => {
+ const renderBenchmarkDetails = () =>
+ render(
+
+
+
+ );
+
+ it('renders the component correctly', () => {
+ const { getByTestId } = renderBenchmarkDetails();
+ expect(getByTestId('benchmark-asset-type')).toBeInTheDocument();
+ });
+
+ it('calls the navigate function with correct parameters when a benchmark is clicked', () => {
+ const { getByTestId } = renderBenchmarkDetails();
+ const benchmarkLink = getByTestId('benchmark-asset-type');
+ benchmarkLink.click();
+
+ expect(mockNavToFindings).toHaveBeenCalledWith(
+ {
+ 'data_stream.namespace': 'test-namespace',
+ 'rule.benchmark.id': 'cis_aws',
+ 'rule.benchmark.version': '1.2.3',
+ },
+ ['cloud.account.id']
+ );
+ });
+});
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx
index 869b1473b443..203c63d3be2a 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx
@@ -23,23 +23,32 @@ import { getBenchmarkIdQuery } from './benchmarks_section';
import { BenchmarkData } from '../../../../common/types_old';
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 }) => {
+export const BenchmarkDetailsBox = ({
+ benchmark,
+ activeNamespace,
+}: {
+ benchmark: BenchmarkData;
+ activeNamespace?: string;
+}) => {
const navToFindings = useNavigateFindings();
- const handleClickCloudProvider = () =>
- navToFindings(getBenchmarkIdQuery(benchmark), [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID]);
+ const handleClickCloudProvider = () => {
+ navToFindings(getBenchmarkIdQuery(benchmark, activeNamespace), [
+ FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID,
+ ]);
+ };
- const handleClickCluster = () =>
- navToFindings(getBenchmarkIdQuery(benchmark), [
+ const handleClickCluster = () => {
+ navToFindings(getBenchmarkIdQuery(benchmark, activeNamespace), [
FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID,
]);
+ };
const getBenchmarkInfo = (benchmarkId: string, cloudAssetCount: number): BenchmarkInfo => {
const benchmarks: Record = {
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.test.tsx
index 486624caa0c1..ce439ed503c6 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.test.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.test.tsx
@@ -17,13 +17,19 @@ import {
DASHBOARD_TABLE_HEADER_SCORE_TEST_ID,
} from '../test_subjects';
+const mockNavToFindings = jest.fn();
+jest.mock('@kbn/cloud-security-posture/src/hooks/use_navigate_findings', () => ({
+ useNavigateFindings: () => mockNavToFindings,
+}));
+
describe('', () => {
- const renderBenchmarks = (alterMockData = {}) =>
+ const renderBenchmarks = (alterMockData = {}, namespace?: string) =>
render(
);
diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
index f4cc7a5ba002..7254b7b6bdc0 100644
--- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
+++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
@@ -35,19 +35,30 @@ import { ComplianceScoreChart } from '../compliance_charts/compliance_score_char
import { BenchmarkDetailsBox } from './benchmark_details_box';
const BENCHMARK_DEFAULT_SORT_ORDER = 'asc';
-export const getBenchmarkIdQuery = (benchmark: BenchmarkData): NavFilter => {
- return {
- 'rule.benchmark.id': benchmark.meta.benchmarkId,
- 'rule.benchmark.version': benchmark.meta.benchmarkVersion,
- };
+export const getBenchmarkIdQuery = (
+ benchmark: BenchmarkData,
+ activeNamespace?: string
+): NavFilter => {
+ return activeNamespace
+ ? {
+ 'rule.benchmark.id': benchmark.meta.benchmarkId,
+ 'rule.benchmark.version': benchmark.meta.benchmarkVersion,
+ [`${FINDINGS_GROUPING_OPTIONS.NAMESPACE}`]: activeNamespace,
+ }
+ : {
+ 'rule.benchmark.id': benchmark.meta.benchmarkId,
+ 'rule.benchmark.version': benchmark.meta.benchmarkVersion,
+ };
};
export const BenchmarksSection = ({
complianceData,
dashboardType,
+ activeNamespace,
}: {
complianceData: ComplianceDashboardDataV2;
dashboardType: PosturePolicyTemplate;
+ activeNamespace?: string;
}) => {
const { euiTheme } = useEuiTheme();
const navToFindings = useNavigateFindings();
@@ -65,32 +76,30 @@ export const BenchmarksSection = ({
benchmark: BenchmarkData,
evaluation: Evaluation,
groupBy: string[] = [FINDINGS_GROUPING_OPTIONS.NONE]
- ) => {
+ ) =>
navToFindings(
{
- ...getPolicyTemplateQuery(dashboardType),
- ...getBenchmarkIdQuery(benchmark),
+ ...getPolicyTemplateQuery(dashboardType, activeNamespace),
+ ...getBenchmarkIdQuery(benchmark, activeNamespace),
'result.evaluation': evaluation,
},
groupBy
);
- };
const navToFailedFindingsByBenchmarkAndSection = (
benchmark: BenchmarkData,
ruleSection: string,
resultEvaluation: 'passed' | 'failed' = RULE_FAILED
- ) => {
+ ) =>
navToFindings(
{
- ...getPolicyTemplateQuery(dashboardType),
- ...getBenchmarkIdQuery(benchmark),
+ ...getPolicyTemplateQuery(dashboardType, activeNamespace),
+ ...getBenchmarkIdQuery(benchmark, activeNamespace),
'rule.section': ruleSection,
'result.evaluation': resultEvaluation,
},
[FINDINGS_GROUPING_OPTIONS.NONE]
);
- };
const navToFailedFindingsByBenchmark = (benchmark: BenchmarkData) => {
navToFindingsByBenchmarkAndEvaluation(benchmark, RULE_FAILED, [
@@ -175,7 +184,7 @@ export const BenchmarksSection = ({
`}
>
-
+
= {
third: 8,
};
-export const getPolicyTemplateQuery = (policyTemplate: PosturePolicyTemplate): NavFilter => ({
- 'rule.benchmark.posture_type': policyTemplate,
-});
+export const getPolicyTemplateQuery = (
+ policyTemplate: PosturePolicyTemplate,
+ activeNamespace?: string
+): NavFilter =>
+ activeNamespace
+ ? {
+ 'rule.benchmark.posture_type': policyTemplate,
+ [`${FINDINGS_GROUPING_OPTIONS.NAMESPACE}`]: activeNamespace,
+ }
+ : {
+ 'rule.benchmark.posture_type': policyTemplate,
+ };
export const SummarySection = ({
dashboardType,
complianceData,
+ activeNamespace,
}: {
dashboardType: PosturePolicyTemplate;
complianceData: ComplianceDashboardDataV2;
+ activeNamespace?: string;
}) => {
const navToFindings = useNavigateFindings();
const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE);
@@ -58,9 +69,13 @@ export const SummarySection = ({
const { euiTheme } = useEuiTheme();
const handleEvalCounterClick = (evaluation: Evaluation) => {
- navToFindings({ 'result.evaluation': evaluation, ...getPolicyTemplateQuery(dashboardType) }, [
- FINDINGS_GROUPING_OPTIONS.NONE,
- ]);
+ navToFindings(
+ {
+ 'result.evaluation': evaluation,
+ ...getPolicyTemplateQuery(dashboardType, activeNamespace),
+ },
+ [FINDINGS_GROUPING_OPTIONS.NONE]
+ );
};
const handleCellClick = (
@@ -69,7 +84,7 @@ export const SummarySection = ({
) => {
navToFindings(
{
- ...getPolicyTemplateQuery(dashboardType),
+ ...getPolicyTemplateQuery(dashboardType, activeNamespace),
'rule.section': ruleSection,
'result.evaluation': resultEvaluation,
},
@@ -78,9 +93,13 @@ export const SummarySection = ({
};
const handleViewAllClick = () => {
- navToFindings({ 'result.evaluation': RULE_FAILED, ...getPolicyTemplateQuery(dashboardType) }, [
- FINDINGS_GROUPING_OPTIONS.RULE_SECTION,
- ]);
+ navToFindings(
+ {
+ 'result.evaluation': RULE_FAILED,
+ ...getPolicyTemplateQuery(dashboardType, activeNamespace),
+ },
+ [FINDINGS_GROUPING_OPTIONS.RULE_SECTION]
+ );
};
const counters: CspCounterCardProps[] = useMemo(
@@ -97,7 +116,12 @@ export const SummarySection = ({
'xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription',
{ defaultMessage: 'Accounts Evaluated' }
),
- title: ,
+ title: (
+
+ ),
button: (
{
- navToFindings(getPolicyTemplateQuery(dashboardType), [
+ navToFindings(getPolicyTemplateQuery(dashboardType, activeNamespace), [
FINDINGS_GROUPING_OPTIONS.RESOURCE_ID,
]);
}}
@@ -150,6 +174,7 @@ export const SummarySection = ({
dashboardType,
kspmIntegrationLink,
navToFindings,
+ activeNamespace,
]
);
const chartTitle = i18n.translate(