[Cloud Posture] Dashboard initial FTR (#155163)

This commit is contained in:
Jordan 2023-04-23 11:51:44 +03:00 committed by GitHub
parent d6d933a2af
commit 118daf778b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 224 additions and 17 deletions

View file

@ -29,13 +29,14 @@ import {
import { FormattedDate, FormattedTime } from '@kbn/i18n-react';
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { DASHBOARD_COMPLIANCE_SCORE_CHART } from '../test_subjects';
import { statusColors } from '../../../common/constants';
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 {
interface ComplianceScoreChartProps {
compact?: boolean;
trend: PostureTrend[];
data: Stats;
@ -48,7 +49,7 @@ const getPostureScorePercentage = (postureScore: number): string => `${Math.roun
const PercentageInfo = ({
compact,
postureScore,
}: CloudPostureScoreChartProps['data'] & { compact?: CloudPostureScoreChartProps['compact'] }) => {
}: ComplianceScoreChartProps['data'] & { compact?: ComplianceScoreChartProps['compact'] }) => {
const { euiTheme } = useEuiTheme();
const percentage = getPostureScorePercentage(postureScore);
@ -59,6 +60,7 @@ const PercentageInfo = ({
paddingLeft: compact ? euiTheme.size.s : euiTheme.size.xs,
marginBottom: compact ? euiTheme.size.s : 'none',
}}
data-test-subj={DASHBOARD_COMPLIANCE_SCORE_CHART.COMPLIANCE_SCORE}
>
<h3>{percentage}</h3>
</EuiTitle>
@ -140,12 +142,12 @@ const CounterLink = ({
);
};
export const CloudPostureScoreChart = ({
export const ComplianceScoreChart = ({
data,
trend,
onEvalCounterClick,
compact,
}: CloudPostureScoreChartProps) => {
}: ComplianceScoreChartProps) => {
const { euiTheme } = useEuiTheme();
return (
@ -173,7 +175,7 @@ export const CloudPostureScoreChart = ({
color={statusColors.passed}
onClick={() => onEvalCounterClick(RULE_PASSED)}
tooltipContent={i18n.translate(
'xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip',
'xpack.csp.complianceScoreChart.counterLink.passedFindingsTooltip',
{ defaultMessage: 'Passed findings' }
)}
/>
@ -184,7 +186,7 @@ export const CloudPostureScoreChart = ({
color={statusColors.failed}
onClick={() => onEvalCounterClick(RULE_FAILED)}
tooltipContent={i18n.translate(
'xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip',
'xpack.csp.complianceScoreChart.counterLink.failedFindingsTooltip',
{ defaultMessage: 'Failed findings' }
)}
/>

View file

@ -31,6 +31,7 @@ import {
KUBERNETES_DASHBOARD_CONTAINER,
KUBERNETES_DASHBOARD_TAB,
CLOUD_DASHBOARD_TAB,
CLOUD_POSTURE_DASHBOARD_PAGE_HEADER,
} from './test_subjects';
import { useCspmStatsApi, useKspmStatsApi } from '../../common/api/use_stats_api';
import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api';
@ -332,6 +333,7 @@ export const ComplianceDashboard = () => {
return (
<CloudPosturePage>
<EuiPageHeader
data-test-subj={CLOUD_POSTURE_DASHBOARD_PAGE_HEADER}
bottomBorder
pageTitle={
<CloudPosturePageTitle

View file

@ -12,7 +12,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, useEuiTheme } from '@elas
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
import type {
Cluster,
ComplianceDashboardData,
@ -33,6 +32,7 @@ import {
DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID,
DASHBOARD_TABLE_HEADER_SCORE_TEST_ID,
} from '../test_subjects';
import { ComplianceScoreChart } from '../compliance_charts/compliance_score_chart';
const CLUSTER_DEFAULT_SORT_ORDER = 'asc';
@ -175,7 +175,7 @@ export const BenchmarksSection = ({
`}
data-test-subj={DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID}
>
<CloudPostureScoreChart
<ComplianceScoreChart
compact
id={`${cluster.meta.assetIdentifierId}_score_chart`}
data={cluster.stats}

View file

@ -10,11 +10,11 @@ import { EuiFlexGroup, EuiFlexItem, EuiFlexItemProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { statusColors } from '../../../common/constants';
import { DASHBOARD_COUNTER_CARDS } from '../test_subjects';
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';
import { ChartPanel } from '../../../components/chart_panel';
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
import { ComplianceScoreChart } from '../compliance_charts/compliance_score_chart';
import type {
ComplianceDashboardData,
Evaluation,
@ -139,6 +139,7 @@ export const SummarySection = ({
// height for compliance by cis section with max rows
height: 310px;
`}
data-test-subj={DASHBOARD_SUMMARY_CONTAINER}
>
<EuiFlexItem grow={dashboardColumnsGrow.first}>
<EuiFlexGroup direction="column">
@ -151,7 +152,7 @@ export const SummarySection = ({
</EuiFlexItem>
<EuiFlexItem grow={dashboardColumnsGrow.second}>
<ChartPanel title={chartTitle}>
<CloudPostureScoreChart
<ComplianceScoreChart
id="cloud_posture_score_chart"
data={complianceData.stats}
trend={complianceData.trend}

View file

@ -7,8 +7,11 @@
export const MISSING_FINDINGS_NO_DATA_CONFIG = 'missing-findings-no-data-config';
export const DASHBOARD_CONTAINER = 'dashboard-container';
export const DASHBOARD_SUMMARY_CONTAINER = 'dashboard-summary-section';
export const KUBERNETES_DASHBOARD_CONTAINER = 'kubernetes-dashboard-container';
export const CLOUD_DASHBOARD_CONTAINER = 'cloud-dashboard-container';
export const CLOUD_POSTURE_DASHBOARD_PAGE_HEADER = 'cloud-posture-dashboard-page-header';
export const DASHBOARD_COUNTER_CARDS = {
CLUSTERS_EVALUATED: 'dashboard-counter-card-clusters-evaluated',
RESOURCES_EVALUATED: 'dashboard-counter-card-resources-evaluated',
@ -18,3 +21,7 @@ export const DASHBOARD_TABLE_HEADER_SCORE_TEST_ID = 'csp:dashboard-sections-tabl
export const DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID = 'csp:dashboard-sections-table-column-score';
export const KUBERNETES_DASHBOARD_TAB = 'kubernetes-dashboard-tab';
export const CLOUD_DASHBOARD_TAB = 'cloud-dashboard-tab';
export const DASHBOARD_COMPLIANCE_SCORE_CHART = {
COMPLIANCE_SCORE: 'dashboard-summary-section-compliance-score',
};

View file

@ -10619,8 +10619,6 @@
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "Ajouter une intégration KSPM",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "En savoir plus sur le niveau de sécurité du cloud",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "Détectez les erreurs de configuration de sécurité dans vos ressources cloud !",
"xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "Échec des résultats",
"xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "Réussite des résultats",
"xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "Afficher le tableau de bord CSP",
"xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "Afficher les résultats CSP ",
"xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "Afficher les règles CSP ",

View file

@ -10619,8 +10619,6 @@
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "KSPM統合の追加",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "クラウドセキュリティ態勢の詳細をご覧ください",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "クラウドリソースでセキュリティの誤った構成を検出します。",
"xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "失敗した調査結果",
"xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "合格した調査結果",
"xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "CSPダッシュボードを表示",
"xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "CSP調査結果を表示 ",
"xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "CSPルールを表示 ",

View file

@ -10619,8 +10619,6 @@
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.addKspmIntegrationButtonTitle": "添加 KSPM 集成",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.learnMoreTitle": "了解有关云安全态势的详情",
"xpack.csp.cloudPosturePage.packageNotInstalledRenderer.promptTitle": "在云资源中检测安全配置错误!",
"xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip": "失败的结果",
"xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip": "通过的结果",
"xpack.csp.createPackagePolicy.customAssetsTab.dashboardViewLabel": "查看 CSP 仪表板",
"xpack.csp.createPackagePolicy.customAssetsTab.findingsViewLabel": "查看 CSP 结果 ",
"xpack.csp.createPackagePolicy.customAssetsTab.rulesViewLabel": "查看 CSP 规则 ",

View file

@ -0,0 +1,134 @@
/*
* 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 expect from '@kbn/expect';
import type { FtrProviderContext } from '../ftr_provider_context';
// Defined in CSP plugin
const LATEST_FINDINGS_INDEX = 'logs-cloud_security_posture.findings_latest-default';
export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common']);
const retry = getService('retry');
const es = getService('es');
const supertest = getService('supertest');
const log = getService('log');
/**
* required before indexing findings
*/
const waitForPluginInitialized = (): Promise<void> =>
retry.try(async () => {
log.debug('Check CSP plugin is initialized');
const response = await supertest
.get('/internal/cloud_security_posture/status?check=init')
.expect(200);
expect(response.body).to.eql({ isPluginInitialized: true });
log.debug('CSP plugin is initialized');
});
const index = {
remove: () => es.indices.delete({ index: LATEST_FINDINGS_INDEX, ignore_unavailable: true }),
add: async <T>(findingsMock: T[]) => {
await Promise.all(
findingsMock.map((finding) =>
es.index({
index: LATEST_FINDINGS_INDEX,
body: finding,
})
)
);
},
};
const dashboard = {
getDashboardPageHeader: () => testSubjects.find('cloud-posture-dashboard-page-header'),
getDashboardTabs: async () => {
const dashboardPageHeader = await dashboard.getDashboardPageHeader();
return await dashboardPageHeader.findByClassName('euiTabs');
},
getCloudTab: async () => {
const tabs = await dashboard.getDashboardTabs();
return await tabs.findByXpath(`//span[text()="Cloud"]`);
},
getKubernetesTab: async () => {
const tabs = await dashboard.getDashboardTabs();
return await tabs.findByXpath(`//span[text()="Kubernetes"]`);
},
clickTab: async (tab: 'Cloud' | 'Kubernetes') => {
if (tab === 'Cloud') {
const cloudTab = await dashboard.getCloudTab();
await cloudTab.click();
}
if (tab === 'Kubernetes') {
const k8sTab = await dashboard.getKubernetesTab();
await k8sTab.click();
}
},
getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'),
// Cloud Dashboard
getCloudDashboard: async () => {
await dashboard.clickTab('Cloud');
return await testSubjects.find('cloud-dashboard-container');
},
getCloudSummarySection: async () => {
await dashboard.getCloudDashboard();
return await testSubjects.find('dashboard-summary-section');
},
getCloudComplianceScore: async () => {
await dashboard.getCloudSummarySection();
return await testSubjects.find('dashboard-summary-section-compliance-score');
},
// Kubernetes Dashboard
getKubernetesDashboard: async () => {
await dashboard.clickTab('Kubernetes');
return await testSubjects.find('kubernetes-dashboard-container');
},
getKubernetesSummarySection: async () => {
await dashboard.getKubernetesDashboard();
return await testSubjects.find('dashboard-summary-section');
},
getKubernetesComplianceScore: async () => {
await dashboard.getKubernetesSummarySection();
return await testSubjects.find('dashboard-summary-section-compliance-score');
},
getKubernetesComplianceScore2: async () => {
// await dashboard.getKubernetesSummarySection();
return await testSubjects.find('dashboard-summary-section-compliance-score');
},
};
const navigateToComplianceDashboardPage = async () => {
await PageObjects.common.navigateToUrl(
'securitySolution', // Defined in Security Solution plugin
'cloud_security_posture/dashboard',
{ shouldUseHashForSubUrl: false }
);
};
return {
waitForPluginInitialized,
navigateToComplianceDashboardPage,
dashboard,
index,
};
}

View file

@ -7,8 +7,10 @@
import { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects';
import { FindingsPageProvider } from './findings_page';
import { CspDashboardPageProvider } from './csp_dashboard_page';
export const pageObjects = {
...xpackFunctionalPageObjects,
findings: FindingsPageProvider,
cloudPostureDashboard: CspDashboardPageProvider,
};

View file

@ -0,0 +1,64 @@
/*
* 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 expect from '@kbn/expect';
import Chance from 'chance';
import type { FtrProviderContext } from '../ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const retry = getService('retry');
const pageObjects = getPageObjects(['common', 'cloudPostureDashboard']);
const chance = new Chance();
const data = [
{
'@timestamp': new Date().toISOString(),
resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' },
result: { evaluation: 'failed' },
rule: {
name: 'Upper case rule name',
section: 'Upper case section',
benchmark: {
id: 'cis_k8s',
posture_type: 'kspm',
},
},
cluster_id: 'Upper case cluster id',
},
];
describe('Cloud Posture Dashboard Page', () => {
let cspDashboard: typeof pageObjects.cloudPostureDashboard;
let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard;
before(async () => {
cspDashboard = pageObjects.cloudPostureDashboard;
dashboard = pageObjects.cloudPostureDashboard.dashboard;
await cspDashboard.waitForPluginInitialized();
await cspDashboard.index.add(data);
await cspDashboard.navigateToComplianceDashboardPage();
await retry.waitFor(
'Cloud posture integration dashboard to be displayed',
async () => !!dashboard.getIntegrationDashboardContainer()
);
});
after(async () => {
await cspDashboard.index.remove();
});
describe('Kubernetes Dashboard', () => {
it('displays accurate summary compliance score', async () => {
const scoreElement = await dashboard.getKubernetesComplianceScore();
expect((await scoreElement.getVisibleText()) === '0%').to.be(true);
});
});
});
}

View file

@ -11,5 +11,6 @@ import { FtrProviderContext } from '../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Cloud Security Posture', function () {
loadTestFile(require.resolve('./findings'));
loadTestFile(require.resolve('./compliance_dashboard'));
});
}