Skip to content

Commit

Permalink
[Cloud Security][BugFix] Fix for loading issue when loading Dashboard (
Browse files Browse the repository at this point in the history
…#153908)

## Summary

This is a fix for issue where:
- the dashboard is stuck on loading state after user installed either
KSPM or CSPM
- Installation Prompt for integration thats not been installed yet is
not rendered
-  Regression on Findings page caused by Status PR

Added a test to cover rendering test for CSPM or KSPM installation prompt on Dashboard page (depending on which one is not installed yet)
---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
animehart and kibanamachine authored Mar 31, 2023
1 parent dfbf21f commit 107f4d8
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 { CspSetupStatus } from '../../../common/types';

// Cloud Posture Management Status
export const getCpmStatus = (cpmStatusData: CspSetupStatus | undefined) => {
// if has findings in any of the integrations.
const hasFindings =
cpmStatusData?.indicesDetails[0].status === 'not-empty' ||
cpmStatusData?.kspm.status === 'indexed' ||
cpmStatusData?.cspm.status === 'indexed';

// kspm
const hasKspmFindings =
cpmStatusData?.kspm?.status === 'indexed' ||
cpmStatusData?.indicesDetails[0].status === 'not-empty';

// cspm
const hasCspmFindings =
cpmStatusData?.cspm?.status === 'indexed' ||
cpmStatusData?.indicesDetails[0].status === 'not-empty';

const isKspmInstalled = cpmStatusData?.kspm?.status !== 'not-installed';
const isCspmInstalled = cpmStatusData?.cspm?.status !== 'not-installed';
const isKspmPrivileged = cpmStatusData?.kspm?.status !== 'unprivileged';
const isCspmPrivileged = cpmStatusData?.cspm?.status !== 'unprivileged';

const isCspmIntegrationInstalled = isCspmInstalled && isCspmPrivileged;
const isKspmIntegrationInstalled = isKspmInstalled && isKspmPrivileged;

const isEmptyData =
cpmStatusData?.kspm?.status === 'not-installed' &&
cpmStatusData?.cspm?.status === 'not-installed' &&
cpmStatusData?.indicesDetails[0].status === 'empty';

return {
hasFindings,
hasKspmFindings,
hasCspmFindings,
isCspmInstalled,
isKspmInstalled,
isKspmPrivileged,
isCspmPrivileged,
isCspmIntegrationInstalled,
isKspmIntegrationInstalled,
isEmptyData,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ describe('<CloudPosturePage />', () => {
(useCspSetupStatusApi as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: { status: 'indexed' },
data: {
cspm: { status: 'indexed' },
kspm: { status: 'indexed' },
indicesDetails: [
{ index: 'logs-cloud_security_posture.findings_latest-default', status: 'not-empty' },
{ index: 'logs-cloud_security_posture.findings-default*', status: 'not-empty' },
],
},
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { useCspIntegrationLink } from '../common/navigation/use_csp_integration_

import noDataIllustration from '../assets/illustrations/no_data_illustration.svg';
import { cspIntegrationDocsNavigation } from '../common/navigation/constants';
import { getCpmStatus } from '../common/utils/get_cpm_status';

export const LOADING_STATE_TEST_SUBJECT = 'cloud_posture_page_loading';
export const ERROR_STATE_TEST_SUBJECT = 'cloud_posture_page_error';
Expand Down Expand Up @@ -250,9 +251,10 @@ export const CloudPosturePage = <TData, TError>({
noDataRenderer = defaultNoDataRenderer,
}: CloudPosturePageProps<TData, TError>) => {
const subscriptionStatus = useSubscriptionStatus();
const getSetupStatus = useCspSetupStatusApi();
const { data: getSetupStatus, isLoading, isError, error } = useCspSetupStatusApi();
const kspmIntegrationLink = useCspIntegrationLink(KSPM_POLICY_TEMPLATE);
const cspmIntegrationLink = useCspIntegrationLink(CSPM_POLICY_TEMPLATE);
const { isEmptyData, hasFindings } = getCpmStatus(getSetupStatus);

const render = () => {
if (subscriptionStatus.isError) {
Expand All @@ -267,23 +269,23 @@ export const CloudPosturePage = <TData, TError>({
return subscriptionNotAllowedRenderer();
}

if (getSetupStatus.isError) {
return defaultErrorRenderer(getSetupStatus.error);
if (isError) {
return defaultErrorRenderer(error);
}

if (getSetupStatus.isLoading) {
if (isLoading) {
return defaultLoadingRenderer();
}

/* Checks if its a completely new user which means no integration has been installed and no latest findings default index has been found */
if (
getSetupStatus.data?.kspm?.status === 'not-installed' &&
getSetupStatus.data?.cspm?.status === 'not-installed' &&
getSetupStatus.data?.indicesDetails[0].status === 'empty'
) {
if (isEmptyData) {
return packageNotInstalledRenderer({ kspmIntegrationLink, cspmIntegrationLink });
}

if (!hasFindings) {
return children;
}

if (!query) {
return children;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ import { useCISIntegrationPoliciesLink } from '../common/navigation/use_navigate
import { NO_FINDINGS_STATUS_TEST_SUBJ } from './test_subjects';
import { CloudPosturePage } from './cloud_posture_page';
import { useCspSetupStatusApi } from '../common/api/use_setup_status_api';
import type { CloudSecurityPolicyTemplate, IndexDetails } from '../../common/types';
import type { IndexDetails, PostureTypes } from '../../common/types';

const REFETCH_INTERVAL_MS = 20000;

interface PostureTypes {
posturetype: CloudSecurityPolicyTemplate;
}

const NotDeployed = () => {
// using an existing hook to get agent id and package policy id
const benchmarks = useCspBenchmarkIntegrations({
Expand Down Expand Up @@ -180,14 +176,14 @@ const Unprivileged = ({ unprivilegedIndices }: { unprivilegedIndices: string[] }
* This component will return the render states based on cloud posture setup status API
* since 'not-installed' is being checked globally by CloudPosturePage and 'indexed' is the pass condition, those states won't be handled here
* */
export const NoFindingsStates = (posturetype?: PostureTypes) => {
export const NoFindingsStates = ({ posturetype }: { posturetype: PostureTypes }) => {
const getSetupStatus = useCspSetupStatusApi({
options: { refetchInterval: REFETCH_INTERVAL_MS },
});
const statusKspm = getSetupStatus.data?.kspm?.status;
const statusCspm = getSetupStatus.data?.cspm?.status;
const indicesStatus = getSetupStatus.data?.indicesDetails;
const status = posturetype?.posturetype === 'cspm' ? statusCspm : statusKspm;
const status = posturetype === 'cspm' ? statusCspm : statusKspm;
const unprivilegedIndices =
indicesStatus &&
indicesStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ describe('<Benchmarks />', () => {
(useCspSetupStatusApi as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: { status: 'indexed' },
data: {
cspm: { status: 'indexed' },
kspm: { status: 'indexed' },
indicesDetails: [
{ index: 'logs-cloud_security_posture.findings_latest-default', status: 'not-empty' },
{ index: 'logs-cloud_security_posture.findings-default*', status: 'not-empty' },
],
},
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React from 'react';

import { coreMock } from '@kbn/core/public/mocks';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { TestProvider } from '../../test/test_provider';
import { ComplianceDashboard } from '.';
import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api';
Expand All @@ -18,11 +18,17 @@ import {
CLOUD_DASHBOARD_CONTAINER,
DASHBOARD_CONTAINER,
KUBERNETES_DASHBOARD_CONTAINER,
KUBERNETES_DASHBOARD_TAB,
CLOUD_DASHBOARD_TAB,
} from './test_subjects';
import { mockDashboardData } from './mock';
import { createReactQueryResponse } from '../../test/fixtures/react_query';
import { NO_FINDINGS_STATUS_TEST_SUBJ } from '../../components/test_subjects';
import { expectIdsInDoc } from '../../test/utils';
import {
CSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT,
KSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT,
} from '../../components/cloud_posture_page';

jest.mock('../../common/api/use_setup_status_api');
jest.mock('../../common/api/use_stats_api');
Expand Down Expand Up @@ -498,4 +504,86 @@ describe('<ComplianceDashboard />', () => {
],
});
});

it('Show CSPM installation prompt if CSPM is not installed and KSPM is installed ,NO AGENT', () => {
(useCspSetupStatusApi as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: {
kspm: { status: 'not-deployed', healthyAgents: 0, installedPackagePolicies: 1 },
cspm: { status: 'not-installed' },
indicesDetails: [
{ index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' },
{ index: 'logs-cloud_security_posture.findings-default*', status: 'empty' },
],
},
})
);
(useKspmStatsApi as jest.Mock).mockImplementation(() => ({
isSuccess: true,
isLoading: false,
data: { stats: { totalFindings: 0 } },
}));
(useCspmStatsApi as jest.Mock).mockImplementation(() => ({
isSuccess: true,
isLoading: false,
data: undefined,
}));

renderComplianceDashboardPage();

screen.getByTestId(CLOUD_DASHBOARD_TAB).click();

expectIdsInDoc({
be: [CSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT],
notToBe: [
KUBERNETES_DASHBOARD_CONTAINER,
NO_FINDINGS_STATUS_TEST_SUBJ.INDEX_TIMEOUT,
NO_FINDINGS_STATUS_TEST_SUBJ.NO_AGENTS_DEPLOYED,
NO_FINDINGS_STATUS_TEST_SUBJ.INDEXING,
NO_FINDINGS_STATUS_TEST_SUBJ.UNPRIVILEGED,
],
});
});

it('Show KSPM installation prompt if KSPM is not installed and CSPM is installed , NO AGENT', () => {
(useCspSetupStatusApi as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: {
cspm: { status: 'not-deployed' },
kspm: { status: 'not-installed' },
indicesDetails: [
{ index: 'logs-cloud_security_posture.findings_latest-default', status: 'empty' },
{ index: 'logs-cloud_security_posture.findings-default*', status: 'empty' },
],
},
})
);
(useCspmStatsApi as jest.Mock).mockImplementation(() => ({
isSuccess: true,
isLoading: false,
data: { stats: { totalFindings: 0 } },
}));
(useKspmStatsApi as jest.Mock).mockImplementation(() => ({
isSuccess: true,
isLoading: false,
data: undefined,
}));

renderComplianceDashboardPage();

screen.getByTestId(KUBERNETES_DASHBOARD_TAB).click();

expectIdsInDoc({
be: [KSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT],
notToBe: [
CLOUD_DASHBOARD_CONTAINER,
NO_FINDINGS_STATUS_TEST_SUBJ.INDEX_TIMEOUT,
NO_FINDINGS_STATUS_TEST_SUBJ.NO_AGENTS_DEPLOYED,
NO_FINDINGS_STATUS_TEST_SUBJ.INDEXING,
NO_FINDINGS_STATUS_TEST_SUBJ.UNPRIVILEGED,
],
});
});
});
Loading

0 comments on commit 107f4d8

Please sign in to comment.