mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][API testing] Move and restructures Basic detection engine tests (#171531)
## Summary Following the initial work in this https://github.com/elastic/kibana/pull/166755 - Addresses part of https://github.com/elastic/kibana/issues/151902 for basic detection engine-related tests - Introduced a new license folder to hold the `Basic` Ess tests and the `Essentials` Serverless tests, is called `basic_essentials_license` - Added new base configurations files for `serverless/config.base.essentials` and `ess/config.base.basic` - Moved the utility files associated with Basic tests to the new directory `security_solution_api_integration`. Files not actively used in the previous folder were moved, while duplicate files remained in their original positions. - Updated the CodeOwner file for the newly moved tests - Old/new group details, decisions, and execution time are mentioned in this [document](https://docs.google.com/document/d/1CRFfDWMzw3ob03euWIvT4-IoiLXjoiPWI8mTBqP4Zks/edit) - The **Privileges** Tests are skipped in Serverless now until the FTR Roles [PR](https://github.com/elastic/kibana/pull/170131) gets merged | Action | File | New Path | |--------|------|----------| | Moved|basic/create_rules| basic_essentials_license/detection_engine/rules/create_rules| | Moved|basic/create_rules| basic_essentials_license/detection_engine/rules/create_ml_rules_privileges| | Moved|basic/create_rules| basic_essentials_license/detection_engine/alerts/open_close_alerts| | Moved|basic/create_rules| basic_essentials_license/detection_engine/alerts/query_alerts_backword_compatibility| | Moved|basic/create_rules| basic_essentials_license/detection_engine/alerts/query_alerts|
This commit is contained in:
parent
11acc025b5
commit
5fa20cc3a7
23 changed files with 9616 additions and 173 deletions
|
@ -14,7 +14,9 @@ disabled:
|
|||
- x-pack/test/api_integration/config.ts
|
||||
- x-pack/test/fleet_api_integration/config.base.ts
|
||||
- x-pack/test/security_solution_api_integration/config/ess/config.base.ts
|
||||
- x-pack/test/security_solution_api_integration/config/ess/config.base.basic.ts
|
||||
- x-pack/test/security_solution_api_integration/config/serverless/config.base.ts
|
||||
- x-pack/test/security_solution_api_integration/config/serverless/config.base.essentials.ts
|
||||
- x-pack/test/security_solution_endpoint/config.base.ts
|
||||
- x-pack/test/security_solution_endpoint_api_int/config.base.ts
|
||||
|
||||
|
@ -486,3 +488,6 @@ enabled:
|
|||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine/configs/ess.config.ts
|
||||
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -1395,6 +1395,7 @@ x-pack/test/security_solution_api_integration/test_suites/detections_response/de
|
|||
x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions @elastic/security-detection-engine
|
||||
x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/alerts @elastic/security-detection-engine
|
||||
x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles @elastic/security-detection-engine
|
||||
x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine @elastic/security-detection-engine
|
||||
/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users @elastic/security-detection-engine
|
||||
|
||||
## Security Threat Intelligence - Under Security Platform
|
||||
|
|
|
@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context';
|
|||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('detection engine api basic license', function () {
|
||||
loadTestFile(require.resolve('./create_rules'));
|
||||
loadTestFile(require.resolve('./create_rules_bulk'));
|
||||
loadTestFile(require.resolve('./delete_rules'));
|
||||
loadTestFile(require.resolve('./delete_rules_bulk'));
|
||||
|
@ -22,8 +21,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
|
|||
loadTestFile(require.resolve('./update_rules_bulk'));
|
||||
loadTestFile(require.resolve('./patch_rules_bulk'));
|
||||
loadTestFile(require.resolve('./patch_rules'));
|
||||
loadTestFile(require.resolve('./query_signals'));
|
||||
loadTestFile(require.resolve('./open_close_signals'));
|
||||
loadTestFile(require.resolve('./import_timelines'));
|
||||
loadTestFile(require.resolve('./coverage_overview'));
|
||||
});
|
||||
|
|
|
@ -44,7 +44,6 @@ export * from './get_rule_for_signal_testing_with_timestamp_override';
|
|||
export * from './get_rule_with_web_hook_action';
|
||||
export * from './get_rule_with_legacy_investigation_fields';
|
||||
export * from './get_saved_query_rule_for_signal_testing';
|
||||
export * from './get_signal_status';
|
||||
export * from './get_signals_by_id';
|
||||
export * from './get_signals_by_ids';
|
||||
export * from './get_signals_by_rule_ids';
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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 { createTestConfig } from './config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
license: 'basic',
|
||||
ssl: true,
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
export interface CreateTestConfigOptions {
|
||||
testFiles: string[];
|
||||
junit: { reportName: string };
|
||||
kbnTestServerArgs?: string[];
|
||||
kbnTestServerEnv?: Record<string, string>;
|
||||
}
|
||||
import { services } from '../../../../test_serverless/api_integration/services';
|
||||
|
||||
export function createTestConfig(options: CreateTestConfigOptions) {
|
||||
return async ({ readConfigFile }: FtrConfigProviderContext) => {
|
||||
const svlSharedConfig = await readConfigFile(
|
||||
require.resolve('../../../../test_serverless/shared/config.base.ts')
|
||||
);
|
||||
return {
|
||||
...svlSharedConfig.getAll(),
|
||||
services: {
|
||||
...services,
|
||||
},
|
||||
kbnTestServer: {
|
||||
...svlSharedConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...svlSharedConfig.get('kbnTestServer.serverArgs'),
|
||||
'--serverless=security',
|
||||
`--xpack.securitySolutionServerless.productTypes=${JSON.stringify([
|
||||
{ product_line: 'security', product_tier: 'essentials' },
|
||||
{ product_line: 'endpoint', product_tier: 'essentials' },
|
||||
])}`,
|
||||
...(options.kbnTestServerArgs || []),
|
||||
],
|
||||
env: {
|
||||
...svlSharedConfig.get('kbnTestServer.env'),
|
||||
...options.kbnTestServerEnv,
|
||||
},
|
||||
},
|
||||
testFiles: options.testFiles,
|
||||
junit: options.junit,
|
||||
|
||||
mochaOpts: {
|
||||
...svlSharedConfig.get('mochaOpts'),
|
||||
grep: '/^(?!.*@brokenInServerless).*@serverless.*/',
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
|
@ -7,6 +7,8 @@
|
|||
"scripts": {
|
||||
"initialize-server:dr:default": "node ./scripts/index.js server detections_response default_license",
|
||||
"run-tests:dr:default": "node ./scripts/index.js runner detections_response default_license",
|
||||
"initialize-server:dr:basicEssentials": "node ./scripts/index.js server detections_response basic_essentials_license",
|
||||
"run-tests:dr:basicEssentials": "node ./scripts/index.js runner detections_response basic_essentials_license",
|
||||
"initialize-server:ea:default": "node ./scripts/index.js server entity_analytics default_license",
|
||||
"run-tests:ea:default": "node ./scripts/index.js runner entity_analytics default_license",
|
||||
"exception_workflows:server:serverless": "npm run initialize-server:dr:default exceptions/workflows serverless",
|
||||
|
@ -98,6 +100,11 @@
|
|||
"telemetry:runner:serverless": "npm run run-tests:dr:default telemetry serverless serverlessEnv",
|
||||
"telemetry:qa:serverless": "npm run run-tests:dr:default telemetry serverless qaEnv",
|
||||
"telemetry:server:ess": "npm run initialize-server:dr:default telemetry ess",
|
||||
"telemetry:runner:ess": "npm run run-tests:dr:default telemetry ess essEnv"
|
||||
"telemetry:runner:ess": "npm run run-tests:dr:default telemetry ess essEnv",
|
||||
"detection_engine_basicessentionals:server:serverless": "npm run initialize-server:dr:basicEssentials detection_engine serverless",
|
||||
"detection_engine_basicessentionals:runner:serverless": "npm run run-tests:dr:basicEssentials detection_engine serverless serverlessEnv",
|
||||
"detection_engine_basicessentionals:qa:serverless": "npm run run-tests:dr:basicEssentials detection_engine serverless qaEnv",
|
||||
"detection_engine_basicessentionals:server:ess": "npm run initialize-server:dr:basicEssentials detection_engine ess",
|
||||
"detection_engine_basicessentionals:runner:ess": "npm run run-tests:dr:basicEssentials detection_engine ess essEnv"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,40 +14,45 @@ import {
|
|||
DETECTION_ENGINE_QUERY_SIGNALS_URL,
|
||||
} from '@kbn/security-solution-plugin/common/constants';
|
||||
import { DetectionAlert } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import {
|
||||
createSignalsIndex,
|
||||
setSignalStatus,
|
||||
getQuerySignalIds,
|
||||
createAlertsIndex,
|
||||
setAlertStatus,
|
||||
getQueryAlertIds,
|
||||
deleteAllRules,
|
||||
createRule,
|
||||
waitForSignalsToBePresent,
|
||||
getSignalsByIds,
|
||||
waitForAlertsToBePresent,
|
||||
getAlertsByIds,
|
||||
waitForRuleSuccess,
|
||||
getRuleForSignalTesting,
|
||||
getRuleForAlertTesting,
|
||||
deleteAllAlerts,
|
||||
} from '../../utils';
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditbeatHost = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('open_close_signals', () => {
|
||||
describe('@ess @serverless open_close_alerts', () => {
|
||||
describe('tests with auditbeat data', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(auditbeatHost);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(auditbeatHost);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -55,94 +60,94 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
it('should be able to execute and get 10 signals', async () => {
|
||||
it('should be able to execute and get 10 alerts', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'process.executable: "/usr/bin/sudo"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 10, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
expect(signalsOpen.hits.hits.length).equal(10);
|
||||
await waitForAlertsToBePresent(supertest, log, 10, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
expect(alertsOpen.hits.hits.length).equal(10);
|
||||
});
|
||||
|
||||
it('should be have set the signals in an open state initially', async () => {
|
||||
it('should be have set the alerts in an open state initially', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'process.executable: "/usr/bin/sudo"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 10, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const everySignalOpen = signalsOpen.hits.hits.every(
|
||||
await waitForAlertsToBePresent(supertest, log, 10, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const everyAlertOpen = alertsOpen.hits.hits.every(
|
||||
(hit) => hit._source?.[ALERT_WORKFLOW_STATUS] === 'open'
|
||||
);
|
||||
expect(everySignalOpen).to.eql(true);
|
||||
expect(everyAlertOpen).to.eql(true);
|
||||
});
|
||||
|
||||
it('should be able to get a count of 10 closed signals when closing 10', async () => {
|
||||
it('should be able to get a count of 10 closed alerts when closing 10', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'process.executable: "/usr/bin/sudo"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 10, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const signalIds = signalsOpen.hits.hits.map((signal) => signal._id);
|
||||
await waitForAlertsToBePresent(supertest, log, 10, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const alertIds = alertsOpen.hits.hits.map((alert) => alert._id);
|
||||
|
||||
// set all of the signals to the state of closed. There is no reason to use a waitUntil here
|
||||
// set all of the alerts to the state of closed. There is no reason to use a waitUntil here
|
||||
// as this route intentionally has a waitFor within it and should only return when the query has
|
||||
// the data.
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_SIGNALS_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(setSignalStatus({ signalIds, status: 'closed' }))
|
||||
.send(setAlertStatus({ alertIds, status: 'closed' }))
|
||||
.expect(200);
|
||||
|
||||
const { body: signalsClosed }: { body: estypes.SearchResponse<DetectionAlert> } =
|
||||
const { body: alertsClosed }: { body: estypes.SearchResponse<DetectionAlert> } =
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getQuerySignalIds(signalIds))
|
||||
.send(getQueryAlertIds(alertIds))
|
||||
.expect(200);
|
||||
expect(signalsClosed.hits.hits.length).to.equal(10);
|
||||
expect(alertsClosed.hits.hits.length).to.equal(10);
|
||||
});
|
||||
|
||||
// Test is failing after changing refresh to false
|
||||
it.skip('should be able close 10 signals immediately and they all should be closed', async () => {
|
||||
it.skip('should be able close 10 alerts immediately and they all should be closed', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'process.executable: "/usr/bin/sudo"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 10, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const signalIds = signalsOpen.hits.hits.map((signal) => signal._id);
|
||||
await waitForAlertsToBePresent(supertest, log, 10, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const alertIds = alertsOpen.hits.hits.map((alert) => alert._id);
|
||||
|
||||
// set all of the signals to the state of closed. There is no reason to use a waitUntil here
|
||||
// set all of the alerts to the state of closed. There is no reason to use a waitUntil here
|
||||
// as this route intentionally has a waitFor within it and should only return when the query has
|
||||
// the data.
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_SIGNALS_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(setSignalStatus({ signalIds, status: 'closed' }))
|
||||
.send(setAlertStatus({ alertIds, status: 'closed' }))
|
||||
.expect(200);
|
||||
|
||||
const { body: signalsClosed }: { body: estypes.SearchResponse<DetectionAlert> } =
|
||||
const { body: alertsClosed }: { body: estypes.SearchResponse<DetectionAlert> } =
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getQuerySignalIds(signalIds))
|
||||
.send(getQueryAlertIds(alertIds))
|
||||
.expect(200);
|
||||
|
||||
const everySignalClosed = signalsClosed.hits.hits.every(
|
||||
const everyAlertClosed = alertsClosed.hits.hits.every(
|
||||
(hit) => hit._source?.[ALERT_WORKFLOW_STATUS] === 'closed'
|
||||
);
|
||||
expect(everySignalClosed).to.eql(true);
|
||||
expect(everyAlertClosed).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,25 +11,25 @@ import {
|
|||
DETECTION_ENGINE_QUERY_SIGNALS_URL,
|
||||
ALERTS_AS_DATA_FIND_URL,
|
||||
} from '@kbn/security-solution-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { getSignalStatus, createSignalsIndex, deleteAllAlerts } from '../../utils';
|
||||
import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common';
|
||||
import { getAlertStatus, createAlertsIndex, deleteAllAlerts } from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
|
||||
describe('query_signals_route and find_alerts_route', () => {
|
||||
describe('@ess @serverless query_signals_route and find_alerts_route', () => {
|
||||
describe('validation checks', () => {
|
||||
// This fails and should be investigated or removed if it no longer applies
|
||||
it.skip('should not give errors when querying and the signals index does exist and is empty', async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
it.skip('should not give errors when querying and the alerts index does exist and is empty', async () => {
|
||||
await createAlertsIndex(supertest, log);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSignalStatus())
|
||||
.send(getAlertStatus())
|
||||
.expect(200);
|
||||
|
||||
// remove any server generated items that are indeterministic
|
||||
|
@ -48,56 +48,19 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('backwards compatibility', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await createSignalsIndex(supertest, log);
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
});
|
||||
|
||||
it('should be able to filter old signals on host.os.name.caseless using runtime field', async () => {
|
||||
const query = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [{ match_phrase: { 'host.os.name.caseless': 'windows' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(query)
|
||||
.expect(200);
|
||||
expect(body.hits.total.value).to.eql(3);
|
||||
});
|
||||
|
||||
it('should be able to filter old signals using field aliases', async () => {
|
||||
const query = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(query)
|
||||
.expect(200);
|
||||
expect(body.hits.total.value).to.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('runtime fields', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await createSignalsIndex(supertest, log);
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs',
|
||||
{
|
||||
useCreate: true,
|
||||
docsOnly: true,
|
||||
}
|
||||
);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
// await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
});
|
||||
|
||||
|
@ -129,12 +92,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
describe('find_alerts_route', () => {
|
||||
describe('validation checks', () => {
|
||||
// This fails and should be investigated or removed if it no longer applies
|
||||
it.skip('should not give errors when querying and the signals index does exist and is empty', async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
it.skip('should not give errors when querying and the alerts index does exist and is empty', async () => {
|
||||
await createAlertsIndex(supertest, log);
|
||||
const { body } = await supertest
|
||||
.post(ALERTS_AS_DATA_FIND_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ...getSignalStatus(), index: '.siem-signals-default' })
|
||||
.send({ ...getAlertStatus(), index: '.siem-signals-default' })
|
||||
.expect(200);
|
||||
|
||||
// remove any server generated items that are indeterministic
|
||||
|
@ -153,10 +116,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
it('should not give errors when executing security solution histogram aggs', async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
await supertest
|
||||
.post(ALERTS_AS_DATA_FIND_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||
.send({
|
||||
index: '.siem-signals-default',
|
||||
aggs: {
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { createAlertsIndex, deleteAllAlerts } from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
|
||||
describe('@ess query_alerts_backword_compatibility', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals');
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
});
|
||||
|
||||
it('should be able to filter old alerts on host.os.name.caseless using runtime field', async () => {
|
||||
const query = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [{ match_phrase: { 'host.os.name.caseless': 'windows' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(query)
|
||||
.expect(200);
|
||||
expect(body.hits.total.value).to.eql(3);
|
||||
});
|
||||
|
||||
it('should be able to filter old alerts using field aliases', async () => {
|
||||
const query = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(query)
|
||||
.expect(200);
|
||||
expect(body.hits.total.value).to.eql(3);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../config/ess/config.base.basic')
|
||||
);
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName: 'Detection Engine ESS - Basic Integration Tests',
|
||||
},
|
||||
};
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../config/serverless/config.base.essentials';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName: 'Detection Engine Serverless - Essentials Integration Tests',
|
||||
},
|
||||
});
|
|
@ -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';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Detection Engine Basic and Essentials API', function () {
|
||||
loadTestFile(require.resolve('./rules/create_rules'));
|
||||
loadTestFile(require.resolve('./rules/create_ml_rules_privileges'));
|
||||
loadTestFile(require.resolve('./alerts/open_close_alerts'));
|
||||
loadTestFile(require.resolve('./alerts/query_alerts'));
|
||||
loadTestFile(require.resolve('./alerts/query_alerts_backword_compatibility'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 'expect';
|
||||
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
|
||||
import {
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
removeServerGeneratedProperties,
|
||||
getSimpleMlRule,
|
||||
deleteAllAlerts,
|
||||
updateUsername,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditbeatPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('create_ml_rules', () => {
|
||||
describe('Creating Machine Learning rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load(auditbeatPath);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload(auditbeatPath);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
it('@ess should give a 403 when trying to create a single Machine Learning rule since the license is basic', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleMlRule())
|
||||
.expect(403);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).toEqual({
|
||||
message: 'Your license does not support machine learning. Please upgrade your license.',
|
||||
status_code: 403,
|
||||
});
|
||||
});
|
||||
it('@serverless should give a 200 when trying to create a single Machine Learning rule since the license is essentials', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleMlRule())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
const expectedRule = updateUsername(getSimpleMlRule(), ELASTICSEARCH_USERNAME);
|
||||
expect(bodyToCompare).toEqual(expect.objectContaining(expectedRule));
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -6,42 +6,49 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||
import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
import {
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
getSimpleRule,
|
||||
getSimpleRuleOutput,
|
||||
getSimpleRuleOutputWithoutRuleId,
|
||||
getSimpleRuleWithoutRuleId,
|
||||
removeServerGeneratedProperties,
|
||||
removeServerGeneratedPropertiesIncludingRuleId,
|
||||
getSimpleMlRule,
|
||||
deleteAllAlerts,
|
||||
} from '../../utils';
|
||||
updateUsername,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditbeatPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('create_rules', () => {
|
||||
describe('@ess @serverless create_rules', () => {
|
||||
describe('creating rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(auditbeatPath);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(auditbeatPath);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -53,12 +60,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutput());
|
||||
const expectedRule = updateUsername(bodyToCompare, ELASTICSEARCH_USERNAME);
|
||||
|
||||
expect(bodyToCompare).to.eql(expectedRule);
|
||||
});
|
||||
|
||||
it('should create a single rule without an input index', async () => {
|
||||
|
@ -72,90 +81,49 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
type: 'query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
};
|
||||
const expected = {
|
||||
actions: [],
|
||||
author: [],
|
||||
created_by: 'elastic',
|
||||
description: 'Simple Rule Query',
|
||||
enabled: true,
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
interval: '5m',
|
||||
rule_id: 'rule-1',
|
||||
language: 'kuery',
|
||||
output_index: '',
|
||||
max_signals: 100,
|
||||
risk_score: 1,
|
||||
risk_score_mapping: [],
|
||||
name: 'Simple Rule Query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: [],
|
||||
related_integrations: [],
|
||||
required_fields: [],
|
||||
setup: '',
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic',
|
||||
tags: [],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
exceptions_list: [],
|
||||
version: 1,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(rule)
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
const expectedRule = updateUsername(bodyToCompare, ELASTICSEARCH_USERNAME);
|
||||
|
||||
expect(bodyToCompare).to.eql(expectedRule);
|
||||
});
|
||||
|
||||
it('should create a single rule without a rule_id', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleRuleWithoutRuleId())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId());
|
||||
});
|
||||
const expectedRule = updateUsername(
|
||||
getSimpleRuleOutputWithoutRuleId(),
|
||||
ELASTICSEARCH_USERNAME
|
||||
);
|
||||
|
||||
it('should give a 403 when trying to create a single Machine Learning rule since the license is basic', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.send(getSimpleMlRule())
|
||||
.expect(403);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql({
|
||||
message: 'Your license does not support machine learning. Please upgrade your license.',
|
||||
status_code: 403,
|
||||
});
|
||||
expect(bodyToCompare).to.eql(expectedRule);
|
||||
});
|
||||
|
||||
it('should cause a 409 conflict if we attempt to create the same rule_id twice', async () => {
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31')
|
||||
.send(getSimpleRule())
|
||||
.expect(409);
|
||||
|
|
@ -5,6 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const getSignalStatus = () => ({
|
||||
export const getAlertStatus = () => ({
|
||||
aggs: { statuses: { terms: { field: 'kibana.alert.workflow_status', size: 10 } } },
|
||||
});
|
|
@ -20,4 +20,5 @@ export * from './get_alert_status_empty_response';
|
|||
export * from './get_query_alert_ids';
|
||||
export * from './set_alert_tags';
|
||||
export * from './get_preview_alerts';
|
||||
export * from './get_alert_status';
|
||||
export * from './migrations';
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { getSimpleRuleOutput } from './get_simple_rule_output';
|
||||
import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties';
|
||||
|
||||
/**
|
||||
* This is the typical output of a simple rule that Kibana will output with all the defaults except
|
||||
* for all the server generated properties such as created_by. Useful for testing end to end tests.
|
||||
*/
|
||||
export const getSimpleRuleOutputWithoutRuleId = (
|
||||
ruleId = 'rule-1'
|
||||
): Omit<RuleWithoutServerGeneratedProperties, 'rule_id'> => {
|
||||
const rule = getSimpleRuleOutput(ruleId);
|
||||
const { rule_id: rId, ...ruleWithoutRuleId } = rule;
|
||||
return ruleWithoutRuleId;
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { getSimpleRule } from './get_simple_rule';
|
||||
|
||||
/**
|
||||
* This is a typical simple rule for testing that is easy for most basic testing
|
||||
*/
|
||||
export const getSimpleRuleWithoutRuleId = (): RuleCreateProps => {
|
||||
const simpleRule = getSimpleRule();
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const { rule_id, ...ruleWithoutId } = simpleRule;
|
||||
return ruleWithoutId;
|
||||
};
|
|
@ -39,5 +39,8 @@ export * from './generate_event';
|
|||
export * from './create_legacy_rule_action';
|
||||
export * from './get_simple_threat_match';
|
||||
export * from './get_simple_ml_rule';
|
||||
export * from './remove_server_generated_properties_including_rule_id';
|
||||
export * from './get_simple_rule_output_without_rule_id';
|
||||
export * from './get_simple_rule_without_rule_id';
|
||||
|
||||
export * from './prebuilt_rules';
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
import { removeServerGeneratedProperties } from './remove_server_generated_properties';
|
||||
|
||||
/**
|
||||
* This will remove server generated properties such as date times, etc... including the rule_id
|
||||
* @param rule Rule to pass in to remove typical server generated properties
|
||||
*/
|
||||
export const removeServerGeneratedPropertiesIncludingRuleId = (
|
||||
rule: RuleResponse
|
||||
): Partial<RuleResponse> => {
|
||||
const ruleWithRemovedProperties = removeServerGeneratedProperties(rule);
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const { rule_id, ...additionalRuledIdRemoved } = ruleWithRemovedProperties;
|
||||
return additionalRuledIdRemoved;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue