From df9fd9d0304a6d7c44c6b8542fb9943e29a3d0d2 Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:33:24 +0300 Subject: [PATCH] [Cloud Security] Azure support for dashboard and flyout and PLG for dashboard (#167422) --- .../common/constants.ts | 3 + .../components/accounts_evaluated_widget.tsx | 132 +++++++++++------- .../public/components/cis_benchmark_icon.tsx | 2 + .../public/components/csp_counter_card.tsx | 39 +----- .../summary_section.test.tsx | 14 +- .../dashboard_sections/summary_section.tsx | 65 +++++---- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 9 files changed, 131 insertions(+), 127 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 23de4b6d02e36..1cc356cbfd5e3 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -100,6 +100,9 @@ export const CLOUDBEAT_VULN_MGMT_GCP = 'cloudbeat/vuln_mgmt_gcp'; export const CLOUDBEAT_VULN_MGMT_AZURE = 'cloudbeat/vuln_mgmt_azure'; export const CIS_AWS = 'cis_aws'; export const CIS_GCP = 'cis_gcp'; +export const CIS_K8S = 'cis_k8s'; +export const CIS_EKS = 'cis_eks'; +export const CIS_AZURE = 'cis_azure'; export const KSPM_POLICY_TEMPLATE = 'kspm'; export const CSPM_POLICY_TEMPLATE = 'cspm'; export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt'; 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 39b37c85aee39..418b1c37a1bdd 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 @@ -5,13 +5,43 @@ * 2.0. */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { CIS_AWS, CIS_GCP } from '../../common/constants'; +import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { CIS_AWS, CIS_GCP, CIS_AZURE, CIS_K8S, CIS_EKS } from '../../common/constants'; import { Cluster } from '../../common/types'; import { CISBenchmarkIcon } from './cis_benchmark_icon'; import { CompactFormattedNumber } from './compact_formatted_number'; import { useNavigateFindings } from '../common/hooks/use_navigate_findings'; +// order in array will determine order of appearance in the dashboard +const benchmarks = [ + { + type: CIS_AWS, + name: 'Amazon Web Services (AWS)', + provider: 'aws', + }, + { + type: CIS_GCP, + name: 'Google Cloud Platform (GCP)', + provider: 'gcp', + }, + { + type: CIS_AZURE, + name: 'Azure', + provider: 'azure', + }, + { + type: CIS_K8S, + name: 'Kubernetes', + benchmarkId: 'cis_k8s', + }, + { + type: CIS_EKS, + name: 'Amazon Elastic Kubernetes Service (EKS)', + benchmarkId: 'cis_eks', + }, +]; + export const AccountsEvaluatedWidget = ({ clusters, benchmarkAbbreviateAbove = 999, @@ -20,6 +50,8 @@ export const AccountsEvaluatedWidget = ({ /** numbers higher than the value of this field will be abbreviated using compact notation and have a tooltip displaying the full value */ benchmarkAbbreviateAbove?: number; }) => { + const { euiTheme } = useEuiTheme(); + const filterClustersById = (benchmarkId: string) => { return clusters?.filter((obj) => obj?.meta.benchmark.id === benchmarkId) || []; }; @@ -30,56 +62,52 @@ export const AccountsEvaluatedWidget = ({ navToFindings({ 'cloud.provider': provider }); }; - const cisAwsClusterAmount = filterClustersById(CIS_AWS).length; - const cisGcpClusterAmount = filterClustersById(CIS_GCP).length; + const navToFindingsByCisBenchmark = (cisBenchmark: string) => { + navToFindings({ 'rule.benchmark.id': cisBenchmark }); + }; + + const benchmarkElements = benchmarks.map((benchmark) => { + const clusterAmount = filterClustersById(benchmark.type).length; + + return ( + clusterAmount > 0 && ( + { + if (benchmark.provider) { + navToFindingsByCloudProvider(benchmark.provider); + } + if (benchmark.benchmarkId) { + navToFindingsByCisBenchmark(benchmark.benchmarkId); + } + }} + css={css` + transition: ${euiTheme.animation.normal} ease-in; + border-bottom: ${euiTheme.border.thick}; + border-color: transparent; - const cisAwsBenchmarkName = 'Amazon Web Services (AWS)'; - const cisGcpBenchmarkName = 'Google Cloud Platform (GCP)'; + :hover { + cursor: pointer; + border-color: ${euiTheme.colors.darkestShade}; + } + `} + > + + + + + + + + + + ) + ); + }); - return ( - <> - - {cisAwsClusterAmount > 0 && ( - - - - - - { - navToFindingsByCloudProvider('aws'); - }} - > - - - - - )} - {cisGcpClusterAmount > 0 && ( - - - - - - { - navToFindingsByCloudProvider('gcp'); - }} - > - - - - - )} - - - ); + // Render the benchmark elements + return {benchmarkElements}; }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx index a0048b21fb92f..50f9ca1b15d9d 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx @@ -23,6 +23,8 @@ const getBenchmarkIdIconType = (props: Props): string => { switch (props.type) { case 'cis_eks': return cisEksIcon; + case 'cis_azure': + return 'logoAzure'; case 'cis_aws': return 'logoAWS'; case 'cis_gcp': diff --git a/x-pack/plugins/cloud_security_posture/public/components/csp_counter_card.tsx b/x-pack/plugins/cloud_security_posture/public/components/csp_counter_card.tsx index def19c1c871ec..9b07c0f3edded 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/csp_counter_card.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/csp_counter_card.tsx @@ -5,14 +5,13 @@ * 2.0. */ -import React, { MouseEventHandler } from 'react'; -import { css } from '@emotion/react'; -import { EuiIcon, EuiPanel, EuiStat, useEuiTheme } from '@elastic/eui'; +import React, { ReactNode } from 'react'; +import { EuiPanel, EuiStat, useEuiTheme, EuiHorizontalRule } from '@elastic/eui'; import type { EuiStatProps } from '@elastic/eui'; export interface CspCounterCardProps { id: string; - onClick?: MouseEventHandler; + button?: ReactNode; title: EuiStatProps['title']; titleColor?: EuiStatProps['titleColor']; description: EuiStatProps['description']; @@ -22,25 +21,10 @@ export const CspCounterCard = (counter: CspCounterCardProps) => { const { euiTheme } = useEuiTheme(); return ( - + { descriptionElement="h6" description={counter.description} /> - {counter.onClick && ( - - )} + + {counter.button} ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.test.tsx index f78b596074349..0545d4f3bb429 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.test.tsx @@ -31,11 +31,7 @@ describe('', () => { renderCloudSummarySection(); expectIdsInDoc({ - be: [ - DASHBOARD_COUNTER_CARDS.CLUSTERS_EVALUATED, - DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED, - DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS, - ], + be: [DASHBOARD_COUNTER_CARDS.CLUSTERS_EVALUATED, DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED], }); }); @@ -46,7 +42,6 @@ describe('', () => { expect(screen.getByTestId(DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED)).toHaveTextContent( '162' ); - expect(screen.getByTestId(DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS)).toHaveTextContent('17'); }); it('renders counters value in compact abbreviation if its above one million', () => { @@ -55,12 +50,5 @@ describe('', () => { expect(screen.getByTestId(DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED)).toHaveTextContent( '999,999' ); - expect(screen.getByTestId(DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS)).toHaveTextContent('1M'); - }); - - it('renders N/A as an empty state', () => { - renderCloudSummarySection({ stats: { totalFailed: undefined } }); - - expect(screen.getByTestId(DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS)).toHaveTextContent('N/A'); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx index 9837c531021f2..50d5493387466 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx @@ -6,10 +6,10 @@ */ import React, { useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFlexItemProps } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlexItemProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; -import { statusColors } from '../../../common/constants'; +import { useCspIntegrationLink } from '../../../common/navigation/use_csp_integration_link'; import { DASHBOARD_COUNTER_CARDS, DASHBOARD_SUMMARY_CONTAINER } from '../test_subjects'; import { CspCounterCard, CspCounterCardProps } from '../../../components/csp_counter_card'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; @@ -56,6 +56,8 @@ export const SummarySection = ({ }) => { const navToFindings = useNavigateFindings(); const navToFindingsByResource = useNavigateFindingsByResource(); + const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE); + const kspmIntegrationLink = useCspIntegrationLink(KSPM_POLICY_TEMPLATE); const handleEvalCounterClick = (evaluation: Evaluation) => { navToFindings({ 'result.evaluation': evaluation, ...getPolicyTemplateQuery(dashboardType) }); @@ -87,12 +89,26 @@ export const SummarySection = ({ 'xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription', { defaultMessage: 'Accounts Evaluated' } ), - title: - dashboardType === KSPM_POLICY_TEMPLATE ? ( - - ) : ( - - ), + title: , + button: ( + + {dashboardType === KSPM_POLICY_TEMPLATE + ? i18n.translate( + 'xpack.csp.dashboard.summarySection.counterCard.clustersEvaluatedButtonTitle', + { defaultMessage: 'Enroll more clusters' } + ) + : i18n.translate( + 'xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedButtonTitle', + { defaultMessage: 'Enroll more accounts' } + )} + + ), }, { id: DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED, @@ -101,32 +117,27 @@ export const SummarySection = ({ { defaultMessage: 'Resources Evaluated' } ), title: , - onClick: () => { - navToFindingsByResource(getPolicyTemplateQuery(dashboardType)); - }, - }, - { - id: DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS, - description: i18n.translate( - 'xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription', - { defaultMessage: 'Failing Findings' } + button: ( + { + navToFindingsByResource(getPolicyTemplateQuery(dashboardType)); + }} + > + {i18n.translate( + 'xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedButtonTitle', + { defaultMessage: 'View all resources' } + )} + ), - title: , - titleColor: complianceData.stats.totalFailed > 0 ? statusColors.failed : 'text', - onClick: () => { - navToFindings({ - 'result.evaluation': RULE_FAILED, - ...getPolicyTemplateQuery(dashboardType), - }); - }, }, ], [ complianceData.clusters, complianceData.stats.resourcesEvaluated, - complianceData.stats.totalFailed, + cspmIntegrationLink, dashboardType, - navToFindings, + kspmIntegrationLink, navToFindingsByResource, ] ); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index aa95ebb6e2aef..260328513d6c1 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -11377,7 +11377,6 @@ "xpack.csp.dashboard.summarySection.complianceByCisSectionPanelTitle": "Conformité par section CIS", "xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription": "Comptes évalués", "xpack.csp.dashboard.summarySection.counterCard.clustersEvaluatedDescription": "Clusters évalués", - "xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription": "Résultats en échec", "xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedDescription": "Ressources évaluées", "xpack.csp.dashboardTabs.cloudTab.tabTitle": "Cloud", "xpack.csp.dashboardTabs.kubernetesTab.tabTitle": "Kubernetes", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0c085d75d7902..db4e8cc674281 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11392,7 +11392,6 @@ "xpack.csp.dashboard.summarySection.complianceByCisSectionPanelTitle": "CISセクション別のコンプライアンス", "xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription": "評価されたアカウント", "xpack.csp.dashboard.summarySection.counterCard.clustersEvaluatedDescription": "評価されたクラスター", - "xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription": "失敗した調査結果", "xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedDescription": "評価されたリソース", "xpack.csp.dashboardTabs.cloudTab.tabTitle": "クラウド", "xpack.csp.dashboardTabs.kubernetesTab.tabTitle": "Kubernetes", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4cc2eb901cbbd..68e8ae1196d60 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11392,7 +11392,6 @@ "xpack.csp.dashboard.summarySection.complianceByCisSectionPanelTitle": "合规性(按 CIS 部分)", "xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription": "已评估帐户", "xpack.csp.dashboard.summarySection.counterCard.clustersEvaluatedDescription": "集群已评估", - "xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription": "失败的结果", "xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedDescription": "资源已评估", "xpack.csp.dashboardTabs.cloudTab.tabTitle": "云", "xpack.csp.dashboardTabs.kubernetesTab.tabTitle": "Kubernetes",