Skip to content

Commit

Permalink
[Cloud Security] Azure support for dashboard and flyout and PLG for d…
Browse files Browse the repository at this point in the history
…ashboard (elastic#167422)
  • Loading branch information
JordanSh authored Sep 28, 2023
1 parent ba31c76 commit df9fd9d
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 127 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) || [];
};
Expand All @@ -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 && (
<EuiFlexItem
key={benchmark.type}
onClick={() => {
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};
}
`}
>
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem>
<CISBenchmarkIcon type={benchmark.type} name={benchmark.name} size={'l'} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<CompactFormattedNumber
number={clusterAmount}
abbreviateAbove={benchmarkAbbreviateAbove}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
)
);
});

return (
<>
<EuiFlexGroup gutterSize="m">
{cisAwsClusterAmount > 0 && (
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem>
<CISBenchmarkIcon type={CIS_AWS} name={cisAwsBenchmarkName} size={'l'} />
</EuiFlexItem>
<EuiFlexItem
grow={false}
onClick={() => {
navToFindingsByCloudProvider('aws');
}}
>
<CompactFormattedNumber
number={cisAwsClusterAmount}
abbreviateAbove={benchmarkAbbreviateAbove}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
)}
{cisGcpClusterAmount > 0 && (
<EuiFlexItem>
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiFlexItem>
<CISBenchmarkIcon type={CIS_GCP} name={cisGcpBenchmarkName} size={'l'} />
</EuiFlexItem>
<EuiFlexItem
grow={false}
onClick={() => {
navToFindingsByCloudProvider('gcp');
}}
>
<CompactFormattedNumber
number={cisGcpClusterAmount}
abbreviateAbove={benchmarkAbbreviateAbove}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
)}
</EuiFlexGroup>
</>
);
// Render the benchmark elements
return <EuiFlexGroup gutterSize="m">{benchmarkElements}</EuiFlexGroup>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLButtonElement>;
button?: ReactNode;
title: EuiStatProps['title'];
titleColor?: EuiStatProps['titleColor'];
description: EuiStatProps['description'];
Expand All @@ -22,25 +21,10 @@ export const CspCounterCard = (counter: CspCounterCardProps) => {
const { euiTheme } = useEuiTheme();

return (
<EuiPanel
hasBorder
onClick={counter.onClick}
paddingSize="m"
css={css`
position: relative;
display: flex;
align-items: center;
:hover .euiIcon {
color: ${euiTheme.colors.primary};
transition: ${euiTheme.animation.normal};
}
`}
data-test-subj={counter.id}
>
<EuiPanel hasBorder paddingSize="m" data-test-subj={counter.id}>
<EuiStat
css={{
height: '100%',
height: '60%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-around',
Expand All @@ -55,17 +39,8 @@ export const CspCounterCard = (counter: CspCounterCardProps) => {
descriptionElement="h6"
description={counter.description}
/>
{counter.onClick && (
<EuiIcon
type={'pivot'}
css={css`
color: ${euiTheme.colors.lightShade};
position: absolute;
top: ${euiTheme.size.s};
right: ${euiTheme.size.s};
`}
/>
)}
<EuiHorizontalRule margin="xs" />
{counter.button}
</EuiPanel>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ describe('<CloudSummarySection />', () => {
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],
});
});

Expand All @@ -46,7 +42,6 @@ describe('<CloudSummarySection />', () => {
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', () => {
Expand All @@ -55,12 +50,5 @@ describe('<CloudSummarySection />', () => {
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');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) });
Expand Down Expand Up @@ -87,12 +89,26 @@ export const SummarySection = ({
'xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription',
{ defaultMessage: 'Accounts Evaluated' }
),
title:
dashboardType === KSPM_POLICY_TEMPLATE ? (
<CompactFormattedNumber number={complianceData.clusters.length} />
) : (
<AccountsEvaluatedWidget clusters={complianceData.clusters} />
),
title: <AccountsEvaluatedWidget clusters={complianceData.clusters} />,
button: (
<EuiButtonEmpty
iconType="listAdd"
target="_blank"
href={
dashboardType === KSPM_POLICY_TEMPLATE ? kspmIntegrationLink : cspmIntegrationLink
}
>
{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' }
)}
</EuiButtonEmpty>
),
},
{
id: DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED,
Expand All @@ -101,32 +117,27 @@ export const SummarySection = ({
{ defaultMessage: 'Resources Evaluated' }
),
title: <CompactFormattedNumber number={complianceData.stats.resourcesEvaluated || 0} />,
onClick: () => {
navToFindingsByResource(getPolicyTemplateQuery(dashboardType));
},
},
{
id: DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS,
description: i18n.translate(
'xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription',
{ defaultMessage: 'Failing Findings' }
button: (
<EuiButtonEmpty
iconType="search"
onClick={() => {
navToFindingsByResource(getPolicyTemplateQuery(dashboardType));
}}
>
{i18n.translate(
'xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedButtonTitle',
{ defaultMessage: 'View all resources' }
)}
</EuiButtonEmpty>
),
title: <CompactFormattedNumber number={complianceData.stats.totalFailed} />,
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,
]
);
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit df9fd9d

Please sign in to comment.