Skip to content

Commit

Permalink
[Cloud Posture] Dashboard initial FTR (#155163)
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanSh authored Apr 23, 2023
1 parent d6d933a commit 118daf7
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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>
Expand Down Expand Up @@ -140,12 +142,12 @@ const CounterLink = ({
);
};

export const CloudPostureScoreChart = ({
export const ComplianceScoreChart = ({
data,
trend,
onEvalCounterClick,
compact,
}: CloudPostureScoreChartProps) => {
}: ComplianceScoreChartProps) => {
const { euiTheme } = useEuiTheme();

return (
Expand Down Expand Up @@ -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' }
)}
/>
Expand All @@ -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' }
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -332,6 +333,7 @@ export const ComplianceDashboard = () => {
return (
<CloudPosturePage>
<EuiPageHeader
data-test-subj={CLOUD_POSTURE_DASHBOARD_PAGE_HEADER}
bottomBorder
pageTitle={
<CloudPosturePageTitle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';

Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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">
Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
};
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 ",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -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ルールを表示 ",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 规则 ",
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
}
Loading

0 comments on commit 118daf7

Please sign in to comment.