mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Cloud Posture] Dashboard Redesign trend graph (#144814)
This commit is contained in:
parent
00b5e88ef3
commit
34d8a68d10
18 changed files with 217 additions and 475 deletions
|
@ -40,7 +40,6 @@ export const INTERNAL_FEATURE_FLAGS = {
|
|||
showManageRulesMock: false,
|
||||
showFindingFlyoutEvidence: false,
|
||||
showFindingsGroupBy: true,
|
||||
showNewDashboard: false,
|
||||
} as const;
|
||||
|
||||
export const CSP_RULE_SAVED_OBJECT_TYPE = 'csp_rule';
|
||||
|
|
|
@ -21,8 +21,6 @@ export const useCisKubernetesIntegration = () => {
|
|||
const { http } = useKibana().services;
|
||||
|
||||
return useQuery<GetInfoResponse, DefaultPackagesInstallationError>(['integrations'], () =>
|
||||
http.get<GetInfoResponse>(epmRouteService.getInfoPath(CLOUD_SECURITY_POSTURE_PACKAGE_NAME), {
|
||||
query: { experimental: true },
|
||||
})
|
||||
http.get<GetInfoResponse>(epmRouteService.getInfoPath(CLOUD_SECURITY_POSTURE_PACKAGE_NAME))
|
||||
);
|
||||
};
|
||||
|
|
|
@ -60,7 +60,7 @@ export const ChartPanel: React.FC<ChartPanelProps> = ({
|
|||
|
||||
return (
|
||||
<EuiPanel hasBorder={hasBorder} hasShadow={false} data-test-subj="chart-panel">
|
||||
<EuiFlexGroup direction="column" gutterSize="none" style={{ height: '100%' }}>
|
||||
<EuiFlexGroup direction="column" gutterSize="m" style={{ height: '100%' }}>
|
||||
<EuiFlexItem grow={false}>
|
||||
{title && (
|
||||
<EuiTitle size="xs">
|
||||
|
|
|
@ -30,6 +30,6 @@ const getBenchmarkIdIconType = (props: Props): string => {
|
|||
|
||||
export const CISBenchmarkIcon = (props: Props) => (
|
||||
<EuiToolTip content={props.name}>
|
||||
<EuiIcon type={getBenchmarkIdIconType(props)} size="xxl" css={props.style} />
|
||||
<EuiIcon type={getBenchmarkIdIconType(props)} size="xl" css={props.style} />
|
||||
</EuiToolTip>
|
||||
);
|
||||
|
|
|
@ -5,32 +5,31 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import { EuiCard, EuiIcon, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui';
|
||||
import type { EuiTextProps, EuiCardProps } from '@elastic/eui';
|
||||
import { EuiIcon, EuiPanel, EuiStat, useEuiTheme } from '@elastic/eui';
|
||||
import type { EuiStatProps } from '@elastic/eui';
|
||||
|
||||
export type CspCounterCardProps = Pick<EuiCardProps, 'onClick' | 'id' | 'title' | 'description'> & {
|
||||
descriptionColor?: EuiTextProps['color'];
|
||||
};
|
||||
export interface CspCounterCardProps {
|
||||
id: string;
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
title: EuiStatProps['title'];
|
||||
titleColor?: EuiStatProps['titleColor'];
|
||||
description: EuiStatProps['description'];
|
||||
}
|
||||
|
||||
export const CspCounterCard = (counter: CspCounterCardProps) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<EuiCard
|
||||
title={
|
||||
<EuiTitle size="xxxs">
|
||||
<h6>{counter.title}</h6>
|
||||
</EuiTitle>
|
||||
}
|
||||
<EuiPanel
|
||||
hasBorder
|
||||
onClick={counter.onClick}
|
||||
paddingSize="m"
|
||||
textAlign="left"
|
||||
layout="vertical"
|
||||
css={css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:hover .euiIcon {
|
||||
color: ${euiTheme.colors.primary};
|
||||
|
@ -39,21 +38,29 @@ export const CspCounterCard = (counter: CspCounterCardProps) => {
|
|||
`}
|
||||
data-test-subj={counter.id}
|
||||
>
|
||||
<EuiText color={counter.descriptionColor}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>{counter.description}</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
<EuiStat
|
||||
css={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-around',
|
||||
}}
|
||||
titleSize="s"
|
||||
title={counter.title}
|
||||
titleColor={counter.titleColor}
|
||||
descriptionElement="h6"
|
||||
description={counter.description}
|
||||
/>
|
||||
{counter.onClick && (
|
||||
<EuiIcon
|
||||
type="link"
|
||||
css={css`
|
||||
position: absolute;
|
||||
top: ${euiTheme.size.m};
|
||||
right: ${euiTheme.size.m};
|
||||
top: ${euiTheme.size.s};
|
||||
right: ${euiTheme.size.s};
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
</EuiCard>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
export const CasesTable = () => {
|
||||
return (
|
||||
<EuiFlexGroup direction="column" justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="visualizeApp" size="xl" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
<FormattedMessage
|
||||
id="xpack.csp.dashboard.casesTable.placeholderTitle"
|
||||
defaultMessage="Coming soon"
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -10,100 +10,51 @@ import {
|
|||
AreaSeries,
|
||||
Axis,
|
||||
Chart,
|
||||
ElementClickListener,
|
||||
niceTimeFormatByDay,
|
||||
Partition,
|
||||
PartitionElementEvent,
|
||||
PartitionLayout,
|
||||
Settings,
|
||||
timeFormatter,
|
||||
} from '@elastic/charts';
|
||||
import { EuiFlexGroup, EuiText, EuiHorizontalRule, EuiFlexItem } from '@elastic/eui';
|
||||
import {
|
||||
useEuiTheme,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
type EuiLinkButtonProps,
|
||||
type EuiTextProps,
|
||||
EuiToolTip,
|
||||
EuiToolTipProps,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedDate, FormattedTime } from '@kbn/i18n-react';
|
||||
import moment from 'moment';
|
||||
import { statusColors } from '../../../common/constants';
|
||||
import type { PostureTrend, Stats } from '../../../../common/types';
|
||||
import { CompactFormattedNumber } from '../../../components/compact_formatted_number';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { RULE_FAILED, RULE_PASSED } from '../../../../common/constants';
|
||||
import { CompactFormattedNumber } from '../../../components/compact_formatted_number';
|
||||
import type { Evaluation, PostureTrend, Stats } from '../../../../common/types';
|
||||
import { useKibana } from '../../../common/hooks/use_kibana';
|
||||
|
||||
interface CloudPostureScoreChartProps {
|
||||
compact?: boolean;
|
||||
trend: PostureTrend[];
|
||||
data: Stats;
|
||||
id: string;
|
||||
partitionOnElementClick: (elements: PartitionElementEvent[]) => void;
|
||||
onEvalCounterClick: (evaluation: Evaluation) => void;
|
||||
}
|
||||
|
||||
const getPostureScorePercentage = (postureScore: number): string => `${Math.round(postureScore)}%`;
|
||||
|
||||
const ScoreChart = ({
|
||||
data: { totalPassed, totalFailed },
|
||||
id,
|
||||
partitionOnElementClick,
|
||||
}: Omit<CloudPostureScoreChartProps, 'trend'>) => {
|
||||
const data = [
|
||||
{ label: RULE_PASSED, value: totalPassed },
|
||||
{ label: RULE_FAILED, value: totalFailed },
|
||||
];
|
||||
const {
|
||||
services: { charts },
|
||||
} = useKibana();
|
||||
|
||||
return (
|
||||
<Chart size={{ height: 90, width: 90 }}>
|
||||
<Settings
|
||||
theme={[
|
||||
// theme overrides
|
||||
{
|
||||
partition: {
|
||||
linkLabel: { maximumSection: Infinity, maxCount: 0 },
|
||||
outerSizeRatio: 0.75,
|
||||
emptySizeRatio: 0.7,
|
||||
},
|
||||
},
|
||||
// theme
|
||||
charts.theme.useChartsTheme(),
|
||||
]}
|
||||
baseTheme={charts.theme.useChartsBaseTheme()}
|
||||
onElementClick={partitionOnElementClick as ElementClickListener}
|
||||
/>
|
||||
<Partition
|
||||
id={id}
|
||||
data={data}
|
||||
valueGetter="percent"
|
||||
valueAccessor={(d) => d.value}
|
||||
layout={PartitionLayout.sunburst}
|
||||
layers={[
|
||||
{
|
||||
groupByRollup: (d: { label: string }) => d.label,
|
||||
shape: {
|
||||
fillColor: (d, index) =>
|
||||
d.dataName === RULE_PASSED ? statusColors.success : statusColors.danger,
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Chart>
|
||||
);
|
||||
};
|
||||
|
||||
const PercentageInfo = ({
|
||||
compact,
|
||||
postureScore,
|
||||
totalPassed,
|
||||
totalFindings,
|
||||
}: CloudPostureScoreChartProps['data']) => {
|
||||
}: CloudPostureScoreChartProps['data'] & { compact?: CloudPostureScoreChartProps['compact'] }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const percentage = getPostureScorePercentage(postureScore);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" justifyContent="center">
|
||||
<EuiText style={{ fontSize: 40, fontWeight: 'bold', lineHeight: 1 }}>{percentage}</EuiText>
|
||||
<EuiText size="xs">
|
||||
<CompactFormattedNumber number={totalPassed} />
|
||||
{'/'}
|
||||
<CompactFormattedNumber number={totalFindings} />
|
||||
{' Findings passed'}
|
||||
</EuiText>
|
||||
</EuiFlexGroup>
|
||||
<EuiTitle css={{ fontSize: compact ? euiTheme.size.l : euiTheme.size.xxl }}>
|
||||
<h3>{percentage}</h3>
|
||||
</EuiTitle>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -149,38 +100,94 @@ const ComplianceTrendChart = ({ trend }: { trend: PostureTrend[] }) => {
|
|||
tickFormat={timeFormatter(niceTimeFormatByDay(2))}
|
||||
ticks={4}
|
||||
/>
|
||||
<Axis
|
||||
ticks={3}
|
||||
id="left-axis"
|
||||
position="left"
|
||||
showGridLines
|
||||
domain={{ min: 0, max: 100 }}
|
||||
tickFormat={(rawScore) => getPostureScorePercentage(rawScore)}
|
||||
/>
|
||||
<Axis ticks={3} id="left-axis" position="left" showGridLines domain={{ min: 0, max: 100 }} />
|
||||
</Chart>
|
||||
);
|
||||
};
|
||||
|
||||
const CounterLink = ({
|
||||
text,
|
||||
count,
|
||||
color,
|
||||
onClick,
|
||||
tooltipContent,
|
||||
}: {
|
||||
count: number;
|
||||
text: string;
|
||||
color: EuiTextProps['color'];
|
||||
onClick: EuiLinkButtonProps['onClick'];
|
||||
tooltipContent: EuiToolTipProps['content'];
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<EuiToolTip content={tooltipContent}>
|
||||
<EuiLink color="text" onClick={onClick} css={{ display: 'flex' }}>
|
||||
<EuiText color={color} style={{ fontWeight: euiTheme.font.weight.medium }} size="s">
|
||||
<CompactFormattedNumber number={count} abbreviateAbove={999} />
|
||||
|
||||
</EuiText>
|
||||
<EuiText size="s">{text}</EuiText>
|
||||
</EuiLink>
|
||||
</EuiToolTip>
|
||||
);
|
||||
};
|
||||
|
||||
export const CloudPostureScoreChart = ({
|
||||
data,
|
||||
trend,
|
||||
id,
|
||||
partitionOnElementClick,
|
||||
}: CloudPostureScoreChartProps) => (
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem grow={4}>
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
|
||||
<ScoreChart {...{ id, data, partitionOnElementClick }} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<PercentageInfo {...data} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<EuiFlexItem grow={6}>
|
||||
<ComplianceTrendChart trend={trend} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
onEvalCounterClick,
|
||||
compact,
|
||||
}: CloudPostureScoreChartProps) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
justifyContent="spaceBetween"
|
||||
style={{ height: '100%' }}
|
||||
gutterSize="none"
|
||||
>
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiFlexGroup direction="row" justifyContent="spaceBetween" gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<PercentageInfo {...data} compact={compact} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup
|
||||
justifyContent="flexEnd"
|
||||
gutterSize="none"
|
||||
alignItems={compact ? 'center' : 'flexStart'}
|
||||
style={{ paddingRight: euiTheme.size.xl }}
|
||||
>
|
||||
<CounterLink
|
||||
text="passed"
|
||||
count={data.totalPassed}
|
||||
color="success"
|
||||
onClick={() => onEvalCounterClick(RULE_PASSED)}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip',
|
||||
{ defaultMessage: 'Passed findings' }
|
||||
)}
|
||||
/>
|
||||
{`-`}
|
||||
<CounterLink
|
||||
text="failed"
|
||||
count={data.totalFailed}
|
||||
color="danger"
|
||||
onClick={() => onEvalCounterClick(RULE_FAILED)}
|
||||
tooltipContent={i18n.translate(
|
||||
'xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip',
|
||||
{ defaultMessage: 'Failed findings' }
|
||||
)}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={compact ? 8 : 6}>
|
||||
<ComplianceTrendChart trend={trend} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ export interface RisksTableProps {
|
|||
maxItems: number;
|
||||
onCellClick: (name: string) => void;
|
||||
onViewAllClick: () => void;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export const getTopRisks = (
|
||||
|
@ -42,26 +43,31 @@ export const RisksTable = ({
|
|||
maxItems,
|
||||
onCellClick,
|
||||
onViewAllClick,
|
||||
compact,
|
||||
}: RisksTableProps) => {
|
||||
const columns: Array<EuiBasicTableColumn<GroupedFindingsEvaluation>> = useMemo(
|
||||
() => [
|
||||
{
|
||||
field: 'name',
|
||||
truncateText: true,
|
||||
name: i18n.translate('xpack.csp.dashboard.risksTable.cisSectionColumnLabel', {
|
||||
defaultMessage: 'CIS Section',
|
||||
}),
|
||||
name: compact
|
||||
? ''
|
||||
: i18n.translate('xpack.csp.dashboard.risksTable.cisSectionColumnLabel', {
|
||||
defaultMessage: 'CIS Section',
|
||||
}),
|
||||
render: (name: GroupedFindingsEvaluation['name']) => (
|
||||
<EuiLink onClick={() => onCellClick(name)} className="eui-textTruncate">
|
||||
<EuiLink onClick={() => onCellClick(name)} className="eui-textTruncate" color="text">
|
||||
{name}
|
||||
</EuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'totalFailed',
|
||||
name: i18n.translate('xpack.csp.dashboard.risksTable.findingsColumnLabel', {
|
||||
defaultMessage: 'Findings',
|
||||
}),
|
||||
name: compact
|
||||
? ''
|
||||
: i18n.translate('xpack.csp.dashboard.risksTable.findingsColumnLabel', {
|
||||
defaultMessage: 'Findings',
|
||||
}),
|
||||
render: (
|
||||
totalFailed: GroupedFindingsEvaluation['totalFailed'],
|
||||
resource: GroupedFindingsEvaluation
|
||||
|
@ -78,13 +84,13 @@ export const RisksTable = ({
|
|||
),
|
||||
},
|
||||
],
|
||||
[onCellClick]
|
||||
[compact, onCellClick]
|
||||
);
|
||||
|
||||
const items = useMemo(() => getTopRisks(resourcesTypes, maxItems), [resourcesTypes, maxItems]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" justifyContent="spaceBetween" gutterSize="s">
|
||||
<EuiFlexGroup direction="column" justifyContent="spaceBetween" gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiBasicTable<GroupedFindingsEvaluation>
|
||||
rowHeader="name"
|
||||
|
@ -93,16 +99,14 @@ export const RisksTable = ({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup justifyContent="center" gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onViewAllClick} iconType="search">
|
||||
<FormattedMessage
|
||||
id="xpack.csp.dashboard.risksTable.viewAllButtonTitle"
|
||||
defaultMessage="View all failed findings"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<div>
|
||||
<EuiButtonEmpty onClick={onViewAllClick} iconType="search">
|
||||
<FormattedMessage
|
||||
id="xpack.csp.dashboard.risksTable.viewAllButtonTitle"
|
||||
defaultMessage="View all failed findings"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -9,13 +9,10 @@ import React from 'react';
|
|||
import { EuiSpacer, EuiPageHeader } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { INTERNAL_FEATURE_FLAGS } from '../../../common/constants';
|
||||
import { CloudSummarySection } from './dashboard_sections/cloud_summary_section';
|
||||
import { CloudPosturePageTitle } from '../../components/cloud_posture_page_title';
|
||||
import { CloudPosturePage } from '../../components/cloud_posture_page';
|
||||
import { DASHBOARD_CONTAINER } from './test_subjects';
|
||||
import { SummarySection } from './dashboard_sections/summary_section';
|
||||
import { BenchmarksSection } from './dashboard_sections/benchmarks_section';
|
||||
import { useComplianceDashboardDataApi } from '../../common/api';
|
||||
import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api';
|
||||
import { NoFindingsStates } from '../../components/no_findings_states';
|
||||
|
@ -51,21 +48,12 @@ export const ComplianceDashboard = () => {
|
|||
margin-right: auto;
|
||||
`}
|
||||
>
|
||||
{INTERNAL_FEATURE_FLAGS.showNewDashboard ? (
|
||||
<>
|
||||
<CloudSummarySection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
<CloudBenchmarksSection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<SummarySection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
<BenchmarksSection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<CloudSummarySection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
<CloudBenchmarksSection complianceData={getDashboardData.data!} />
|
||||
<EuiSpacer />
|
||||
</>
|
||||
</div>
|
||||
</CloudPosturePage>
|
||||
);
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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 { EuiFlexItem, EuiPanel, EuiSpacer, EuiFlexGroup, useEuiTheme } from '@elastic/eui';
|
||||
import { PartitionElementEvent } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
|
||||
import { ChartPanel } from '../../../components/chart_panel';
|
||||
import type { ComplianceDashboardData, Evaluation } from '../../../../common/types';
|
||||
import { RisksTable } from '../compliance_charts/risks_table';
|
||||
import { RULE_FAILED } from '../../../../common/constants';
|
||||
import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings';
|
||||
import { ClusterDetailsBox } from './cluster_details_box';
|
||||
|
||||
const cardHeight = 300;
|
||||
|
||||
export const BenchmarksSection = ({
|
||||
complianceData,
|
||||
}: {
|
||||
complianceData: ComplianceDashboardData;
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const navToFindings = useNavigateFindings();
|
||||
|
||||
const handleElementClick = (clusterId: string, elements: PartitionElementEvent[]) => {
|
||||
const [element] = elements;
|
||||
const [layerValue] = element;
|
||||
const evaluation = layerValue[0].groupByRollup as Evaluation;
|
||||
|
||||
navToFindings({ cluster_id: clusterId, 'result.evaluation': evaluation });
|
||||
};
|
||||
|
||||
const handleCellClick = (clusterId: string, ruleSection: string) => {
|
||||
navToFindings({
|
||||
cluster_id: clusterId,
|
||||
'rule.section': ruleSection,
|
||||
'result.evaluation': RULE_FAILED,
|
||||
});
|
||||
};
|
||||
|
||||
const handleViewAllClick = (clusterId: string) => {
|
||||
navToFindings({ cluster_id: clusterId, 'result.evaluation': RULE_FAILED });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{complianceData.clusters.map((cluster) => (
|
||||
<React.Fragment key={cluster.meta.clusterId}>
|
||||
<EuiPanel hasBorder hasShadow={false} paddingSize="none">
|
||||
<EuiFlexGroup gutterSize="none" style={{ height: cardHeight }}>
|
||||
<EuiFlexItem
|
||||
grow={2}
|
||||
style={{
|
||||
borderRight: `1px solid ${euiTheme.colors.lightShade}`,
|
||||
borderRadius: `${euiTheme.border.radius.medium} 0 0 ${euiTheme.border.radius.medium}`,
|
||||
background: euiTheme.colors.lightestShade,
|
||||
padding: euiTheme.size.base,
|
||||
}}
|
||||
>
|
||||
<ClusterDetailsBox cluster={cluster} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={4}
|
||||
style={{ borderRight: `1px solid ${euiTheme.colors.lightShade}` }}
|
||||
>
|
||||
<ChartPanel
|
||||
title={i18n.translate(
|
||||
'xpack.csp.dashboard.benchmarkSection.complianceScorePanelTitle',
|
||||
{ defaultMessage: 'Compliance Score' }
|
||||
)}
|
||||
hasBorder={false}
|
||||
>
|
||||
<CloudPostureScoreChart
|
||||
id={`${cluster.meta.clusterId}_score_chart`}
|
||||
data={cluster.stats}
|
||||
trend={cluster.trend}
|
||||
partitionOnElementClick={(elements) =>
|
||||
handleElementClick(cluster.meta.clusterId, elements)
|
||||
}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={4}>
|
||||
<ChartPanel
|
||||
title={i18n.translate(
|
||||
'xpack.csp.dashboard.benchmarkSection.failedFindingsPanelTitle',
|
||||
{ defaultMessage: 'Failed Findings' }
|
||||
)}
|
||||
hasBorder={false}
|
||||
>
|
||||
<RisksTable
|
||||
data={cluster.groupedFindingsEvaluation}
|
||||
maxItems={3}
|
||||
onCellClick={(resourceTypeName) =>
|
||||
handleCellClick(cluster.meta.clusterId, resourceTypeName)
|
||||
}
|
||||
onViewAllClick={() => handleViewAllClick(cluster.meta.clusterId)}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiFlexItem, EuiFlexGroup, useEuiTheme, EuiTitle } from '@elastic/eui';
|
||||
import { PartitionElementEvent } from '@elastic/charts';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ChartPanel } from '../../../components/chart_panel';
|
||||
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
|
||||
import type { ComplianceDashboardData, Evaluation } from '../../../../common/types';
|
||||
import { RisksTable } from '../compliance_charts/risks_table';
|
||||
|
@ -26,11 +24,7 @@ export const CloudBenchmarksSection = ({
|
|||
const { euiTheme } = useEuiTheme();
|
||||
const navToFindings = useNavigateFindings();
|
||||
|
||||
const handleElementClick = (clusterId: string, elements: PartitionElementEvent[]) => {
|
||||
const [element] = elements;
|
||||
const [layerValue] = element;
|
||||
const evaluation = layerValue[0].groupByRollup as Evaluation;
|
||||
|
||||
const handleEvalCounterClick = (clusterId: string, evaluation: Evaluation) => {
|
||||
navToFindings({ cluster_id: clusterId, 'result.evaluation': evaluation });
|
||||
};
|
||||
|
||||
|
@ -54,7 +48,7 @@ export const CloudBenchmarksSection = ({
|
|||
style={{
|
||||
borderBottom: euiTheme.border.thick,
|
||||
borderBottomColor: euiTheme.colors.text,
|
||||
marginBottom: euiTheme.size.l,
|
||||
marginBottom: euiTheme.size.m,
|
||||
paddingBottom: euiTheme.size.s,
|
||||
}}
|
||||
>
|
||||
|
@ -105,20 +99,28 @@ export const CloudBenchmarksSection = ({
|
|||
<ClusterDetailsBox cluster={cluster} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={dashboardColumnsGrow.second}>
|
||||
<ChartPanel hasBorder={false}>
|
||||
<div
|
||||
style={{
|
||||
paddingLeft: euiTheme.size.base,
|
||||
paddingRight: euiTheme.size.base,
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<CloudPostureScoreChart
|
||||
compact
|
||||
id={`${cluster.meta.clusterId}_score_chart`}
|
||||
data={cluster.stats}
|
||||
trend={cluster.trend}
|
||||
partitionOnElementClick={(elements) =>
|
||||
handleElementClick(cluster.meta.clusterId, elements)
|
||||
onEvalCounterClick={(evaluation) =>
|
||||
handleEvalCounterClick(cluster.meta.clusterId, evaluation)
|
||||
}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={dashboardColumnsGrow.third}>
|
||||
<ChartPanel hasBorder={false}>
|
||||
<div style={{ paddingLeft: euiTheme.size.base, paddingRight: euiTheme.size.base }}>
|
||||
<RisksTable
|
||||
compact
|
||||
data={cluster.groupedFindingsEvaluation}
|
||||
maxItems={3}
|
||||
onCellClick={(resourceTypeName) =>
|
||||
|
@ -126,7 +128,7 @@ export const CloudBenchmarksSection = ({
|
|||
}
|
||||
onViewAllClick={() => handleViewAllClick(cluster.meta.clusterId)}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
))}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import React, { useMemo } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { PartitionElementEvent } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FlexItemGrowSize } from '@elastic/eui/src/components/flex/flex_item';
|
||||
import { DASHBOARD_COUNTER_CARDS } from '../test_subjects';
|
||||
|
@ -23,13 +22,6 @@ import {
|
|||
} from '../../../common/hooks/use_navigate_findings';
|
||||
import { RULE_FAILED } from '../../../../common/constants';
|
||||
|
||||
const defaultHeight = 360;
|
||||
|
||||
// TODO: limit this to desktop media queries only
|
||||
const summarySectionWrapperStyle = {
|
||||
height: defaultHeight,
|
||||
};
|
||||
|
||||
export const dashboardColumnsGrow: Record<string, FlexItemGrowSize> = {
|
||||
first: 3,
|
||||
second: 8,
|
||||
|
@ -44,11 +36,7 @@ export const CloudSummarySection = ({
|
|||
const navToFindings = useNavigateFindings();
|
||||
const navToFindingsByResource = useNavigateFindingsByResource();
|
||||
|
||||
const handleElementClick = (elements: PartitionElementEvent[]) => {
|
||||
const [element] = elements;
|
||||
const [layerValue] = element;
|
||||
const evaluation = layerValue[0].groupByRollup as Evaluation;
|
||||
|
||||
const handleEvalCounterClick = (evaluation: Evaluation) => {
|
||||
navToFindings({ 'result.evaluation': evaluation });
|
||||
};
|
||||
|
||||
|
@ -67,33 +55,31 @@ export const CloudSummarySection = ({
|
|||
() => [
|
||||
{
|
||||
id: DASHBOARD_COUNTER_CARDS.CLUSTERS_EVALUATED,
|
||||
title: i18n.translate(
|
||||
description: i18n.translate(
|
||||
'xpack.csp.dashboard.summarySection.counterCard.clustersEvaluatedDescription',
|
||||
{ defaultMessage: 'Clusters Evaluated' }
|
||||
),
|
||||
description: <CompactFormattedNumber number={complianceData.clusters.length} />,
|
||||
title: <CompactFormattedNumber number={complianceData.clusters.length} />,
|
||||
},
|
||||
{
|
||||
id: DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED,
|
||||
title: i18n.translate(
|
||||
description: i18n.translate(
|
||||
'xpack.csp.dashboard.summarySection.counterCard.resourcesEvaluatedDescription',
|
||||
{ defaultMessage: 'Resources Evaluated' }
|
||||
),
|
||||
description: (
|
||||
<CompactFormattedNumber number={complianceData.stats.resourcesEvaluated || 0} />
|
||||
),
|
||||
title: <CompactFormattedNumber number={complianceData.stats.resourcesEvaluated || 0} />,
|
||||
onClick: () => {
|
||||
navToFindingsByResource();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: DASHBOARD_COUNTER_CARDS.FAILING_FINDINGS,
|
||||
title: i18n.translate(
|
||||
description: i18n.translate(
|
||||
'xpack.csp.dashboard.summarySection.counterCard.failingFindingsDescription',
|
||||
{ defaultMessage: 'Failing Findings' }
|
||||
),
|
||||
description: <CompactFormattedNumber number={complianceData.stats.totalFailed} />,
|
||||
descriptionColor: complianceData.stats.totalFailed > 0 ? 'danger' : 'text',
|
||||
title: <CompactFormattedNumber number={complianceData.stats.totalFailed} />,
|
||||
titleColor: complianceData.stats.totalFailed > 0 ? 'danger' : 'text',
|
||||
onClick: () => {
|
||||
navToFindings({ 'result.evaluation': RULE_FAILED });
|
||||
},
|
||||
|
@ -109,7 +95,7 @@ export const CloudSummarySection = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="l" style={summarySectionWrapperStyle}>
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={dashboardColumnsGrow.first}>
|
||||
<EuiFlexGroup direction="column">
|
||||
{counters.map((counter) => (
|
||||
|
@ -129,15 +115,16 @@ export const CloudSummarySection = ({
|
|||
id="cloud_posture_score_chart"
|
||||
data={complianceData.stats}
|
||||
trend={complianceData.trend}
|
||||
partitionOnElementClick={handleElementClick}
|
||||
onEvalCounterClick={handleEvalCounterClick}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={dashboardColumnsGrow.third}>
|
||||
<ChartPanel
|
||||
title={i18n.translate('xpack.csp.dashboard.summarySection.failedFindingsPanelTitle', {
|
||||
defaultMessage: 'Failed Findings',
|
||||
})}
|
||||
title={i18n.translate(
|
||||
'xpack.csp.dashboard.summarySection.complianceByCisSectionPanelTitle',
|
||||
{ defaultMessage: 'Compliance By CIS Section' }
|
||||
)}
|
||||
>
|
||||
<RisksTable
|
||||
data={complianceData.groupedFindingsEvaluation}
|
||||
|
|
|
@ -9,11 +9,11 @@ import {
|
|||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiToolTip,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import moment from 'moment';
|
||||
|
@ -30,6 +30,7 @@ const defaultClusterTitle = i18n.translate(
|
|||
);
|
||||
|
||||
export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const navToFindings = useNavigateFindings();
|
||||
|
||||
const shortId = cluster.meta.clusterId.slice(0, 6);
|
||||
|
@ -40,7 +41,7 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s" alignItems="flexStart">
|
||||
<EuiFlexGroup direction="column" gutterSize="none" alignItems="flexStart">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
|
@ -64,8 +65,8 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
|
|||
}
|
||||
>
|
||||
<EuiLink onClick={() => handleClusterTitleClick(cluster.meta.clusterId)} color="text">
|
||||
<EuiText size="xs">
|
||||
<h3>
|
||||
<EuiTitle css={{ fontSize: 20 }}>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.csp.dashboard.benchmarkSection.clusterTitle"
|
||||
defaultMessage="{title} - {shortId}"
|
||||
|
@ -74,31 +75,25 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
|
|||
shortId,
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiText>
|
||||
</h5>
|
||||
</EuiTitle>
|
||||
</EuiLink>
|
||||
</EuiToolTip>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="xs" color="subdued">
|
||||
<EuiIcon type="clock" />
|
||||
<FormattedMessage
|
||||
id="xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle"
|
||||
defaultMessage=" Last evaluated {dateFromNow}"
|
||||
defaultMessage="Last evaluated {dateFromNow}"
|
||||
values={{
|
||||
dateFromNow: moment(cluster.meta.lastUpdate).fromNow(),
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<CISBenchmarkIcon
|
||||
type={cluster.meta.benchmarkId}
|
||||
name={cluster.meta.benchmarkName}
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
}}
|
||||
/>
|
||||
<EuiFlexItem
|
||||
grow={true}
|
||||
style={{ justifyContent: 'flex-end', paddingBottom: euiTheme.size.m }}
|
||||
>
|
||||
<CISBenchmarkIcon type={cluster.meta.benchmarkId} name={cluster.meta.benchmarkName} />
|
||||
</EuiFlexItem>
|
||||
{INTERNAL_FEATURE_FLAGS.showManageRulesMock && (
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* 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 { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
|
||||
import { PartitionElementEvent } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ChartPanel } from '../../../components/chart_panel';
|
||||
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
|
||||
import type { ComplianceDashboardData, Evaluation } from '../../../../common/types';
|
||||
import { RisksTable } from '../compliance_charts/risks_table';
|
||||
import { CasesTable } from '../compliance_charts/cases_table';
|
||||
import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings';
|
||||
import { RULE_FAILED } from '../../../../common/constants';
|
||||
|
||||
const defaultHeight = 360;
|
||||
|
||||
// TODO: limit this to desktop media queries only
|
||||
const summarySectionWrapperStyle = {
|
||||
height: defaultHeight,
|
||||
};
|
||||
|
||||
export const SummarySection = ({ complianceData }: { complianceData: ComplianceDashboardData }) => {
|
||||
const navToFindings = useNavigateFindings();
|
||||
|
||||
const handleElementClick = (elements: PartitionElementEvent[]) => {
|
||||
const [element] = elements;
|
||||
const [layerValue] = element;
|
||||
const evaluation = layerValue[0].groupByRollup as Evaluation;
|
||||
|
||||
navToFindings({ 'result.evaluation': evaluation });
|
||||
};
|
||||
|
||||
const handleCellClick = (ruleSection: string) => {
|
||||
navToFindings({
|
||||
'rule.section': ruleSection,
|
||||
'result.evaluation': RULE_FAILED,
|
||||
});
|
||||
};
|
||||
|
||||
const handleViewAllClick = () => {
|
||||
navToFindings({ 'result.evaluation': RULE_FAILED });
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFlexGrid columns={3} style={summarySectionWrapperStyle}>
|
||||
<EuiFlexItem>
|
||||
<ChartPanel
|
||||
title={i18n.translate('xpack.csp.dashboard.summarySection.cloudPostureScorePanelTitle', {
|
||||
defaultMessage: 'Cloud Posture Score',
|
||||
})}
|
||||
>
|
||||
<CloudPostureScoreChart
|
||||
id="cloud_posture_score_chart"
|
||||
data={complianceData.stats}
|
||||
trend={complianceData.trend}
|
||||
partitionOnElementClick={handleElementClick}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ChartPanel
|
||||
title={i18n.translate('xpack.csp.dashboard.summarySection.failedFindingsPanelTitle', {
|
||||
defaultMessage: 'Failed Findings',
|
||||
})}
|
||||
>
|
||||
<RisksTable
|
||||
data={complianceData.groupedFindingsEvaluation}
|
||||
maxItems={5}
|
||||
onCellClick={handleCellClick}
|
||||
onViewAllClick={handleViewAllClick}
|
||||
/>
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ChartPanel
|
||||
title={i18n.translate('xpack.csp.dashboard.summarySection.openCasesPanelTitle', {
|
||||
defaultMessage: 'Open Cases',
|
||||
})}
|
||||
>
|
||||
<CasesTable />
|
||||
</ChartPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
);
|
||||
};
|
|
@ -78,9 +78,11 @@ export class CspPlugin
|
|||
(
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<RedirectAppLinks coreStart={core}>
|
||||
<SetupContext.Provider value={{ isCloudEnabled: this.isCloudEnabled }}>
|
||||
<CspRouter {...props} />
|
||||
</SetupContext.Provider>
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<SetupContext.Provider value={{ isCloudEnabled: this.isCloudEnabled }}>
|
||||
<CspRouter {...props} />
|
||||
</SetupContext.Provider>
|
||||
</div>
|
||||
</RedirectAppLinks>
|
||||
</KibanaContextProvider>
|
||||
),
|
||||
|
|
|
@ -9853,16 +9853,11 @@
|
|||
"xpack.csp.cspEvaluationBadge.failLabel": "Échec",
|
||||
"xpack.csp.cspEvaluationBadge.passLabel": "Réussite",
|
||||
"xpack.csp.cspSettings.rules": "Règles de sécurité du CSP - ",
|
||||
"xpack.csp.dashboard.benchmarkSection.complianceScorePanelTitle": "Score de conformité",
|
||||
"xpack.csp.dashboard.benchmarkSection.failedFindingsPanelTitle": "Échec des résultats",
|
||||
"xpack.csp.dashboard.casesTable.placeholderTitle": "Bientôt disponible",
|
||||
"xpack.csp.dashboard.cspPageTemplate.pageTitle": "Niveau du cloud",
|
||||
"xpack.csp.dashboard.risksTable.cisSectionColumnLabel": "Section CIS",
|
||||
"xpack.csp.dashboard.risksTable.findingsColumnLabel": "Résultats",
|
||||
"xpack.csp.dashboard.risksTable.viewAllButtonTitle": "Afficher tous les échecs des résultats",
|
||||
"xpack.csp.dashboard.summarySection.cloudPostureScorePanelTitle": "Score du niveau du cloud",
|
||||
"xpack.csp.dashboard.summarySection.failedFindingsPanelTitle": "Échec des résultats",
|
||||
"xpack.csp.dashboard.summarySection.openCasesPanelTitle": "Cas ouverts",
|
||||
"xpack.csp.expandColumnDescriptionLabel": "Développer",
|
||||
"xpack.csp.expandColumnNameLabel": "Développer",
|
||||
"xpack.csp.findings.distributionBar.totalFailedLabel": "Échec des résultats",
|
||||
|
|
|
@ -9840,16 +9840,11 @@
|
|||
"xpack.csp.cspEvaluationBadge.failLabel": "失敗",
|
||||
"xpack.csp.cspEvaluationBadge.passLabel": "合格",
|
||||
"xpack.csp.cspSettings.rules": "CSPセキュリティルール - ",
|
||||
"xpack.csp.dashboard.benchmarkSection.complianceScorePanelTitle": "コンプライアンススコア",
|
||||
"xpack.csp.dashboard.benchmarkSection.failedFindingsPanelTitle": "失敗した調査結果",
|
||||
"xpack.csp.dashboard.casesTable.placeholderTitle": "まもなくリリース",
|
||||
"xpack.csp.dashboard.cspPageTemplate.pageTitle": "クラウド態勢",
|
||||
"xpack.csp.dashboard.risksTable.cisSectionColumnLabel": "CISセクション",
|
||||
"xpack.csp.dashboard.risksTable.findingsColumnLabel": "調査結果",
|
||||
"xpack.csp.dashboard.risksTable.viewAllButtonTitle": "すべてのフィールド調査結果を表示",
|
||||
"xpack.csp.dashboard.summarySection.cloudPostureScorePanelTitle": "クラウド態勢スコア",
|
||||
"xpack.csp.dashboard.summarySection.failedFindingsPanelTitle": "失敗した調査結果",
|
||||
"xpack.csp.dashboard.summarySection.openCasesPanelTitle": "ケースを開く",
|
||||
"xpack.csp.expandColumnDescriptionLabel": "拡張",
|
||||
"xpack.csp.expandColumnNameLabel": "拡張",
|
||||
"xpack.csp.findings.distributionBar.totalFailedLabel": "失敗した調査結果",
|
||||
|
|
|
@ -9858,16 +9858,11 @@
|
|||
"xpack.csp.cspEvaluationBadge.failLabel": "失败",
|
||||
"xpack.csp.cspEvaluationBadge.passLabel": "通过",
|
||||
"xpack.csp.cspSettings.rules": "CSP 安全规则 - ",
|
||||
"xpack.csp.dashboard.benchmarkSection.complianceScorePanelTitle": "合规性分数",
|
||||
"xpack.csp.dashboard.benchmarkSection.failedFindingsPanelTitle": "失败的结果",
|
||||
"xpack.csp.dashboard.casesTable.placeholderTitle": "即将推出",
|
||||
"xpack.csp.dashboard.cspPageTemplate.pageTitle": "云态势",
|
||||
"xpack.csp.dashboard.risksTable.cisSectionColumnLabel": "CIS 部分",
|
||||
"xpack.csp.dashboard.risksTable.findingsColumnLabel": "结果",
|
||||
"xpack.csp.dashboard.risksTable.viewAllButtonTitle": "查看所有失败的结果",
|
||||
"xpack.csp.dashboard.summarySection.cloudPostureScorePanelTitle": "云态势分数",
|
||||
"xpack.csp.dashboard.summarySection.failedFindingsPanelTitle": "失败的结果",
|
||||
"xpack.csp.dashboard.summarySection.openCasesPanelTitle": "未结案例",
|
||||
"xpack.csp.expandColumnDescriptionLabel": "展开",
|
||||
"xpack.csp.expandColumnNameLabel": "展开",
|
||||
"xpack.csp.findings.distributionBar.totalFailedLabel": "失败的结果",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue