[8.8] [Cloud Security][Bug Fix] Empty EKS Dashboard / Wrong number of Healthy agents shown fix (#156601) (#156919)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[Cloud Security][Bug Fix] Empty EKS Dashboard / Wrong number of
Healthy agents shown fix
(#156601)](https://github.com/elastic/kibana/pull/156601)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Rickyanto
Ang","email":"rickyangwyn@gmail.com"},"sourceCommit":{"committedDate":"2023-05-05T20:44:05Z","message":"[Cloud
Security][Bug Fix] Empty EKS Dashboard / Wrong number of Healthy agents
shown fix (#156601)\n\n## Summary\r\n\r\nThis Fix is to address issue
where Healthy agents remains zero even when\r\nwe have Healthy agents
and EKS Dashboard is empty even though we have\r\nfindings for that
integration flowing in\r\n\r\nAlso added new API test for
Benchmark\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b5fc7c4054a1bb97e4f522ee61c19a62054cace9","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Cloud
Security","v8.8.0","v8.9.0"],"number":156601,"url":"https://github.com/elastic/kibana/pull/156601","mergeCommit":{"message":"[Cloud
Security][Bug Fix] Empty EKS Dashboard / Wrong number of Healthy agents
shown fix (#156601)\n\n## Summary\r\n\r\nThis Fix is to address issue
where Healthy agents remains zero even when\r\nwe have Healthy agents
and EKS Dashboard is empty even though we have\r\nfindings for that
integration flowing in\r\n\r\nAlso added new API test for
Benchmark\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b5fc7c4054a1bb97e4f522ee61c19a62054cace9"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"8.8","label":"v8.8.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/156601","number":156601,"mergeCommit":{"message":"[Cloud
Security][Bug Fix] Empty EKS Dashboard / Wrong number of Healthy agents
shown fix (#156601)\n\n## Summary\r\n\r\nThis Fix is to address issue
where Healthy agents remains zero even when\r\nwe have Healthy agents
and EKS Dashboard is empty even though we have\r\nfindings for that
integration flowing in\r\n\r\nAlso added new API test for
Benchmark\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b5fc7c4054a1bb97e4f522ee61c19a62054cace9"}}]}]
BACKPORT-->

Co-authored-by: Rickyanto Ang <rickyangwyn@gmail.com>
This commit is contained in:
Kibana Machine 2023-05-05 17:56:08 -04:00 committed by GitHub
parent 75f8f72127
commit 2a20412fd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 242 additions and 141 deletions

View file

@ -107,3 +107,10 @@ export type BenchmarkName = CspRuleTemplateMetadata['benchmark']['name'];
export type PostureInput = typeof SUPPORTED_CLOUDBEAT_INPUTS[number];
export type CloudSecurityPolicyTemplate = typeof SUPPORTED_POLICY_TEMPLATES[number];
export type PosturePolicyTemplate = Extract<CloudSecurityPolicyTemplate, 'kspm' | 'cspm'>;
export interface BenchmarkResponse {
items: Benchmark[];
total: number;
page: number;
perPage: number;
}

View file

@ -19,11 +19,15 @@ import type {
} from '@kbn/fleet-plugin/common';
import { errors } from '@elastic/elasticsearch';
import { CloudSecurityPolicyTemplate, PostureTypes } from '../../common/types';
import { SUPPORTED_POLICY_TEMPLATES } from '../../common/constants';
import {
SUPPORTED_POLICY_TEMPLATES,
CLOUD_SECURITY_POSTURE_PACKAGE_NAME,
} from '../../common/constants';
import { CSP_FLEET_PACKAGE_KUERY } from '../../common/utils/helpers';
import {
BENCHMARK_PACKAGE_POLICY_PREFIX,
BenchmarksQueryParams,
DEFAULT_BENCHMARKS_PER_PAGE,
} from '../../common/schemas/benchmark';
export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies';
@ -34,25 +38,8 @@ const isFleetMissingAgentHttpError = (error: unknown) =>
const isPolicyTemplate = (input: any): input is CloudSecurityPolicyTemplate =>
SUPPORTED_POLICY_TEMPLATES.includes(input);
const getPackageNameQuery = (
// ADD 3rd case both cspm and kspm, for findings posture type empty => kspm
postureType: string,
packageName: string,
benchmarkFilter?: string
): string => {
const integrationNameQuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${packageName}`;
const integrationPostureType = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.vars.posture.value:${postureType}`;
if (postureType === 'all') {
const kquery = benchmarkFilter
? `${integrationNameQuery} AND ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: *${benchmarkFilter}*`
: `${integrationNameQuery}`;
return kquery;
} else {
const kquery = benchmarkFilter
? `${integrationNameQuery} AND ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: *${benchmarkFilter}* AND ${integrationPostureType}`
: `${integrationNameQuery} AND ${integrationPostureType}`;
return kquery;
}
const getPackageNameQuery = (): string => {
return `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:"${CLOUD_SECURITY_POSTURE_PACKAGE_NAME}"`;
};
export type AgentStatusByAgentPolicyMap = Record<string, GetAgentStatusResponse['results']>;
@ -94,7 +81,7 @@ export const getCspAgentPolicies = async (
ignoreMissing: true,
});
export const getCspPackagePolicies = (
export const getCspPackagePolicies = async (
soClient: SavedObjectsClientContract,
packagePolicyService: PackagePolicyClient,
packageName: string,
@ -103,13 +90,37 @@ export const getCspPackagePolicies = (
): Promise<ListResult<PackagePolicy>> => {
const sortField = queryParams.sort_field?.replaceAll(BENCHMARK_PACKAGE_POLICY_PREFIX, '');
return packagePolicyService.list(soClient, {
kuery: getPackageNameQuery(postureType, packageName, queryParams.benchmark_name),
page: queryParams.page,
perPage: queryParams.per_page,
const allCSPPackages = await packagePolicyService.list(soClient, {
kuery: getPackageNameQuery(),
page: 1,
perPage: 10000,
sortField,
sortOrder: queryParams.sort_order,
});
const filteredItems = allCSPPackages.items.filter(
(pkg) =>
pkg.inputs.filter((input) =>
postureType === 'all'
? input.enabled
: input.enabled && input.policy_template === postureType
).length > 0 &&
(!queryParams.benchmark_name ||
pkg.name.toLowerCase().includes(queryParams.benchmark_name.toLowerCase()))
);
const page = queryParams?.page ?? 1;
const perPage = queryParams?.per_page ?? DEFAULT_BENCHMARKS_PER_PAGE;
return {
items: filteredItems.slice(
(page - 1) * perPage,
Math.min(filteredItems.length, page * perPage)
),
total: filteredItems.length,
page,
perPage,
};
};
export const getInstalledPolicyTemplates = async (

View file

@ -9,19 +9,11 @@ import {
benchmarksQueryParamsSchema,
DEFAULT_BENCHMARKS_PER_PAGE,
} from '../../../common/schemas/benchmark';
import {
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
getCspPackagePolicies,
getCspAgentPolicies,
} from '../../lib/fleet_util';
import { POSTURE_TYPE_ALL } from '../../../common/constants';
import { getCspAgentPolicies } from '../../lib/fleet_util';
import { defineGetBenchmarksRoute, getRulesCountForPolicy } from './benchmarks';
import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server';
import {
createMockAgentPolicyService,
createPackagePolicyServiceMock,
} from '@kbn/fleet-plugin/server/mocks';
import { createMockAgentPolicyService } from '@kbn/fleet-plugin/server/mocks';
import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks';
import { createCspRequestHandlerContextMock } from '../../mocks';
@ -154,111 +146,6 @@ describe('benchmarks API', () => {
mockSoClient = savedObjectsClientMock.create();
});
describe('test getPackagePolicies', () => {
it('should format request by package name', async () => {
const mockPackagePolicyService = createPackagePolicyServiceMock();
await getCspPackagePolicies(
mockSoClient,
mockPackagePolicyService,
'myPackage',
{
page: 1,
per_page: 100,
sort_order: 'desc',
},
POSTURE_TYPE_ALL
);
expect(mockPackagePolicyService.list.mock.calls[0][1]).toMatchObject(
expect.objectContaining({
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage`,
page: 1,
perPage: 100,
})
);
});
it('should build sort request by `sort_field` and default `sort_order`', async () => {
const mockAgentPolicyService = createPackagePolicyServiceMock();
await getCspPackagePolicies(
mockSoClient,
mockAgentPolicyService,
'myPackage',
{
page: 1,
per_page: 100,
sort_field: 'package_policy.name',
sort_order: 'desc',
},
POSTURE_TYPE_ALL
);
expect(mockAgentPolicyService.list.mock.calls[0][1]).toMatchObject(
expect.objectContaining({
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage`,
page: 1,
perPage: 100,
sortField: 'name',
sortOrder: 'desc',
})
);
});
it('should build sort request by `sort_field` and asc `sort_order`', async () => {
const mockAgentPolicyService = createPackagePolicyServiceMock();
await getCspPackagePolicies(
mockSoClient,
mockAgentPolicyService,
'myPackage',
{
page: 1,
per_page: 100,
sort_field: 'package_policy.name',
sort_order: 'asc',
},
POSTURE_TYPE_ALL
);
expect(mockAgentPolicyService.list.mock.calls[0][1]).toMatchObject(
expect.objectContaining({
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage`,
page: 1,
perPage: 100,
sortField: 'name',
sortOrder: 'asc',
})
);
});
});
it('should format request by benchmark_name', async () => {
const mockAgentPolicyService = createPackagePolicyServiceMock();
await getCspPackagePolicies(
mockSoClient,
mockAgentPolicyService,
'myPackage',
{
page: 1,
per_page: 100,
sort_order: 'desc',
benchmark_name: 'my_cis_benchmark',
},
POSTURE_TYPE_ALL
);
expect(mockAgentPolicyService.list.mock.calls[0][1]).toMatchObject(
expect.objectContaining({
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:myPackage AND ${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: *my_cis_benchmark*`,
page: 1,
perPage: 100,
})
);
});
describe('test getAgentPolicies', () => {
it('should return one agent policy id when there is duplication', async () => {
const agentPolicyService = createMockAgentPolicyService();

View file

@ -0,0 +1,194 @@
/*
* 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 { BenchmarkResponse } from '@kbn/cloud-security-posture-plugin/common/types';
import type { SuperTest, Test } from 'supertest';
import { FtrProviderContext } from '../../ftr_provider_context';
// import { createPackagePolicy } from './status';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
describe('GET /internal/cloud_security_posture/benchmark', () => {
let agentPolicyId: string;
let agentPolicyId2: string;
let agentPolicyId3: string;
beforeEach(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
const { body: agentPolicyResponse } = await supertest
.post(`/api/fleet/agent_policies`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'Test policy',
namespace: 'default',
});
agentPolicyId = agentPolicyResponse.item.id;
const { body: agentPolicyResponse2 } = await supertest
.post(`/api/fleet/agent_policies`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'Test policy 2',
namespace: 'default',
});
agentPolicyId2 = agentPolicyResponse2.item.id;
const { body: agentPolicyResponse3 } = await supertest
.post(`/api/fleet/agent_policies`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'Test policy 3',
namespace: 'default',
});
agentPolicyId3 = agentPolicyResponse3.item.id;
await createPackagePolicy(
supertest,
agentPolicyId,
'cspm',
'cloudbeat/cis_aws',
'aws',
'cspm',
'CSPM-1'
);
await createPackagePolicy(
supertest,
agentPolicyId2,
'kspm',
'cloudbeat/cis_k8s',
'vanilla',
'kspm',
'KSPM-1'
);
await createPackagePolicy(
supertest,
agentPolicyId3,
'vuln_mgmt',
'cloudbeat/vuln_mgmt_aws',
'aws',
'vuln_mgmt',
'CNVM-1'
);
});
afterEach(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
});
it(`Should return non-empty array filled with Rules if user has CSP integrations`, async () => {
const { body: res }: { body: BenchmarkResponse } = await supertest
.get(`/internal/cloud_security_posture/benchmarks`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
expect(res.items.length).equal(3);
expect(res.total).equal(3);
});
it(`Should return array size 2 when we set per page to be only 2 (total element is still 3)`, async () => {
const { body: res }: { body: BenchmarkResponse } = await supertest
.get(`/internal/cloud_security_posture/benchmarks?per_page=2`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
expect(res.items.length).equal(2);
expect(res.total).equal(3);
});
it(`Should return array size 2 when we set per page to be only 2 (total element is still 3)`, async () => {
const { body: res }: { body: BenchmarkResponse } = await supertest
.get(`/internal/cloud_security_posture/benchmarks?per_page=2&page=2`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
expect(res.items.length).equal(1);
expect(res.total).equal(3);
});
it(`Should return empty array when we set page to be above the last page number`, async () => {
const { body: res }: { body: BenchmarkResponse } = await supertest
.get(`/internal/cloud_security_posture/benchmarks?per_page=2&page=3`)
.set('kbn-xsrf', 'xxxx')
.expect(200);
expect(res.items.length).equal(0);
expect(res.total).equal(3);
});
});
}
export async function createPackagePolicy(
supertest: SuperTest<Test>,
agentPolicyId: string,
policyTemplate: string,
input: string,
deployment: string,
posture: string,
packageName: string
) {
const version = posture === 'kspm' || posture === 'cspm' ? '1.2.8' : '1.3.0-preview2';
const title = 'Security Posture Management';
const streams = [
{
enabled: false,
data_stream: {
type: 'logs',
dataset: 'cloud_security_posture.vulnerabilities',
},
},
];
const inputTemplate = {
enabled: true,
type: input,
policy_template: policyTemplate,
};
const inputs = posture === 'vuln_mgmt' ? { ...inputTemplate, streams } : { ...inputTemplate };
const { body: postPackageResponse } = await supertest
.post(`/api/fleet/package_policies`)
.set('kbn-xsrf', 'xxxx')
.send({
force: true,
name: packageName,
description: '',
namespace: 'default',
policy_id: agentPolicyId,
enabled: true,
inputs: [inputs],
package: {
name: 'cloud_security_posture',
title,
version,
},
vars: {
deployment: {
value: deployment,
type: 'text',
},
posture: {
value: posture,
type: 'text',
},
},
})
.expect(200);
return postPackageResponse.item;
}

View file

@ -11,6 +11,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
describe('cloud_security_posture', function () {
this.tags(['cloud_security_posture']);
loadTestFile(require.resolve('./status'));
loadTestFile(require.resolve('./benchmark'));
// Place your tests files under this directory and add the following here:
// loadTestFile(require.resolve('./your test name'));

View file

@ -28,6 +28,7 @@ export default function ({ getService }: FtrProviderContext) {
name: 'Test policy',
namespace: 'default',
});
agentPolicyId = agentPolicyResponse.item.id;
});
@ -103,7 +104,7 @@ export default function ({ getService }: FtrProviderContext) {
});
}
async function createPackagePolicy(
export async function createPackagePolicy(
supertest: SuperTest<Test>,
agentPolicyId: string,
policyTemplate: string,