mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Cloud Security] Azure support for dashboard and flyout and PLG for dashboard (#167422)
This commit is contained in:
parent
ba31c76f31
commit
df9fd9d030
9 changed files with 131 additions and 127 deletions
|
@ -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';
|
||||
|
|
|
@ -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 cisAwsBenchmarkName = 'Amazon Web Services (AWS)';
|
||||
const cisGcpBenchmarkName = 'Google Cloud Platform (GCP)';
|
||||
const benchmarkElements = benchmarks.map((benchmark) => {
|
||||
const clusterAmount = filterClustersById(benchmark.type).length;
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
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;
|
||||
|
||||
: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>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Render the benchmark elements
|
||||
return <EuiFlexGroup gutterSize="m">{benchmarkElements}</EuiFlexGroup>;
|
||||
};
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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'];
|
||||
|
@ -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',
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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', () => {
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 ? (
|
||||
<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,
|
||||
|
@ -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,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue