mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cloud Posture] test latest findings table sort (#144668)
This commit is contained in:
parent
1c22c801a4
commit
b9ffc29cd3
17 changed files with 329 additions and 3 deletions
|
@ -133,6 +133,7 @@ enabled:
|
|||
- x-pack/test/cases_api_integration/security_and_spaces/config_trial.ts
|
||||
- x-pack/test/cases_api_integration/security_and_spaces/config_no_public_base_url.ts
|
||||
- x-pack/test/cases_api_integration/spaces_only/config.ts
|
||||
- x-pack/test/cloud_security_posture_functional/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/basic/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group2/config.ts
|
||||
|
|
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -662,6 +662,8 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience
|
|||
/x-pack/plugins/cloud_security_posture/ @elastic/kibana-cloud-security-posture
|
||||
/x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
|
||||
/x-pack/test/api_integration/apis/cloud_security_posture/ @elastic/kibana-cloud-security-posture
|
||||
/x-pack/test/cloud_security_posture_functional/ @elastic/kibana-cloud-security-posture
|
||||
|
||||
|
||||
# Security Solution onboarding tour
|
||||
/x-pack/plugins/security_solution/public/common/components/guided_onboarding @elastic/security-threat-hunting-explore
|
||||
|
|
|
@ -62,6 +62,7 @@ interface BaseCspSetupStatus {
|
|||
latestPackageVersion: string;
|
||||
installedPackagePolicies: number;
|
||||
healthyAgents: number;
|
||||
isPluginInitialized: boolean;
|
||||
}
|
||||
|
||||
interface CspSetupNotInstalledStatus extends BaseCspSetupStatus {
|
||||
|
|
|
@ -239,7 +239,9 @@ const FilterableCell: React.FC<{
|
|||
}
|
||||
`}
|
||||
>
|
||||
<div className="__filter_value eui-textTruncate">{children}</div>
|
||||
<div className="__filter_value eui-textTruncate" data-test-subj="filter_cell_value">
|
||||
{children}
|
||||
</div>
|
||||
<div
|
||||
className="__filter_buttons"
|
||||
css={css`
|
||||
|
|
|
@ -31,6 +31,7 @@ export const createCspRequestHandlerContextMock = () => {
|
|||
agentService: createMockAgentService(),
|
||||
packagePolicyService: createPackagePolicyServiceMock(),
|
||||
packageService: createMockPackageService(),
|
||||
isPluginInitialized: () => false,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -62,6 +62,13 @@ export class CspPlugin
|
|||
private readonly logger: Logger;
|
||||
private isCloudEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* CSP is initialized when the Fleet package is installed.
|
||||
* either directly after installation, or
|
||||
* when the plugin is started and a package is present.
|
||||
*/
|
||||
#isInitialized: boolean = false;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.logger = initializerContext.logger.get();
|
||||
}
|
||||
|
@ -75,6 +82,7 @@ export class CspPlugin
|
|||
setupRoutes({
|
||||
core,
|
||||
logger: this.logger,
|
||||
isPluginInitialized: () => this.#isInitialized,
|
||||
});
|
||||
|
||||
const coreStartServices = core.getStartServices();
|
||||
|
@ -162,12 +170,16 @@ export class CspPlugin
|
|||
|
||||
public stop() {}
|
||||
|
||||
/**
|
||||
* Initialization is idempotent and required for (re)creating indices and transforms.
|
||||
*/
|
||||
async initialize(core: CoreStart, taskManager: TaskManagerStartContract): Promise<void> {
|
||||
this.logger.debug('initialize');
|
||||
const esClient = core.elasticsearch.client.asInternalUser;
|
||||
await initializeCspIndices(esClient, this.logger);
|
||||
await initializeCspTransforms(esClient, this.logger);
|
||||
await scheduleFindingsStatsTask(taskManager, this.logger);
|
||||
this.#isInitialized = true;
|
||||
}
|
||||
|
||||
async uninstallResources(taskManager: TaskManagerStartContract, logger: Logger): Promise<void> {
|
||||
|
|
|
@ -24,9 +24,11 @@ import { defineGetCspStatusRoute } from './status/status';
|
|||
export function setupRoutes({
|
||||
core,
|
||||
logger,
|
||||
isPluginInitialized,
|
||||
}: {
|
||||
core: CoreSetup<CspServerPluginStartDeps, CspServerPluginStart>;
|
||||
logger: Logger;
|
||||
isPluginInitialized(): boolean;
|
||||
}) {
|
||||
const router = core.http.createRouter<CspRequestHandlerContext>();
|
||||
defineGetComplianceDashboardRoute(router);
|
||||
|
@ -57,6 +59,7 @@ export function setupRoutes({
|
|||
agentService: fleet.agentService,
|
||||
packagePolicyService: fleet.packagePolicyService,
|
||||
packageService: fleet.packageService,
|
||||
isPluginInitialized,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -119,6 +119,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 0,
|
||||
healthyAgents: 0,
|
||||
installedPackageVersion: undefined,
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -159,6 +160,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 3,
|
||||
healthyAgents: 0,
|
||||
installedPackageVersion: '0.0.14',
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -208,6 +210,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 3,
|
||||
healthyAgents: 1,
|
||||
installedPackageVersion: '0.0.14',
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -245,6 +248,7 @@ describe('CspSetupStatus route', () => {
|
|||
latestPackageVersion: '0.0.14',
|
||||
installedPackagePolicies: 0,
|
||||
healthyAgents: 0,
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -295,6 +299,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 1,
|
||||
healthyAgents: 0,
|
||||
installedPackageVersion: '0.0.14',
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -352,6 +357,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 1,
|
||||
healthyAgents: 1,
|
||||
installedPackageVersion: '0.0.14',
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -408,6 +414,7 @@ describe('CspSetupStatus route', () => {
|
|||
installedPackagePolicies: 1,
|
||||
healthyAgents: 1,
|
||||
installedPackageVersion: '0.0.14',
|
||||
isPluginInitialized: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { SavedObjectsClientContract } from '@kbn/core/server';
|
|||
import type { AgentPolicyServiceInterface, AgentService } from '@kbn/fleet-plugin/server';
|
||||
import moment from 'moment';
|
||||
import { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, STATUS_ROUTE_PATH } from '../../../common/constants';
|
||||
import type { CspApiRequestHandlerContext, CspRouter } from '../../types';
|
||||
import type { CspSetupStatus, CspStatusCode } from '../../../common/types';
|
||||
|
@ -73,6 +74,7 @@ const getCspStatus = async ({
|
|||
packagePolicyService,
|
||||
agentPolicyService,
|
||||
agentService,
|
||||
isPluginInitialized,
|
||||
}: CspApiRequestHandlerContext): Promise<CspSetupStatus> => {
|
||||
const [hasFindings, installation, latestCspPackage, installedPackagePolicies] = await Promise.all(
|
||||
[
|
||||
|
@ -109,6 +111,7 @@ const getCspStatus = async ({
|
|||
latestPackageVersion: latestCspPackageVersion,
|
||||
healthyAgents,
|
||||
installedPackagePolicies: installedPackagePoliciesTotal,
|
||||
isPluginInitialized: isPluginInitialized(),
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -117,21 +120,37 @@ const getCspStatus = async ({
|
|||
healthyAgents,
|
||||
installedPackagePolicies: installedPackagePoliciesTotal,
|
||||
installedPackageVersion: installation?.install_version,
|
||||
isPluginInitialized: isPluginInitialized(),
|
||||
};
|
||||
};
|
||||
|
||||
export const statusQueryParamsSchema = schema.object({
|
||||
/**
|
||||
* CSP Plugin initialization includes creating indices/transforms/tasks.
|
||||
* Prior to this initialization, the plugin is not ready to index findings.
|
||||
*/
|
||||
check: schema.oneOf([schema.literal('all'), schema.literal('init')], { defaultValue: 'all' }),
|
||||
});
|
||||
|
||||
export const defineGetCspStatusRoute = (router: CspRouter): void =>
|
||||
router.get(
|
||||
{
|
||||
path: STATUS_ROUTE_PATH,
|
||||
validate: false,
|
||||
validate: { query: statusQueryParamsSchema },
|
||||
options: {
|
||||
tags: ['access:cloud-security-posture-read'],
|
||||
},
|
||||
},
|
||||
async (context, _, response) => {
|
||||
async (context, request, response) => {
|
||||
const cspContext = await context.csp;
|
||||
try {
|
||||
if (request.query.check === 'init') {
|
||||
return response.ok({
|
||||
body: {
|
||||
isPluginInitialized: cspContext.isPluginInitialized(),
|
||||
},
|
||||
});
|
||||
}
|
||||
const status = await getCspStatus(cspContext);
|
||||
return response.ok({
|
||||
body: status,
|
||||
|
|
|
@ -72,6 +72,7 @@ export interface CspApiRequestHandlerContext {
|
|||
agentService: AgentService;
|
||||
packagePolicyService: PackagePolicyClient;
|
||||
packageService: PackageService;
|
||||
isPluginInitialized(): boolean;
|
||||
}
|
||||
|
||||
export type CspRequestHandlerContext = CustomRequestHandlerContext<{
|
||||
|
|
34
x-pack/test/cloud_security_posture_functional/config.ts
Normal file
34
x-pack/test/cloud_security_posture_functional/config.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { resolve } from 'path';
|
||||
import type { FtrConfigProviderContext } from '@kbn/test';
|
||||
import { pageObjects } from './page_objects';
|
||||
import { getPreConfiguredFleetPackages, getPreConfiguredAgentPolicies } from './helpers';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const xpackFunctionalConfig = await readConfigFile(
|
||||
require.resolve('../functional/config.base.js')
|
||||
);
|
||||
|
||||
return {
|
||||
...xpackFunctionalConfig.getAll(),
|
||||
pageObjects,
|
||||
testFiles: [resolve(__dirname, './pages')],
|
||||
junit: {
|
||||
reportName: 'X-Pack Cloud Security Posture Functional Tests',
|
||||
},
|
||||
kbnTestServer: {
|
||||
...xpackFunctionalConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...xpackFunctionalConfig.get('kbnTestServer.serverArgs'),
|
||||
...getPreConfiguredFleetPackages(),
|
||||
...getPreConfiguredAgentPolicies(),
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
13
x-pack/test/cloud_security_posture_functional/ftr_provider_context.d.ts
vendored
Normal file
13
x-pack/test/cloud_security_posture_functional/ftr_provider_context.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { GenericFtrProviderContext } from '@kbn/test';
|
||||
|
||||
import { pageObjects } from './page_objects';
|
||||
import { services } from '../functional/services';
|
||||
|
||||
export type FtrProviderContext = GenericFtrProviderContext<typeof services, typeof pageObjects>;
|
27
x-pack/test/cloud_security_posture_functional/helpers.ts
Normal file
27
x-pack/test/cloud_security_posture_functional/helpers.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const CLOUD_SECURITY_POSTURE_PACKAGE_NAME = 'cloud_security_posture';
|
||||
|
||||
/**
|
||||
* flags to load kibana with fleet pre-configured to have 'cloud_security_posture' integration installed
|
||||
*/
|
||||
export const getPreConfiguredFleetPackages = () => [
|
||||
`--xpack.fleet.packages.0.name=${CLOUD_SECURITY_POSTURE_PACKAGE_NAME}`,
|
||||
`--xpack.fleet.packages.0.version=latest`,
|
||||
];
|
||||
|
||||
/**
|
||||
* flags to load kibana with pre-configured agent policy with a 'cloud_security_posture' package policy
|
||||
*/
|
||||
export const getPreConfiguredAgentPolicies = () => [
|
||||
`--xpack.fleet.agentPolicies.0.id=agent-policy-csp`,
|
||||
`--xpack.fleet.agentPolicies.0.name=example-agent-policy-csp`,
|
||||
`--xpack.fleet.agentPolicies.0.package_policies.0.id=integration-policy-csp`,
|
||||
`--xpack.fleet.agentPolicies.0.package_policies.0.name=example-integration-csp`,
|
||||
`--xpack.fleet.agentPolicies.0.package_policies.0.package.name=${CLOUD_SECURITY_POSTURE_PACKAGE_NAME}`,
|
||||
];
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 STATUS_API_PATH = '/internal/cloud_security_posture/status?check=init';
|
||||
const FINDINGS_INDEX = 'logs-cloud_security_posture.findings_latest-default';
|
||||
const FINDINGS_ROUTE = 'cloud_security_posture/findings';
|
||||
const FINDINGS_TABLE_TESTID = 'findings_table';
|
||||
const getFilterValueSelector = (columnIndex: number) =>
|
||||
`tbody tr td:nth-child(${columnIndex + 1}) div[data-test-subj="filter_cell_value"]`;
|
||||
|
||||
// Defined in Security Solution plugin
|
||||
const SECURITY_SOLUTION_APP_NAME = 'securitySolution';
|
||||
|
||||
export function FindingsPageProvider({ 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(STATUS_API_PATH).expect(200);
|
||||
expect(response.body).to.eql({ isPluginInitialized: true });
|
||||
log.debug('CSP plugin is initialized');
|
||||
});
|
||||
|
||||
const index = {
|
||||
remove: () => es.indices.delete({ index: FINDINGS_INDEX, ignore_unavailable: true }),
|
||||
add: async <T>(findingsMock: T[]) => {
|
||||
await waitForPluginInitialized();
|
||||
await Promise.all(
|
||||
findingsMock.map((finding) =>
|
||||
es.index({
|
||||
index: FINDINGS_INDEX,
|
||||
body: finding,
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const table = {
|
||||
getTableElement: () => testSubjects.find(FINDINGS_TABLE_TESTID),
|
||||
|
||||
getColumnIndex: async (columnName: string) => {
|
||||
const tableElement = await table.getTableElement();
|
||||
const headers = await tableElement.findAllByCssSelector('thead tr :is(th,td)');
|
||||
const headerIndexes = await Promise.all(headers.map((header) => header.getVisibleText()));
|
||||
const columnIndex = headerIndexes.findIndex((i) => i === columnName);
|
||||
expect(columnIndex).to.be.greaterThan(-1);
|
||||
return [columnIndex, headers[columnIndex]] as [
|
||||
number,
|
||||
Awaited<ReturnType<typeof testSubjects.find>>
|
||||
];
|
||||
},
|
||||
|
||||
getFilterColumnValues: async (columnName: string) => {
|
||||
const tableElement = await table.getTableElement();
|
||||
const [columnIndex] = await table.getColumnIndex(columnName);
|
||||
const columnCells = await tableElement.findAllByCssSelector(
|
||||
getFilterValueSelector(columnIndex)
|
||||
);
|
||||
|
||||
return await Promise.all(columnCells.map((h) => h.getVisibleText()));
|
||||
},
|
||||
|
||||
assertColumnSort: async (columnName: string, direction: 'asc' | 'desc') => {
|
||||
const values = (await table.getFilterColumnValues(columnName)).filter(Boolean);
|
||||
expect(values).to.not.be.empty();
|
||||
const sorted = values
|
||||
.slice()
|
||||
.sort((a, b) => (direction === 'asc' ? a.localeCompare(b) : b.localeCompare(a)));
|
||||
values.every((value, i) => expect(value).to.be(sorted[i]));
|
||||
},
|
||||
|
||||
toggleColumnSortOrFail: async (columnName: string, direction: 'asc' | 'desc') => {
|
||||
const getColumnElement = async () => (await table.getColumnIndex(columnName))[1];
|
||||
const element = await getColumnElement();
|
||||
const currentSort = await element.getAttribute('aria-sort');
|
||||
if (currentSort === 'none') {
|
||||
// a click is needed to focus on Eui column header
|
||||
await element.click();
|
||||
|
||||
// default is ascending
|
||||
if (direction === 'desc') {
|
||||
const nonStaleElement = await getColumnElement();
|
||||
await nonStaleElement.click();
|
||||
}
|
||||
}
|
||||
if (
|
||||
(currentSort === 'ascending' && direction === 'desc') ||
|
||||
(currentSort === 'descending' && direction === 'asc')
|
||||
) {
|
||||
// Without getting the element again, the click throws an error (stale element reference)
|
||||
const nonStaleElement = await getColumnElement();
|
||||
await nonStaleElement.click();
|
||||
}
|
||||
await table.assertColumnSort(columnName, direction);
|
||||
},
|
||||
};
|
||||
|
||||
const navigateToFindingsPage = async () => {
|
||||
await PageObjects.common.navigateToUrl(SECURITY_SOLUTION_APP_NAME, FINDINGS_ROUTE, {
|
||||
shouldUseHashForSubUrl: false,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
navigateToFindingsPage,
|
||||
table,
|
||||
index,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects';
|
||||
import { FindingsPageProvider } from './findings_page';
|
||||
|
||||
export const pageObjects = {
|
||||
...xpackFunctionalPageObjects,
|
||||
findings: FindingsPageProvider,
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['common', 'findings']);
|
||||
const FINDINGS_SIZE = 2;
|
||||
const findingsMock = Array.from({ length: FINDINGS_SIZE }, (_, id) => {
|
||||
return {
|
||||
resource: { id, name: `Resource ${id}` },
|
||||
result: { evaluation: 'passed' },
|
||||
rule: {
|
||||
name: `Rule ${id}`,
|
||||
section: 'Kubelet',
|
||||
tags: ['Kubernetes'],
|
||||
type: 'process',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('Findings Page', () => {
|
||||
before(async () => {
|
||||
await pageObjects.findings.index.add(findingsMock);
|
||||
await pageObjects.findings.navigateToFindingsPage();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await pageObjects.findings.index.remove();
|
||||
});
|
||||
|
||||
describe('Sort', () => {
|
||||
it('Sorts by rule name', async () => {
|
||||
await pageObjects.findings.table.toggleColumnSortOrFail('Rule', 'asc');
|
||||
});
|
||||
|
||||
it('Sorts by resource name', async () => {
|
||||
await pageObjects.findings.table.toggleColumnSortOrFail('Resource Name', 'desc');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
15
x-pack/test/cloud_security_posture_functional/pages/index.ts
Normal file
15
x-pack/test/cloud_security_posture_functional/pages/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Cloud Security Posture', function () {
|
||||
loadTestFile(require.resolve('./findings'));
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue