mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cloud Posture] Dashboard initial FTR (#155163)
This commit is contained in:
parent
d6d933a2af
commit
118daf778b
12 changed files with 224 additions and 17 deletions
|
@ -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' }
|
||||
)}
|
||||
/>
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
|
|
|
@ -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 ",
|
||||
|
|
|
@ -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ルールを表示 ",
|
||||
|
|
|
@ -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 规则 ",
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue