diff --git a/x-pack/test/cloud_security_posture_api/config.ts b/x-pack/test/cloud_security_posture_api/config.ts index 08dfc89bf1800..4e0ecd1f26e43 100644 --- a/x-pack/test/cloud_security_posture_api/config.ts +++ b/x-pack/test/cloud_security_posture_api/config.ts @@ -9,20 +9,18 @@ import type { FtrConfigProviderContext } from '@kbn/test'; import { CLOUD_SECURITY_PLUGIN_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xpackFunctionalConfig = await readConfigFile( - require.resolve('../functional/config.base.js') - ); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); return { - ...xpackFunctionalConfig.getAll(), - testFiles: [resolve(__dirname, './routes')], + ...xPackAPITestsConfig.getAll(), + testFiles: [resolve(__dirname, './routes'), resolve(__dirname, './telemetry')], junit: { reportName: 'X-Pack Cloud Security Posture API Tests', }, kbnTestServer: { - ...xpackFunctionalConfig.get('kbnTestServer'), + ...xPackAPITestsConfig.get('kbnTestServer'), serverArgs: [ - ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), + ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), /** * Package version is fixed (not latest) so FTR won't suddenly break when package is changed. * diff --git a/x-pack/test/cloud_security_posture_api/routes/benchmarks.ts b/x-pack/test/cloud_security_posture_api/routes/benchmarks.ts index 93ee947cef4a5..d28ffcd73d16d 100644 --- a/x-pack/test/cloud_security_posture_api/routes/benchmarks.ts +++ b/x-pack/test/cloud_security_posture_api/routes/benchmarks.ts @@ -9,7 +9,6 @@ import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; import { - BENCHMARK_SCORE_INDEX_DEFAULT_NS, CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, LATEST_FINDINGS_INDEX_DEFAULT_NS, } from '@kbn/cloud-security-posture-plugin/common/constants'; @@ -18,6 +17,7 @@ import Chance from 'chance'; import { CspBenchmarkRule } from '@kbn/cloud-security-posture-common/schema/rules/latest'; import { FtrProviderContext } from '../ftr_provider_context'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized, EsIndexDataProvider } from '../utils'; const chance = new Chance(); @@ -28,9 +28,10 @@ export default function (providerContext: FtrProviderContext) { const es = getService('es'); const kibanaServer = getService('kibanaServer'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); + const findingsIndex = new EsIndexDataProvider(es, LATEST_FINDINGS_INDEX_DEFAULT_NS); const getCspBenchmarkRules = async (benchmarkId: string): Promise => { let cspBenchmarkRules: CspBenchmarkRule[] = []; @@ -78,86 +79,21 @@ export default function (providerContext: FtrProviderContext) { }, }); - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - - const index = { - addFindings: async (findingsMock: T[]) => { - await Promise.all( - findingsMock.map((findingsDoc) => - es.index({ - index: LATEST_FINDINGS_INDEX_DEFAULT_NS, - body: { ...findingsDoc, '@timestamp': new Date().toISOString() }, - refresh: true, - }) - ) - ); - }, - - addScores: async (scoresMock: T[]) => { - await Promise.all( - scoresMock.map((scoreDoc) => - es.index({ - index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, - body: { ...scoreDoc, '@timestamp': new Date().toISOString() }, - refresh: true, - }) - ) - ); - }, - - removeFindings: async () => { - const indexExists = await es.indices.exists({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - - if (indexExists) { - es.deleteByQuery({ - index: LATEST_FINDINGS_INDEX_DEFAULT_NS, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - removeScores: async () => { - const indexExists = await es.indices.exists({ index: BENCHMARK_SCORE_INDEX_DEFAULT_NS }); - - if (indexExists) { - es.deleteByQuery({ - index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - deleteFindingsIndex: async () => { - const indexExists = await es.indices.exists({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - - if (indexExists) { - await es.indices.delete({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - } - }, - }; - describe('GET /internal/cloud_security_posture/benchmarks', () => { describe('Get Benchmark API', async () => { beforeEach(async () => { - await index.removeFindings(); + await findingsIndex.deleteAll(); + await kibanaServer.savedObjects.clean({ + types: ['cloud-security-posture-settings'], + }); + await waitForPluginInitialized({ retry, logger, supertest }); + }); + + afterEach(async () => { + await findingsIndex.deleteAll(); await kibanaServer.savedObjects.clean({ types: ['cloud-security-posture-settings'], }); - await waitForPluginInitialized(); }); it('Verify cspm benchmark score is updated when muting rules', async () => { @@ -166,7 +102,7 @@ export default function (providerContext: FtrProviderContext) { const cspmFinding = getMockFinding(benchmarkRules[0], 'passed'); - await index.addFindings([cspmFinding]); + await findingsIndex.addBulk([cspmFinding]); const { body: benchmarksBeforeMute } = await supertest .get('/internal/cloud_security_posture/benchmarks') @@ -219,7 +155,7 @@ export default function (providerContext: FtrProviderContext) { const kspmFinding = getMockFinding(benchmarkRules[0], 'passed'); - await index.addFindings([kspmFinding]); + await findingsIndex.addBulk([kspmFinding]); const { body: benchmarksBeforeMute } = await supertest .get('/internal/cloud_security_posture/benchmarks') .set(ELASTIC_HTTP_VERSION_HEADER, '2') @@ -268,11 +204,18 @@ export default function (providerContext: FtrProviderContext) { describe('Get Benchmark API', async () => { beforeEach(async () => { - await index.removeFindings(); + await findingsIndex.deleteAll(); + await kibanaServer.savedObjects.clean({ + types: ['cloud-security-posture-settings'], + }); + await waitForPluginInitialized({ retry, logger, supertest }); + }); + + afterEach(async () => { + await findingsIndex.deleteAll(); await kibanaServer.savedObjects.clean({ types: ['cloud-security-posture-settings'], }); - await waitForPluginInitialized(); }); it('Calling Benchmark API as User with no read access to Security', async () => { @@ -281,7 +224,7 @@ export default function (providerContext: FtrProviderContext) { const cspmFinding1 = getMockFinding(benchmarkRules[0], 'passed'); - await index.addFindings([cspmFinding1]); + await findingsIndex.addBulk([cspmFinding1]); const { body: benchmarksResult } = await supertestWithoutAuth .get('/internal/cloud_security_posture/benchmarks') @@ -303,7 +246,7 @@ export default function (providerContext: FtrProviderContext) { const cspmFinding1 = getMockFinding(benchmarkRules[0], 'passed'); - await index.addFindings([cspmFinding1]); + await findingsIndex.addBulk([cspmFinding1]); const { status } = await supertestWithoutAuth .get('/internal/cloud_security_posture/benchmarks') diff --git a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts index b46ecd1ef2943..490eb6453a6be 100644 --- a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts +++ b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts @@ -22,13 +22,14 @@ import type { CspBenchmarkRule } from '@kbn/cloud-security-posture-common/schema import { generateBenchmarkRuleTags } from '@kbn/cloud-security-posture-plugin/common/utils/detection_rules'; import type { FtrProviderContext } from '../ftr_provider_context'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized } from '../utils'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const retry = getService('retry'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const kibanaServer = getService('kibanaServer'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); @@ -83,23 +84,9 @@ export default function (providerContext: FtrProviderContext) { return detectionRule; }; - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - describe('Verify update csp rules states API', async () => { before(async () => { - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger, supertest }); }); beforeEach(async () => { @@ -108,6 +95,12 @@ export default function (providerContext: FtrProviderContext) { }); }); + afterEach(async () => { + await kibanaServer.savedObjects.clean({ + types: ['cloud-security-posture-settings', 'alert'], + }); + }); + it('mute benchmark rules successfully', async () => { const rule1 = await getRandomCspBenchmarkRule(); const rule2 = await getRandomCspBenchmarkRule(); diff --git a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_get_states.ts b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_get_states.ts index 61a22a234b6d0..d2da944e80df7 100644 --- a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_get_states.ts +++ b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_get_states.ts @@ -16,13 +16,14 @@ import { CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE } from '@kbn/cloud-security-postur import type { CspBenchmarkRule } from '@kbn/cloud-security-posture-common/schema/rules/latest'; import type { FtrProviderContext } from '../ftr_provider_context'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized } from '../utils'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const retry = getService('retry'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const kibanaServer = getService('kibanaServer'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); @@ -42,23 +43,9 @@ export default function (providerContext: FtrProviderContext) { return cspBenchmarkRules.saved_objects[randomIndex].attributes; }; - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - describe('Tests get rules states API', async () => { before(async () => { - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger, supertest }); }); beforeEach(async () => { @@ -67,6 +54,12 @@ export default function (providerContext: FtrProviderContext) { }); }); + afterEach(async () => { + await kibanaServer.savedObjects.clean({ + types: ['cloud-security-posture-settings'], + }); + }); + it('get rules states successfully', async () => { const rule1 = await getRandomCspBenchmarkRule(); const rule2 = await getRandomCspBenchmarkRule(); diff --git a/x-pack/test/cloud_security_posture_api/routes/get_detection_engine_alerts_count_by_rule_tags.ts b/x-pack/test/cloud_security_posture_api/routes/get_detection_engine_alerts_count_by_rule_tags.ts index 0b2ceb882ba23..ed3f89d5c6e08 100644 --- a/x-pack/test/cloud_security_posture_api/routes/get_detection_engine_alerts_count_by_rule_tags.ts +++ b/x-pack/test/cloud_security_posture_api/routes/get_detection_engine_alerts_count_by_rule_tags.ts @@ -8,6 +8,7 @@ import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized } from '../utils'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { @@ -15,29 +16,16 @@ export default function (providerContext: FtrProviderContext) { const retry = getService('retry'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - describe('/internal/cloud_security_posture/detection_engine_rules/alerts/_status', () => { describe('GET detection_engine_rules API with user that has specific access', async () => { before(async () => { - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger, supertest }); }); + it('GET detection_engine_rules API with user with read access', async () => { const { status } = await supertestWithoutAuth .get( diff --git a/x-pack/test/cloud_security_posture_api/routes/index.ts b/x-pack/test/cloud_security_posture_api/routes/index.ts index adacc80fb6f7d..bae346f880723 100644 --- a/x-pack/test/cloud_security_posture_api/routes/index.ts +++ b/x-pack/test/cloud_security_posture_api/routes/index.ts @@ -18,7 +18,6 @@ export default function (providerContext: FtrProviderContext) { await cspSecurity.createUsers(); }); - loadTestFile(require.resolve('../telemetry/telemetry.ts')); loadTestFile(require.resolve('./vulnerabilities_dashboard.ts')); loadTestFile(require.resolve('./stats.ts')); loadTestFile(require.resolve('./csp_benchmark_rules_bulk_update.ts')); diff --git a/x-pack/test/cloud_security_posture_api/routes/stats.ts b/x-pack/test/cloud_security_posture_api/routes/stats.ts index adb2952d5388e..baed734c0f18c 100644 --- a/x-pack/test/cloud_security_posture_api/routes/stats.ts +++ b/x-pack/test/cloud_security_posture_api/routes/stats.ts @@ -27,6 +27,7 @@ import { } from './mocks/benchmark_score_mock'; import { findingsMockData } from './mocks/findings_mock'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized, EsIndexDataProvider } from '../utils'; const removeRealtimeCalculatedFields = (trends: PostureTrend[]) => { return trends.map((trend: PostureTrend) => { @@ -64,88 +65,23 @@ export default function (providerContext: FtrProviderContext) { const log = getService('log'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); - - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - - const index = { - addFindings: async (findingsMock: T[]) => { - await Promise.all( - findingsMock.map((findingsDoc) => - es.index({ - index: LATEST_FINDINGS_INDEX_DEFAULT_NS, - body: { ...findingsDoc, '@timestamp': new Date().toISOString() }, - refresh: true, - }) - ) - ); - }, - - addScores: async (scoresMock: T[]) => { - await Promise.all( - scoresMock.map((scoreDoc) => - es.index({ - index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, - body: { ...scoreDoc, '@timestamp': new Date().toISOString() }, - refresh: true, - }) - ) - ); - }, - - removeFindings: async () => { - const indexExists = await es.indices.exists({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - - if (indexExists) { - es.deleteByQuery({ - index: LATEST_FINDINGS_INDEX_DEFAULT_NS, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - removeScores: async () => { - const indexExists = await es.indices.exists({ index: BENCHMARK_SCORE_INDEX_DEFAULT_NS }); - - if (indexExists) { - es.deleteByQuery({ - index: BENCHMARK_SCORE_INDEX_DEFAULT_NS, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - deleteFindingsIndex: async () => { - const indexExists = await es.indices.exists({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - - if (indexExists) { - await es.indices.delete({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS }); - } - }, - }; + const findingsIndex = new EsIndexDataProvider(es, LATEST_FINDINGS_INDEX_DEFAULT_NS); + const benchmarkScoreIndex = new EsIndexDataProvider(es, BENCHMARK_SCORE_INDEX_DEFAULT_NS); describe('GET /internal/cloud_security_posture/stats', () => { describe('CSPM Compliance Dashboard Stats API', async () => { beforeEach(async () => { - await index.removeFindings(); - await index.removeScores(); + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); - await waitForPluginInitialized(); - await index.addScores(getBenchmarkScoreMockData('cspm', true)); - await index.addFindings([findingsMockData[1]]); + await waitForPluginInitialized({ retry, logger: log, supertest }); + await benchmarkScoreIndex.addBulk(getBenchmarkScoreMockData('cspm', true)); + await findingsIndex.addBulk([findingsMockData[1]]); + }); + + afterEach(async () => { + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); }); it('should return CSPM cluster V1 ', async () => { @@ -185,12 +121,17 @@ export default function (providerContext: FtrProviderContext) { describe('KSPM Compliance Dashboard Stats API', async () => { beforeEach(async () => { - await index.removeFindings(); - await index.removeScores(); + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); - await waitForPluginInitialized(); - await index.addScores(getBenchmarkScoreMockData('kspm', true)); - await index.addFindings([findingsMockData[0]]); + await waitForPluginInitialized({ retry, logger: log, supertest }); + await benchmarkScoreIndex.addBulk(getBenchmarkScoreMockData('kspm', true)); + await findingsIndex.addBulk([findingsMockData[0]]); + }); + + afterEach(async () => { + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); }); it('should return KSPM clusters V1 ', async () => { @@ -249,15 +190,23 @@ export default function (providerContext: FtrProviderContext) { describe('Compliance dashboard based on enabled rules', async () => { beforeEach(async () => { - await index.removeFindings(); - await index.removeScores(); + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); + + await waitForPluginInitialized({ retry, logger: log, supertest }); + }); - await waitForPluginInitialized(); + afterEach(async () => { + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); }); + it('should calculate cspm benchmarks posture score based only on enabled rules', async () => { - await index.addScores(getBenchmarkScoreMockData('cspm', true)); - await index.addScores(getBenchmarkScoreMockData('cspm', false)); - await index.addFindings([findingsMockData[1]]); + await benchmarkScoreIndex.addBulk([ + ...getBenchmarkScoreMockData('cspm', true), + ...getBenchmarkScoreMockData('cspm', false), + ]); + await findingsIndex.addBulk([findingsMockData[1]]); const { body: res }: { body: ComplianceDashboardDataV2 } = await kibanaHttpClient .get(`/internal/cloud_security_posture/stats/cspm`) @@ -277,9 +226,11 @@ export default function (providerContext: FtrProviderContext) { }); it('should calculate kspm benchmarks posture score based only on enabled rules', async () => { - await index.addScores(getBenchmarkScoreMockData('kspm', true)); - await index.addScores(getBenchmarkScoreMockData('kspm', false)); - await index.addFindings([findingsMockData[0]]); + await benchmarkScoreIndex.addBulk([ + ...getBenchmarkScoreMockData('kspm', true), + ...getBenchmarkScoreMockData('kspm', false), + ]); + await findingsIndex.addBulk([findingsMockData[0]]); const { body: res }: { body: ComplianceDashboardDataV2 } = await kibanaHttpClient .get(`/internal/cloud_security_posture/stats/kspm`) @@ -301,30 +252,23 @@ export default function (providerContext: FtrProviderContext) { describe('GET stats API with user that has specific access', async () => { beforeEach(async () => { - await index.removeFindings(); - await index.removeScores(); + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger: log, supertest }); }); - it('GET stats API V1 with user with read access', async () => { - await index.addScores(getBenchmarkScoreMockData('cspm', true)); - await index.addScores(getBenchmarkScoreMockData('cspm', false)); - await index.addFindings([findingsMockData[1]]); - const { status } = await supertestWithoutAuth - .get(`/internal/cloud_security_posture/stats/cspm`) - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .set('kbn-xsrf', 'xxxx') - .auth( - 'role_security_read_user', - cspSecurity.getPasswordForUser('role_security_read_user') - ); - expect(status).to.be(200); + afterEach(async () => { + await findingsIndex.deleteAll(); + await benchmarkScoreIndex.deleteAll(); }); + it('GET stats API V1 with user with read access', async () => { - await index.addScores(getBenchmarkScoreMockData('cspm', true)); - await index.addScores(getBenchmarkScoreMockData('cspm', false)); - await index.addFindings([findingsMockData[1]]); + await benchmarkScoreIndex.addBulk([ + ...getBenchmarkScoreMockData('cspm', true), + ...getBenchmarkScoreMockData('cspm', false), + ]); + await findingsIndex.addBulk([findingsMockData[1]]); const { status } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/stats/cspm`) @@ -336,10 +280,13 @@ export default function (providerContext: FtrProviderContext) { ); expect(status).to.be(200); }); + it('GET stats API V2 with user with read access', async () => { - await index.addScores(getBenchmarkScoreMockData('cspm', true)); - await index.addScores(getBenchmarkScoreMockData('cspm', false)); - await index.addFindings([findingsMockData[1]]); + await benchmarkScoreIndex.addBulk([ + ...getBenchmarkScoreMockData('cspm', true), + ...getBenchmarkScoreMockData('cspm', false), + ]); + await findingsIndex.addBulk([findingsMockData[1]]); const { status } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/stats/cspm`) @@ -353,9 +300,11 @@ export default function (providerContext: FtrProviderContext) { }); it('GET stats API V2 with user without read access', async () => { - await index.addScores(getBenchmarkScoreMockData('kspm', true)); - await index.addScores(getBenchmarkScoreMockData('kspm', false)); - await index.addFindings([findingsMockData[0]]); + await benchmarkScoreIndex.addBulk([ + ...getBenchmarkScoreMockData('kspm', true), + ...getBenchmarkScoreMockData('kspm', false), + ]); + await findingsIndex.addBulk([findingsMockData[0]]); const { status } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/stats/kspm`) diff --git a/x-pack/test/cloud_security_posture_api/routes/status.ts b/x-pack/test/cloud_security_posture_api/routes/status.ts index 0004144575956..d73b059f16ecc 100644 --- a/x-pack/test/cloud_security_posture_api/routes/status.ts +++ b/x-pack/test/cloud_security_posture_api/routes/status.ts @@ -8,6 +8,7 @@ import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { waitForPluginInitialized } from '../utils'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { @@ -15,29 +16,16 @@ export default function (providerContext: FtrProviderContext) { const retry = getService('retry'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - describe('GET /internal/cloud_security_posture/status', () => { describe('GET status API with user that has specific access', async () => { before(async () => { - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger, supertest }); }); + it('GET stats API with user with read access', async () => { const { status } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/status`) diff --git a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts index 8ed4f91a61fea..30bae105df8e9 100644 --- a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts +++ b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts @@ -14,6 +14,7 @@ import { scoresVulnerabilitiesMock, } from './mocks/vulnerabilities_latest_mock'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; +import { EsIndexDataProvider, waitForPluginInitialized } from '../utils'; export interface CnvmStatistics { criticalCount?: number; @@ -112,89 +113,24 @@ export default function (providerContext: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); const supertest = getService('supertest'); - const log = getService('log'); + const logger = getService('log'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); - - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - - const index = { - addFindings: async (vulnerabilitiesMock: T[]) => { - await Promise.all( - vulnerabilitiesMock.map((vulnerabilityDoc) => - es.index({ - index: VULNERABILITIES_LATEST_INDEX, - body: vulnerabilityDoc, - refresh: true, - }) - ) - ); - }, - - addScores: async (scoresMock: T[]) => { - await Promise.all( - scoresMock.map((scoreDoc) => - es.index({ - index: BENCHMARK_SCORES_INDEX, - body: scoreDoc, - refresh: true, - }) - ) - ); - }, - - removeFindings: async () => { - const indexExists = await es.indices.exists({ index: VULNERABILITIES_LATEST_INDEX }); - - if (indexExists) { - await es.deleteByQuery({ - index: VULNERABILITIES_LATEST_INDEX, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - removeScores: async () => { - const indexExists = await es.indices.exists({ index: BENCHMARK_SCORES_INDEX }); - - if (indexExists) { - await es.deleteByQuery({ - index: BENCHMARK_SCORES_INDEX, - query: { match_all: {} }, - refresh: true, - }); - } - }, - - deleteFindingsIndex: async () => { - const indexExists = await es.indices.exists({ index: VULNERABILITIES_LATEST_INDEX }); - - if (indexExists) { - await es.indices.delete({ index: VULNERABILITIES_LATEST_INDEX }); - } - }, - }; + const vulnerabilitiesIndex = new EsIndexDataProvider(es, VULNERABILITIES_LATEST_INDEX); + const scoresIndex = new EsIndexDataProvider(es, BENCHMARK_SCORES_INDEX); describe('Vulnerability Dashboard API', async () => { beforeEach(async () => { - await index.removeFindings(); - await index.removeScores(); - await waitForPluginInitialized(); - await index.addScores(scoresVulnerabilitiesMock); - await index.addFindings(vulnerabilitiesLatestMock); + await vulnerabilitiesIndex.deleteAll(); + await scoresIndex.deleteAll(); + await waitForPluginInitialized({ retry, logger, supertest }); + await scoresIndex.addBulk(scoresVulnerabilitiesMock, false); + await vulnerabilitiesIndex.addBulk(vulnerabilitiesLatestMock, false); + }); + + afterEach(async () => { + await vulnerabilitiesIndex.deleteAll(); + await scoresIndex.deleteAll(); }); it('responds with a 200 status code and matching data mock', async () => { @@ -301,7 +237,7 @@ export default function (providerContext: FtrProviderContext) { }); it('returns a 400 error when necessary indices are nonexistent', async () => { - await index.deleteFindingsIndex(); + await vulnerabilitiesIndex.destroyIndex(); await supertest .get('/internal/cloud_security_posture/vulnerabilities_dashboard') diff --git a/x-pack/test/cloud_security_posture_api/telemetry/index.ts b/x-pack/test/cloud_security_posture_api/telemetry/index.ts new file mode 100644 index 0000000000000..4b0be2126e3d4 --- /dev/null +++ b/x-pack/test/cloud_security_posture_api/telemetry/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function (providerContext: FtrProviderContext) { + const { loadTestFile } = providerContext; + + describe('Cloud Security Posture', function () { + loadTestFile(require.resolve('./telemetry.ts')); + }); +} diff --git a/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts b/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts index e5867ccd74897..a3548bb255348 100644 --- a/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts +++ b/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts @@ -10,8 +10,9 @@ import { ELASTIC_HTTP_VERSION_HEADER, X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; -import { data, MockTelemetryFindings } from './data'; +import { data } from './data'; import type { FtrProviderContext } from '../ftr_provider_context'; +import { waitForPluginInitialized, EsIndexDataProvider } from '../utils'; const FINDINGS_INDEX = 'logs-cloud_security_posture.findings_latest-default'; @@ -20,52 +21,20 @@ export default function ({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); const supertest = getService('supertest'); - const log = getService('log'); - - /** - * required before indexing findings - */ - const waitForPluginInitialized = (): Promise => - retry.try(async () => { - log.debug('Check CSP plugin is initialized'); - const response = await supertest - .get('/internal/cloud_security_posture/status?check=init') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .expect(200); - expect(response.body).to.eql({ isPluginInitialized: true }); - log.debug('CSP plugin is initialized'); - }); - - const index = { - remove: () => - es.deleteByQuery({ - index: FINDINGS_INDEX, - query: { match_all: {} }, - refresh: true, - }), - - add: async (mockTelemetryFindings: MockTelemetryFindings[]) => { - const operations = mockTelemetryFindings.flatMap((doc) => [ - { index: { _index: FINDINGS_INDEX } }, - doc, - ]); - - const response = await es.bulk({ refresh: 'wait_for', index: FINDINGS_INDEX, operations }); - expect(response.errors).to.eql(false); - }, - }; + const logger = getService('log'); + const findingsIndexProvider = new EsIndexDataProvider(es, FINDINGS_INDEX); describe('Verify cloud_security_posture telemetry payloads', async () => { before(async () => { - await waitForPluginInitialized(); + await waitForPluginInitialized({ retry, logger, supertest }); }); afterEach(async () => { - await index.remove(); + await findingsIndexProvider.deleteAll(); }); it('includes only KSPM findings', async () => { - await index.add(data.kspmFindings); + await findingsIndexProvider.addBulk(data.kspmFindings, false); const { body: [{ stats: apiResponse }], @@ -119,7 +88,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('includes only CSPM findings', async () => { - await index.add(data.cspmFindings); + await findingsIndexProvider.addBulk(data.cspmFindings, false); const { body: [{ stats: apiResponse }], @@ -165,8 +134,8 @@ export default function ({ getService }: FtrProviderContext) { }); it('includes CSPM and KSPM findings', async () => { - await index.add(data.kspmFindings); - await index.add(data.cspmFindings); + await findingsIndexProvider.addBulk(data.kspmFindings, false); + await findingsIndexProvider.addBulk(data.cspmFindings, false); const { body: [{ stats: apiResponse }], @@ -244,7 +213,7 @@ export default function ({ getService }: FtrProviderContext) { }); it(`'includes only KSPM findings without posture_type'`, async () => { - await index.add(data.kspmFindingsNoPostureType); + await findingsIndexProvider.addBulk(data.kspmFindingsNoPostureType, false); const { body: [{ stats: apiResponse }], @@ -299,8 +268,8 @@ export default function ({ getService }: FtrProviderContext) { }); it('includes KSPM findings without posture_type and CSPM findings as well', async () => { - await index.add(data.kspmFindingsNoPostureType); - await index.add(data.cspmFindings); + await findingsIndexProvider.addBulk(data.kspmFindingsNoPostureType, false); + await findingsIndexProvider.addBulk(data.cspmFindings, false); const { body: [{ stats: apiResponse }], diff --git a/x-pack/test/cloud_security_posture_api/utils.ts b/x-pack/test/cloud_security_posture_api/utils.ts new file mode 100644 index 0000000000000..6f0d86419a349 --- /dev/null +++ b/x-pack/test/cloud_security_posture_api/utils.ts @@ -0,0 +1,75 @@ +/* + * 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 type { RetryService } from '@kbn/ftr-common-functional-services'; +import type { Agent } from 'supertest'; +import type { ToolingLog } from '@kbn/tooling-log'; +import type { Client as EsClient } from '@elastic/elasticsearch'; +import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; + +/** + * Checks if plugin initialization was done + * Required before indexing findings + */ +export const waitForPluginInitialized = ({ + retry, + logger, + supertest, +}: { + retry: RetryService; + logger: ToolingLog; + supertest: Agent; +}): Promise => + retry.try(async () => { + logger.debug('Check CSP plugin is initialized'); + const response = await supertest + .get('/internal/cloud_security_posture/status?check=init') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .expect(200); + expect(response.body).to.eql({ isPluginInitialized: true }); + logger.debug('CSP plugin is initialized'); + }); + +export class EsIndexDataProvider { + private es: EsClient; + private index: string; + + constructor(es: EsClient, index: string) { + this.es = es; + this.index = index; + } + + addBulk(docs: Array>, overrideTimestamp = true) { + const operations = docs.flatMap((doc) => [ + { index: { _index: this.index } }, + { ...doc, ...(overrideTimestamp ? { '@timestamp': new Date().toISOString() } : {}) }, + ]); + + return this.es.bulk({ refresh: 'wait_for', index: this.index, operations }); + } + + async deleteAll() { + const indexExists = await this.es.indices.exists({ index: this.index }); + + if (indexExists) { + return this.es.deleteByQuery({ + index: this.index, + query: { match_all: {} }, + refresh: true, + }); + } + } + + async destroyIndex() { + const indexExists = await this.es.indices.exists({ index: this.index }); + + if (indexExists) { + return this.es.indices.delete({ index: this.index }); + } + } +}