[Security Solution][API testing] Move and restructures Telemetry logic (#171159)

## 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
Telemetry
- Moved the utility files associated with telemetry 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)

| Action | File | New Path if moved |
|--------|------|----------|
| Delete| security_and_spaces/group4| - |
|
Move|detection_engine_api_integration/security_and_spaces/group4|detections_response/default_license/telemetry/keyword_family|
This commit is contained in:
Wafaa Nasr 2023-11-16 19:12:16 +01:00 committed by GitHub
parent 3791a73dfa
commit 53a37f0415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 867 additions and 316 deletions

View file

@ -225,7 +225,6 @@ enabled:
- x-pack/test/dataset_quality_api_integration/basic/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/group4/config.ts
- x-pack/test/detection_engine_api_integration/security_and_spaces/group10/config.ts
- x-pack/test/disable_ems/config.ts
- x-pack/test/encrypted_saved_objects_api_integration/config.ts
@ -477,3 +476,5 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles/configs/serverless.config.ts
- 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

2
.github/CODEOWNERS vendored
View file

@ -1410,7 +1410,7 @@ x-pack/test/security_solution_api_integration/test_suites/detections_response/de
/x-pack/plugins/security_solution/server/routes @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/server/utils @elastic/security-detections-response @elastic/security-threat-hunting
x-pack/test/security_solution_api_integration/test_suites/detections_response/utils @elastic/security-detections-response
x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry @elastic/security-detections-response
## Security Solution sub teams - security-defend-workflows
/x-pack/plugins/security_solution/public/management/ @elastic/security-defend-workflows

View file

@ -1,15 +0,0 @@
/*
* 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 '../../common/ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default ({ loadTestFile }: FtrProviderContext): void => {
describe('detection engine api security and spaces enabled - Group 4', function () {
loadTestFile(require.resolve('./telemetry'));
});
};

View file

@ -1,34 +0,0 @@
/*
* 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 { ToolingLog } from '@kbn/tooling-log';
import type SuperTest from 'supertest';
import { getWebHookAction } from './get_web_hook_action';
/**
* Helper to cut down on the noise in some of the tests. This
* creates a new action and expects a 200 and does not do any retries.
* @param supertest The supertest deps
*/
export const createNewAction = async (
supertest: SuperTest.SuperTest<SuperTest.Test>,
log: ToolingLog
) => {
const response = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction());
if (response.status !== 200) {
log.error(
`Did not get an expected 200 "ok" when creating a new action. CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify(
response.body
)}, status: ${JSON.stringify(response.status)}`
);
}
return response.body;
};

View file

@ -13,7 +13,6 @@ export * from './create_container_with_entries';
export * from './create_exception_list';
export * from './create_exception_list_item';
export * from './create_legacy_rule_action';
export * from './create_new_action';
export * from './create_rule';
export * from './create_rule_with_exception_entries';
export * from './create_rule_saved_object';
@ -25,12 +24,10 @@ export * from './delete_all_alerts';
export * from './delete_all_timelines';
export * from './delete_exception_list';
export * from './delete_rule';
export * from './downgrade_immutable_rule';
export * from './finalize_signals_migration';
export * from './find_immutable_rule_by_id';
export * from './get_complex_rule';
export * from './get_complex_rule_output';
export * from './get_detection_metrics_from_body';
export * from './get_eql_rule_for_signal_testing';
export * from './get_event_log_execute_complete_by_id';
export * from './get_legacy_action_notification_so';
@ -47,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_security_telemetry_stats';
export * from './get_signal_status';
export * from './get_signals_by_id';
export * from './get_signals_by_ids';
@ -62,9 +58,6 @@ export * from './get_simple_rule_output_without_rule_id';
export * from './get_simple_rule_update';
export * from './get_simple_rule_without_rule_id';
export * from './get_simple_saved_query_rule';
export * from './get_simple_threat_match';
export * from './get_stats';
export * from './get_stats_url';
export * from './get_threat_match_rule_for_signal_testing';
export * from './get_threshold_rule_for_signal_testing';
export * from './get_slack_action';
@ -76,7 +69,6 @@ export * from './preview_rule_with_exception_entries';
export * from './preview_rule';
export * from './refresh_index';
export * from './route_with_namespace';
export * from './remove_time_fields_from_telemetry_stats';
export * from './remove_server_generated_properties';
export * from './remove_server_generated_properties_including_rule_id';
export * from './resolve_simple_rule_output';

View file

@ -93,6 +93,11 @@
"user_roles:runner:serverless": "npm run run-tests:dr:default user_roles serverless serverlessEnv",
"user_roles:qa:serverless": "npm run run-tests:dr:default user_roles serverless qaEnv",
"user_roles:server:ess": "npm run initialize-server:dr:default user_roles ess",
"user_roles:runner:ess": "npm run run-tests:dr:default user_roles ess essEnv"
"user_roles:runner:ess": "npm run run-tests:dr:default user_roles ess essEnv",
"telemetry:server:serverless": "npm run initialize-server:dr:default telemetry serverless",
"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"
}
}

View file

@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext): void => {
expect(bundledInstallResponse._meta.install_source).toBe('bundled');
// Refresh ES indices to avoid race conditions between write and reading of indeces
// See implementation utility function at x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/install_prebuilt_rules_fleet_package.ts
// See implementation utility function at x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts
await es.indices.refresh({ index: ALL_SAVED_OBJECT_INDICES });
// Verify that status is updated after package installation

View file

@ -7,12 +7,16 @@
import { FtrConfigProviderContext } from '@kbn/test';
// eslint-disable-next-line import/no-default-export
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../config.base.ts'));
const functionalConfig = await readConfigFile(
require.resolve('../../../../../config/ess/config.base.trial')
);
return {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
testFiles: [require.resolve('..')],
junit: {
reportName: 'Detection Engine API Integration Tests - ESS - Telemetry',
},
};
}

View file

@ -0,0 +1,18 @@
/*
* 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';
export default createTestConfig({
testFiles: [require.resolve('..')],
junit: {
reportName: 'Detection Engine API Integration Tests - Serverless - Telemetry',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['previewTelemetryUrlEnabled'])}`,
],
});

View file

@ -4,15 +4,14 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../../../ftr_provider_context';
import { FtrProviderContext } from '../../../common/ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default ({ loadTestFile }: FtrProviderContext): void => {
describe('Detection rule type telemetry', function () {
loadTestFile(require.resolve('./usage_collector/all_types'));
loadTestFile(require.resolve('./usage_collector/detection_rules'));
loadTestFile(require.resolve('./usage_collector/detection_rule_status'));
loadTestFile(require.resolve('./usage_collector/detection_rules_legacy_action'));
loadTestFile(require.resolve('./task_based/all_types'));
loadTestFile(require.resolve('./task_based/detection_rules'));

View file

@ -6,17 +6,18 @@
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
import {
createSignalsIndex,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getSecurityTelemetryStats,
removeTimeFieldsFromTelemetryStats,
} from '../../../../utils';
import { deleteAllExceptions } from '../../../../../lists_api_integration/utils';
} from '../../../utils';
import { deleteAllExceptions } from '../../../../../../lists_api_integration/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');
@ -24,7 +25,7 @@ export default ({ getService }: FtrProviderContext) => {
const retry = getService('retry');
const es = getService('es');
describe('All task telemetry types generically', async () => {
describe('@ess @serverless All task telemetry types generically', async () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry');
});
@ -34,7 +35,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
});
afterEach(async () => {
@ -43,7 +44,7 @@ export default ({ getService }: FtrProviderContext) => {
await deleteAllExceptions(supertest, log);
});
it('should only have task metric values when no rules are running', async () => {
it('@skipInQA should only have task metric values when no rules are running', async () => {
await retry.try(async () => {
const stats = await getSecurityTelemetryStats(supertest, log);
removeTimeFieldsFromTelemetryStats(stats);

View file

@ -10,23 +10,22 @@
import expect from '@kbn/expect';
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
import {
createRule,
createSignalsIndex,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getRule,
getRuleForSignalTesting,
fetchRule,
getRuleForAlertTesting,
installMockPrebuiltRules,
getSecurityTelemetryStats,
createExceptionList,
createExceptionListItem,
removeTimeFieldsFromTelemetryStats,
} from '../../../../utils';
import { deleteAllExceptions } from '../../../../../lists_api_integration/utils';
} from '../../../utils';
import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils';
import { FtrProviderContext } from '../../../../../ftr_provider_context';
// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext) => {
const es = getService('es');
const supertest = getService('supertest');
@ -34,7 +33,7 @@ export default ({ getService }: FtrProviderContext) => {
const log = getService('log');
const retry = getService('retry');
describe('Detection rule task telemetry', async () => {
describe('@ess @serverless Detection rule task telemetry', async () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry');
});
@ -44,7 +43,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
});
afterEach(async () => {
@ -54,8 +53,8 @@ export default ({ getService }: FtrProviderContext) => {
});
describe('custom rules should never show any detection_rules telemetry data for each list type', () => {
it('should NOT give telemetry/stats for an exception list of type "detection"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@skipInQA should NOT give telemetry/stats for an exception list of type "detection"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
// create an exception list container of type "detection"
const { id, list_id, namespace_type, type } = await createExceptionList(supertest, log, {
@ -110,8 +109,8 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should NOT give telemetry/stats for an exception list of type "endpoint"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@skipInQA should NOT give telemetry/stats for an exception list of type "endpoint"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
// create an exception list container of type "detection"
const { id, list_id, namespace_type, type } = await createExceptionList(supertest, log, {
@ -166,8 +165,8 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should NOT give telemetry/stats for an exception list of type "endpoint_trusted_apps"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@skipInQA should NOT give telemetry/stats for an exception list of type "endpoint_trusted_apps"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
// create an exception list container of type "detection"
const { id, list_id, namespace_type, type } = await createExceptionList(supertest, log, {
@ -222,8 +221,8 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should NOT give telemetry/stats for an exception list of type "endpoint_events"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@skipInQA should NOT give telemetry/stats for an exception list of type "endpoint_events"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
// create an exception list container of type "detection"
const { id, list_id, namespace_type, type } = await createExceptionList(supertest, log, {
@ -278,8 +277,8 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should NOT give telemetry/stats for an exception list of type "endpoint_host_isolation_exceptions"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@skipInQA should NOT give telemetry/stats for an exception list of type "endpoint_host_isolation_exceptions"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
// create an exception list container of type "detection"
const { id, list_id, namespace_type, type } = await createExceptionList(supertest, log, {
@ -335,7 +334,7 @@ export default ({ getService }: FtrProviderContext) => {
});
});
describe('pre-built/immutable/elastic rules should show detection_rules telemetry data for each list type', () => {
describe('@skipInQA pre-built/immutable/elastic rules should show detection_rules telemetry data for each list type', () => {
beforeEach(async () => {
// install prepackaged rules to get immutable rules for testing
await installMockPrebuiltRules(supertest, es);
@ -368,7 +367,9 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule using "PATCH" endpoint
const { exceptions_list } = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const { exceptions_list } = await fetchRule(supertest, {
ruleId: ELASTIC_SECURITY_RULE_ID,
});
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -427,7 +428,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -504,7 +505,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -581,7 +582,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -658,7 +659,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -735,7 +736,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
@ -786,7 +787,7 @@ export default ({ getService }: FtrProviderContext) => {
});
});
describe('pre-built/immutable/elastic rules should show detection_rules telemetry data for multiple list items and types', () => {
describe('@skipInQA pre-built/immutable/elastic rules should show detection_rules telemetry data for multiple list items and types', () => {
beforeEach(async () => {
// install prepackaged rules to get immutable rules for testing
await installMockPrebuiltRules(supertest, es);
@ -836,7 +837,7 @@ export default ({ getService }: FtrProviderContext) => {
});
// add the exception list to the pre-built/immutable/elastic rule
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
await supertest
.patch(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')

View file

@ -11,19 +11,18 @@ import {
ENDPOINT_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
import {
createSignalsIndex,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getSecurityTelemetryStats,
createExceptionListItem,
createExceptionList,
removeTimeFieldsFromTelemetryStats,
} from '../../../../utils';
import { deleteAllExceptions } from '../../../../../lists_api_integration/utils';
} from '../../../utils';
import { deleteAllExceptions } from '../../../../../../lists_api_integration/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');
@ -32,7 +31,7 @@ export default ({ getService }: FtrProviderContext) => {
const es = getService('es');
// Failing: See https://github.com/elastic/kibana/issues/164334
describe.skip('Security lists task telemetry', async () => {
describe.skip('@ess @serverless Security lists task telemetry', async () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry');
});
@ -42,7 +41,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
// Calling stats endpoint once like this guarantees that the trusted applications and exceptions lists are created for us.
await getSecurityTelemetryStats(supertest, log);
});

View file

@ -7,10 +7,10 @@
import expect from '@kbn/expect';
import { getInitialDetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/get_initial_usage';
import type { FtrProviderContext } from '../../../../common/ftr_provider_context';
import { createSignalsIndex, deleteAllRules, deleteAllAlerts, getStats } from '../../../../utils';
// eslint-disable-next-line import/no-default-export
import { createAlertsIndex, deleteAllRules, deleteAllAlerts, getStats } from '../../../utils';
import { FtrProviderContext } from '../../../../../ftr_provider_context';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
@ -18,7 +18,7 @@ export default ({ getService }: FtrProviderContext) => {
const retry = getService('retry');
const es = getService('es');
describe('Detection rule telemetry', async () => {
describe('@ess @serverless @skipInQA Detection rule telemetry', async () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry');
});
@ -28,7 +28,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
});
afterEach(async () => {

View file

@ -22,21 +22,20 @@ import {
} from '@kbn/security-solution-plugin/server/usage/detections/rules/get_initial_usage';
import {
createRule,
createSignalsIndex,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getEqlRuleForSignalTesting,
getRuleForSignalTesting,
getEqlRuleForAlertTesting,
getRuleForAlertTesting,
getSimpleThreatMatch,
getStats,
getThresholdRuleForSignalTesting,
getThresholdRuleForAlertTesting,
waitForRuleSuccess,
waitForSignalsToBePresent,
waitForAlertsToBePresent,
deleteAllEventLogExecutionEvents,
} from '../../../../utils';
import type { FtrProviderContext } from '../../../../common/ftr_provider_context';
} 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');
@ -46,7 +45,7 @@ export default ({ getService }: FtrProviderContext) => {
// Note: We don't actually find signals well with ML tests at the moment so there are not tests for ML rule type for telemetry
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/132856
describe('Detection rule status telemetry', async () => {
describe('@ess @serverless Detection rule status telemetry', async () => {
before(async () => {
// Just in case other tests do not clean up the event logs, let us clear them now and here only once.
await deleteAllEventLogExecutionEvents(es, log);
@ -58,7 +57,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
});
afterEach(async () => {
@ -70,10 +69,10 @@ export default ({ getService }: FtrProviderContext) => {
describe('"kql" rule type', () => {
let stats: DetectionMetrics | undefined;
before(async () => {
const rule = getRuleForSignalTesting(['telemetry']);
const rule = getRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
// get the stats for all the tests where we at least have the expected "query" to reduce chances of flake by checking that at least one custom rule passed
await retry.try(async () => {
stats = await getStats(supertest, log);
@ -120,7 +119,7 @@ export default ({ getService }: FtrProviderContext) => {
expect(stats?.detection_rules.detection_rule_usage).to.eql(expectedRuleUsage);
});
it('should have zero values for "detection_rule_status.all_rules" rules that are not query based', () => {
it('@skipInQA should have zero values for "detection_rule_status.all_rules" rules that are not query based', () => {
expect(stats?.detection_rules.detection_rule_status.all_rules.eql).to.eql(
getInitialSingleEventMetric()
);
@ -172,7 +171,7 @@ export default ({ getService }: FtrProviderContext) => {
);
});
it('should have non zero values for "index_duration"', () => {
it('@skipInQA should have non zero values for "index_duration"', () => {
expect(
stats?.detection_rules.detection_rule_status.custom_rules.query.index_duration.max
).to.be.above(1);
@ -205,11 +204,11 @@ export default ({ getService }: FtrProviderContext) => {
).to.be.above(1);
});
it('should have non zero values for "succeeded"', () => {
it('@skipInQA should have non zero values for "succeeded"', () => {
expect(stats?.detection_rules.detection_rule_status.custom_rules.query.succeeded).to.eql(1);
});
it('should have non zero values for "succeeded", "index_duration", "search_duration" and "enrichment_duration"', () => {
it('@skipInQA should have non zero values for "succeeded", "index_duration", "search_duration" and "enrichment_duration"', () => {
expect(
stats?.detection_rules.detection_rule_status.custom_rules.query.index_duration.max
).to.be.above(1);
@ -257,10 +256,10 @@ export default ({ getService }: FtrProviderContext) => {
describe('"eql" rule type', () => {
let stats: DetectionMetrics | undefined;
before(async () => {
const rule = getEqlRuleForSignalTesting(['telemetry']);
const rule = getEqlRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
// get the stats for all the tests where we at least have the expected "query" to reduce chances of flake by checking that at least one custom rule passed
await retry.try(async () => {
stats = await getStats(supertest, log);
@ -445,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => {
let stats: DetectionMetrics | undefined;
before(async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry']),
...getThresholdRuleForAlertTesting(['telemetry']),
threshold: {
field: 'keyword',
value: 1,
@ -453,7 +452,7 @@ export default ({ getService }: FtrProviderContext) => {
};
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
// get the stats for all the tests where we at least have the expected "query" to reduce chances of flake by checking that at least one custom rule passed
await retry.try(async () => {
stats = await getStats(supertest, log);
@ -662,7 +661,7 @@ export default ({ getService }: FtrProviderContext) => {
};
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
// get the stats for all the tests where we at least have the expected "query" to reduce chances of flake by checking that at least one custom rule passed
await retry.try(async () => {
stats = await getStats(supertest, log);

View file

@ -14,34 +14,34 @@ import type {
import { getInitialDetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/get_initial_usage';
import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common';
import { RulesTypeUsage } from '@kbn/security-solution-plugin/server/usage/detections/rules/types';
import type { FtrProviderContext } from '../../../../common/ftr_provider_context';
import {
createLegacyRuleAction,
createNewAction,
createWebHookRuleAction,
createRule,
createSignalsIndex,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getEqlRuleForSignalTesting,
getRule,
getRuleForSignalTesting,
getEqlRuleForAlertTesting,
fetchRule,
getRuleForAlertTesting,
getRuleWithWebHookAction,
getSimpleMlRule,
getSimpleRule,
getSimpleThreatMatch,
getStats,
getThresholdRuleForSignalTesting,
getThresholdRuleForAlertTesting,
installMockPrebuiltRules,
waitForRuleSuccess,
waitForSignalsToBePresent,
waitForAlertsToBePresent,
updateRule,
deleteAllEventLogExecutionEvents,
getRuleSavedObjectWithLegacyInvestigationFields,
getRuleSavedObjectWithLegacyInvestigationFieldsEmptyArray,
createRuleThroughAlertingEndpoint,
} from '../../../../utils';
} 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');
@ -49,7 +49,7 @@ export default ({ getService }: FtrProviderContext) => {
const retry = getService('retry');
const es = getService('es');
describe('Detection rule telemetry', async () => {
describe('@ess @serverless Detection rule telemetry', async () => {
before(async () => {
// Just in case other tests do not clean up the event logs, let us clear them now and here only once.
await deleteAllEventLogExecutionEvents(es, log);
@ -61,7 +61,7 @@ export default ({ getService }: FtrProviderContext) => {
});
beforeEach(async () => {
await createSignalsIndex(supertest, log);
await createAlertsIndex(supertest, log);
});
afterEach(async () => {
@ -72,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => {
describe('"kql" rule type', () => {
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "disabled"/"in-active" rule that does not have any actions', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
await createRule(supertest, log, rule);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -102,10 +102,10 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "enabled"/"active" rule that does not have any actions', async () => {
const rule = getRuleForSignalTesting(['telemetry']);
const rule = getRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
@ -136,8 +136,8 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should show "notifications_disabled" to be "1" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getRuleForSignalTesting(['telemetry']);
const hookAction = await createNewAction(supertest, log);
const rule = getRuleForAlertTesting(['telemetry']);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, false, rule);
await createRule(supertest, log, ruleToCreate);
@ -160,13 +160,13 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForSignalTesting(['telemetry']);
const hookAction = await createNewAction(supertest, log);
it('@brokenInServerless should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForAlertTesting(['telemetry']);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
const { id } = await createRule(supertest, log, ruleToCreate);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -189,10 +189,10 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -215,13 +215,13 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForSignalTesting(['telemetry']);
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -244,7 +244,7 @@ export default ({ getService }: FtrProviderContext) => {
});
});
describe('legacy investigation fields', () => {
describe('@brokenInServerless legacy investigation fields', () => {
beforeEach(async () => {
await deleteAllRules(supertest, log);
await createRuleThroughAlertingEndpoint(
@ -294,7 +294,7 @@ export default ({ getService }: FtrProviderContext) => {
describe('"eql" rule type', () => {
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "disabled"/"in-active" rule that does not have any actions', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry'], 'rule-1', false);
const rule = getEqlRuleForAlertTesting(['telemetry'], 'rule-1', false);
await createRule(supertest, log, rule);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -322,10 +322,10 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "enabled"/"active" rule that does not have any actions', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry']);
const rule = getEqlRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
@ -356,8 +356,8 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should show "notifications_disabled" to be "1" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry']);
const hookAction = await createNewAction(supertest, log);
const rule = getEqlRuleForAlertTesting(['telemetry']);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, false, rule);
await createRule(supertest, log, ruleToCreate);
@ -381,12 +381,12 @@ export default ({ getService }: FtrProviderContext) => {
});
it('should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry']);
const hookAction = await createNewAction(supertest, log);
const rule = getEqlRuleForAlertTesting(['telemetry']);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
const { id } = await createRule(supertest, log, ruleToCreate);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -409,10 +409,10 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry'], 'rule-1', false);
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getEqlRuleForAlertTesting(['telemetry'], 'rule-1', false);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -434,13 +434,13 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getEqlRuleForSignalTesting(['telemetry']);
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getEqlRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => {
describe('"threshold" rule type', () => {
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "disabled"/"in-active" rule that does not have any actions', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry'], 'rule-1', false),
...getThresholdRuleForAlertTesting(['telemetry'], 'rule-1', false),
threshold: {
field: 'keyword',
value: 1,
@ -503,7 +503,7 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_enabled", "notifications_disabled" "legacy_notifications_enabled", "legacy_notifications_disabled", all to be "0" for "enabled"/"active" rule that does not have any actions', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry']),
...getThresholdRuleForAlertTesting(['telemetry']),
threshold: {
field: 'keyword',
value: 1,
@ -511,7 +511,7 @@ export default ({ getService }: FtrProviderContext) => {
};
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
@ -543,13 +543,13 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_disabled" to be "1" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry'], 'rule-1', false),
...getThresholdRuleForAlertTesting(['telemetry'], 'rule-1', false),
threshold: {
field: 'keyword',
value: 1,
},
};
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, false, rule);
await createRule(supertest, log, ruleToCreate);
@ -574,17 +574,17 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry']),
...getThresholdRuleForAlertTesting(['telemetry']),
threshold: {
field: 'keyword',
value: 1,
},
};
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
const { id } = await createRule(supertest, log, ruleToCreate);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -607,16 +607,16 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry'], 'rule-1', false),
...getThresholdRuleForAlertTesting(['telemetry'], 'rule-1', false),
threshold: {
field: 'keyword',
value: 1,
},
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -638,19 +638,19 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForSignalTesting(['telemetry']),
...getThresholdRuleForAlertTesting(['telemetry']),
threshold: {
field: 'keyword',
value: 1,
},
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -738,7 +738,7 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_disabled" to be "1" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleMlRule();
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, false, rule);
await createRule(supertest, log, ruleToCreate);
@ -763,7 +763,7 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule = getSimpleMlRule('rule-1', true);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
await createRule(supertest, log, ruleToCreate);
@ -786,10 +786,10 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleMlRule();
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -811,10 +811,10 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getSimpleMlRule('rule-1', true);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -887,7 +887,7 @@ export default ({ getService }: FtrProviderContext) => {
};
const { id } = await createRule(supertest, log, rule);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
@ -919,7 +919,7 @@ export default ({ getService }: FtrProviderContext) => {
it('should show "notifications_disabled" to be "1" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleThreatMatch();
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, false, rule);
await createRule(supertest, log, ruleToCreate);
@ -959,11 +959,11 @@ export default ({ getService }: FtrProviderContext) => {
},
],
};
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
const { id } = await createRule(supertest, log, ruleToCreate);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -986,10 +986,10 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleThreatMatch();
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
@ -1011,7 +1011,7 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule: ThreatMatchRuleCreateProps = {
...getSimpleThreatMatch('rule-1', true),
index: ['telemetry'],
@ -1029,10 +1029,10 @@ export default ({ getService }: FtrProviderContext) => {
],
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createNewAction(supertest, log);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForSignalsToBePresent(supertest, log, 4, [id]);
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -1057,7 +1057,7 @@ export default ({ getService }: FtrProviderContext) => {
});
describe('"pre-packaged"/"immutable" rules', async () => {
it('should show stats for totals for in-active pre-packaged rules', async () => {
it('@skipInQA should show stats for totals for in-active pre-packaged rules', async () => {
await installMockPrebuiltRules(supertest, es);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -1090,7 +1090,7 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show stats for the detection_rule_details for a specific pre-packaged rule', async () => {
it('@skipInQA should show stats for the detection_rule_details for a specific pre-packaged rule', async () => {
await installMockPrebuiltRules(supertest, es);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -1121,13 +1121,13 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "notifications_disabled" to be "1", "has_notification" to be "true, "has_legacy_notification" to be "false" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
it('@skipInQA should show "notifications_disabled" to be "1", "has_notification" to be "true, "has_legacy_notification" to be "false" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const hookAction = await createNewAction(supertest, log);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, newRuleToUpdate);
await updateRule(supertest, log, ruleToUpdate);
await updateRule(supertest, ruleToUpdate);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -1174,13 +1174,13 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "notifications_enabled" to be "1", "has_notification" to be "true, "has_legacy_notification" to be "false" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
it('@skipInQA should show "notifications_enabled" to be "1", "has_notification" to be "true, "has_legacy_notification" to be "false" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const hookAction = await createNewAction(supertest, log);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, true, newRuleToUpdate);
await updateRule(supertest, log, ruleToUpdate);
await updateRule(supertest, ruleToUpdate);
await retry.try(async () => {
const stats = await getStats(supertest, log);
@ -1227,12 +1227,12 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_disabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
it('@brokenInServerless should show "legacy_notifications_disabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const hookAction = await createNewAction(supertest, log);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id, false);
await updateRule(supertest, log, newRuleToUpdate);
await updateRule(supertest, newRuleToUpdate);
await createLegacyRuleAction(supertest, immutableRule.id, hookAction.id);
await retry.try(async () => {
@ -1280,12 +1280,12 @@ export default ({ getService }: FtrProviderContext) => {
});
});
it('should show "legacy_notifications_enabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
it('@brokenInServerless should show "legacy_notifications_enabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await getRule(supertest, log, ELASTIC_SECURITY_RULE_ID);
const hookAction = await createNewAction(supertest, log);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id, true);
await updateRule(supertest, log, newRuleToUpdate);
await updateRule(supertest, newRuleToUpdate);
await createLegacyRuleAction(supertest, immutableRule.id, hookAction.id);
await retry.try(async () => {

View file

@ -0,0 +1,512 @@
/*
* 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 {
ThreatMatchRuleCreateProps,
ThresholdRuleCreateProps,
} from '@kbn/security-solution-plugin/common/api/detection_engine';
import { getInitialDetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/get_initial_usage';
import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common';
import { RulesTypeUsage } from '@kbn/security-solution-plugin/server/usage/detections/rules/types';
import {
createLegacyRuleAction,
createWebHookRuleAction,
createRule,
createAlertsIndex,
deleteAllRules,
deleteAllAlerts,
getEqlRuleForAlertTesting,
fetchRule,
getRuleForAlertTesting,
getRuleWithWebHookAction,
getSimpleMlRule,
getSimpleRule,
getSimpleThreatMatch,
getStats,
getThresholdRuleForAlertTesting,
installMockPrebuiltRules,
waitForRuleSuccess,
waitForAlertsToBePresent,
updateRule,
deleteAllEventLogExecutionEvents,
} 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 retry = getService('retry');
const es = getService('es');
describe('@ess Detection rule legacy actions telemetry', async () => {
before(async () => {
// Just in case other tests do not clean up the event logs, let us clear them now and here only once.
await deleteAllEventLogExecutionEvents(es, log);
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry');
});
after(async () => {
await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/telemetry');
});
beforeEach(async () => {
await createAlertsIndex(supertest, log);
});
afterEach(async () => {
await deleteAllAlerts(supertest, log, es);
await deleteAllRules(supertest, log);
await deleteAllEventLogExecutionEvents(es, log);
});
describe('"kql" rule type', () => {
it('should show "notifications_enabled" to be "1" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForAlertTesting(['telemetry']);
const hookAction = await createWebHookRuleAction(supertest);
const ruleToCreate = getRuleWithWebHookAction(hookAction.id, true, rule);
const { id } = await createRule(supertest, log, ruleToCreate);
await waitForRuleSuccess({ supertest, log, id });
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
query: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.query,
enabled: 1,
alerts: 4,
notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
enabled: 1,
alerts: 4,
notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getRuleForAlertTesting(['telemetry'], 'rule-1', false);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
query: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.query,
disabled: 1,
legacy_notifications_disabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
disabled: 1,
legacy_notifications_disabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
query: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.query,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
});
describe('"eql" rule type', () => {
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getEqlRuleForAlertTesting(['telemetry'], 'rule-1', false);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
eql: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.eql,
disabled: 1,
legacy_notifications_disabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
disabled: 1,
legacy_notifications_disabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getEqlRuleForAlertTesting(['telemetry']);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
eql: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.eql,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
});
describe('"threshold" rule type', () => {
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForAlertTesting(['telemetry'], 'rule-1', false),
threshold: {
field: 'keyword',
value: 1,
},
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
threshold: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.threshold,
disabled: 1,
legacy_notifications_disabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
disabled: 1,
legacy_notifications_disabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule: ThresholdRuleCreateProps = {
...getThresholdRuleForAlertTesting(['telemetry']),
threshold: {
field: 'keyword',
value: 1,
},
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
threshold: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.threshold,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
});
// Note: We don't actually find signals with these tests as we don't have a good way of signal finding with ML rules.
describe('"ml" rule type', () => {
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleMlRule();
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
machine_learning: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.machine_learning,
disabled: 1,
legacy_notifications_disabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
disabled: 1,
legacy_notifications_disabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule = getSimpleMlRule('rule-1', true);
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
machine_learning: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.machine_learning,
enabled: 1,
legacy_notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
enabled: 1,
legacy_notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
});
describe('"indicator_match/threat_match" rule type', () => {
it('should show "legacy_notifications_disabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "disabled"/"in-active"', async () => {
const rule = getSimpleThreatMatch();
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
threat_match: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.threat_match,
disabled: 1,
legacy_notifications_disabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
disabled: 1,
legacy_notifications_disabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
it('should show "legacy_notifications_enabled" to be "1" for rule that has at least "1" legacy action(s) and the alert is "enabled"/"active"', async () => {
const rule: ThreatMatchRuleCreateProps = {
...getSimpleThreatMatch('rule-1', true),
index: ['telemetry'],
threat_index: ['telemetry'],
threat_mapping: [
{
entries: [
{
field: 'keyword',
value: 'keyword',
type: 'mapping',
},
],
},
],
};
const { id } = await createRule(supertest, log, rule);
const hookAction = await createWebHookRuleAction(supertest);
await createLegacyRuleAction(supertest, id, hookAction.id);
await waitForRuleSuccess({ supertest, log, id });
await waitForAlertsToBePresent(supertest, log, 4, [id]);
await retry.try(async () => {
const stats = await getStats(supertest, log);
const expected: RulesTypeUsage = {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage,
threat_match: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.threat_match,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
custom_total: {
...getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total,
alerts: 4,
enabled: 1,
legacy_notifications_enabled: 1,
},
};
expect(stats.detection_rules.detection_rule_usage).to.eql(expected);
});
});
});
describe('"pre-packaged"/"immutable" rules', async () => {
it('should show "legacy_notifications_disabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "disabled"/"in-active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id, false);
await updateRule(supertest, newRuleToUpdate);
await createLegacyRuleAction(supertest, immutableRule.id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
// We have to search by "rule_name" since the "rule_id" it is storing is the Saved Object ID and not the rule_id
const foundRule = stats.detection_rules.detection_rule_detail.find(
(rule) => rule.rule_id === ELASTIC_SECURITY_RULE_ID
);
if (foundRule == null) {
throw new Error('Found rule should not be null. Please change this end to end test.');
}
const {
created_on: createdOn,
updated_on: updatedOn,
rule_id: ruleId,
rule_version: ruleVersion,
...omittedFields
} = foundRule;
expect(omittedFields).to.eql({
rule_name: 'Simple Rule Query',
rule_type: 'query',
enabled: false,
elastic_rule: true,
alert_count_daily: 0,
cases_count_total: 0,
has_notification: false,
has_legacy_notification: true,
has_legacy_investigation_field: false,
});
expect(
stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled
).to.eql(0);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.legacy_notifications_enabled
).to.eql(0);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.legacy_notifications_disabled
).to.eql(1);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.notifications_enabled
).to.eql(0);
expect(stats.detection_rules.detection_rule_usage.custom_total).to.eql(
getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total
);
});
});
it('should show "legacy_notifications_enabled" to be "1", "has_notification" to be "false, "has_legacy_notification" to be "true" for rule that has at least "1" action(s) and the alert is "enabled"/"active"', async () => {
await installMockPrebuiltRules(supertest, es);
const immutableRule = await fetchRule(supertest, { ruleId: ELASTIC_SECURITY_RULE_ID });
const hookAction = await createWebHookRuleAction(supertest);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id, true);
await updateRule(supertest, newRuleToUpdate);
await createLegacyRuleAction(supertest, immutableRule.id, hookAction.id);
await retry.try(async () => {
const stats = await getStats(supertest, log);
// We have to search by "rule_name" since the "rule_id" it is storing is the Saved Object ID and not the rule_id
const foundRule = stats.detection_rules.detection_rule_detail.find(
(rule) => rule.rule_id === ELASTIC_SECURITY_RULE_ID
);
if (foundRule == null) {
throw new Error('Found rule should not be null. Please change this end to end test.');
}
const {
created_on: createdOn,
updated_on: updatedOn,
rule_id: ruleId,
rule_version: ruleVersion,
...omittedFields
} = foundRule;
expect(omittedFields).to.eql({
rule_name: 'Simple Rule Query',
rule_type: 'query',
enabled: true,
elastic_rule: true,
alert_count_daily: 0,
cases_count_total: 0,
has_notification: false,
has_legacy_notification: true,
has_legacy_investigation_field: false,
});
expect(
stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled
).to.eql(0);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.legacy_notifications_enabled
).to.eql(1);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.legacy_notifications_disabled
).to.eql(0);
expect(
stats.detection_rules.detection_rule_usage.elastic_total.notifications_enabled
).to.eql(0);
expect(stats.detection_rules.detection_rule_usage.custom_total).to.eql(
getInitialDetectionMetrics().detection_rules.detection_rule_usage.custom_total
);
});
});
});
});
};

View file

@ -6,5 +6,5 @@
*/
export * from './get_slack_action';
export * from './get_web_hook_action';
export * from './create_new_action';
export * from './create_new_webhook_action';
export * from './legacy_actions';

View file

@ -0,0 +1,39 @@
/*
* 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 { ToolingLog } from '@kbn/tooling-log';
import type { Client } from '@elastic/elasticsearch';
import { countDownES } from '../count_down_es';
/**
* Remove all .kibana-event-log-* documents with an execution.uuid
* This will retry 50 times before giving up and hopefully still not interfere with other tests
* @param es The ElasticSearch handle
* @param log The tooling logger
*/
export const deleteAllEventLogExecutionEvents = async (
es: Client,
log: ToolingLog
): Promise<void> => {
return countDownES(
async () => {
return es.deleteByQuery(
{
index: '.kibana-event-log-*',
q: '_exists_:kibana.alert.rule.execution.uuid',
wait_for_completion: true,
refresh: true,
body: {},
},
{ meta: true }
);
},
'deleteAllEventLogExecutionEvents',
log
);
};

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './delete_all_event_log_execution_events';

View file

@ -6,7 +6,6 @@
*/
import type { DetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/types';
import type { RiskEngineMetrics } from '@kbn/security-solution-plugin/server/usage/risk_engine/types';
/**
* Given a body this will return the detection metrics from it.
@ -24,20 +23,3 @@ export const getDetectionMetricsFromBody = (
): DetectionMetrics => {
return body[0].stats.stack_stats.kibana.plugins.security_solution.detectionMetrics;
};
/**
* Given a body this will return the risk engine metrics from it.
* @param body The Stats body
* @returns Detection metrics
*/
export const getRiskEngineMetricsFromBody = (
body: Array<{
stats: {
stack_stats: {
kibana: { plugins: { security_solution: { riskEngineMetrics: {} } } };
};
};
}>
): RiskEngineMetrics => {
return body[0].stats.stack_stats.kibana.plugins.security_solution.riskEngineMetrics;
};

View file

@ -8,17 +8,13 @@
import type { ToolingLog } from '@kbn/tooling-log';
import type SuperTest from 'supertest';
import type { DetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/types';
import type { RiskEngineMetrics } from '@kbn/security-solution-plugin/server/usage/risk_engine/types';
import {
ELASTIC_HTTP_VERSION_HEADER,
X_ELASTIC_INTERNAL_ORIGIN_REQUEST,
} from '@kbn/core-http-common';
import { getStatsUrl } from './get_stats_url';
import {
getDetectionMetricsFromBody,
getRiskEngineMetricsFromBody,
} from './get_detection_metrics_from_body';
import { getDetectionMetricsFromBody } from './get_detection_metrics_from_body';
/**
* Gets the stats from the stats endpoint.
@ -45,29 +41,3 @@ export const getStats = async (
return getDetectionMetricsFromBody(response.body);
};
/**
* Gets the stats from the stats endpoint.
* @param supertest The supertest agent.
* @returns The detection metrics
*/
export const getRiskEngineStats = async (
supertest: SuperTest.SuperTest<SuperTest.Test>,
log: ToolingLog
): Promise<RiskEngineMetrics> => {
const response = await supertest
.post(getStatsUrl())
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send({ unencrypted: true, refreshCache: true });
if (response.status !== 200) {
log.error(
`Did not get an expected 200 "ok" when getting the stats for risk engine. CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify(
response.body
)}, status: ${JSON.stringify(response.status)}`
);
}
return getRiskEngineMetricsFromBody(response.body);
};

View file

@ -9,13 +9,11 @@ export * from './exception_list_and_item';
export * from './alerts';
export * from './actions';
export * from './data_generator';
export * from './telemetry';
export * from './event_log';
export * from './machine_learning';
export * from './rules/get_rule_so_by_id';
export * from './rules/create_rule_saved_object';
export * from './rules/get_rule_with_legacy_investigation_fields';
export * from './get_index_name_from_load';
export * from './count_down_test';
export * from './count_down_es';
export * from './update_username';
@ -23,3 +21,6 @@ export * from './refresh_index';
export * from './wait_for';
export * from './route_with_namespace';
export * from './wait_for_index_to_populate';
export * from './get_stats';
export * from './get_detection_metrics_from_body';
export * from './get_stats_url';

View file

@ -0,0 +1,35 @@
/*
* 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 SuperTest from 'supertest';
import { UPDATE_OR_CREATE_LEGACY_ACTIONS } from '@kbn/security-solution-plugin/common/constants';
export const createLegacyRuleAction = async (
supertest: SuperTest.SuperTest<SuperTest.Test>,
alertId: string,
connectorId: string
): Promise<unknown> =>
supertest
.post(UPDATE_OR_CREATE_LEGACY_ACTIONS)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.query({ alert_id: alertId })
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: connectorId,
group: 'default',
params: {
message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: '.slack',
},
],
});

View file

@ -0,0 +1,25 @@
/*
* 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';
/**
* This is a representative ML rule payload as expected by the server
* @param ruleId The rule id
* @param enabled Set to tru to enable it, by default it is off
*/
export const getSimpleMlRule = (ruleId = 'rule-1', enabled = false): RuleCreateProps => ({
name: 'Simple ML Rule',
description: 'Simple Machine Learning Rule',
enabled,
anomaly_threshold: 44,
risk_score: 1,
rule_id: ruleId,
severity: 'high',
machine_learning_job_id: ['some_job_id'],
type: 'machine_learning',
});

View file

@ -36,5 +36,8 @@ export * from './preview_rule';
export * from './preview_rule_with_exception_entries';
export * from './patch_rule';
export * from './generate_event';
export * from './create_legacy_rule_action';
export * from './get_simple_threat_match';
export * from './get_simple_ml_rule';
export * from './prebuilt_rules';

View file

@ -9,6 +9,10 @@ import type { ToolingLog } from '@kbn/tooling-log';
import type SuperTest from 'supertest';
import { SECURITY_TELEMETRY_URL } from '@kbn/security-solution-plugin/common/constants';
import {
ELASTIC_HTTP_VERSION_HEADER,
X_ELASTIC_INTERNAL_ORIGIN_REQUEST,
} from '@kbn/core-http-common';
/**
* Gets the stats from the stats endpoint within specifically the security_solutions application.
@ -23,7 +27,8 @@ export const getSecurityTelemetryStats = async (
const response = await supertest
.get(SECURITY_TELEMETRY_URL)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send({ unencrypted: true, refreshCache: true });
if (response.status !== 200) {
log.error(

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './get_security_telemetry_stats';
export * from './remove_time_fields_from_telemetry_stats';

View file

@ -7,7 +7,7 @@
import type { ToolingLog } from '@kbn/tooling-log';
import type { Client } from '@elastic/elasticsearch';
import { waitFor } from '../../../../detection_engine_api_integration/utils/wait_for';
import { waitFor } from './wait_for';
/**
* Waits for the given index to contain documents

View file

@ -0,0 +1,24 @@
/*
* 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 { RiskEngineMetrics } from '@kbn/security-solution-plugin/server/usage/risk_engine/types';
/**
* Given a body this will return the risk engine metrics from it.
* @param body The Stats body
* @returns Detection metrics
*/
export const getRiskEngineMetricsFromBody = (
body: Array<{
stats: {
stack_stats: {
kibana: { plugins: { security_solution: { riskEngineMetrics: {} } } };
};
};
}>
): RiskEngineMetrics => {
return body[0].stats.stack_stats.kibana.plugins.security_solution.riskEngineMetrics;
};

View file

@ -7,44 +7,14 @@
import type { ToolingLog } from '@kbn/tooling-log';
import type SuperTest from 'supertest';
import type { DetectionMetrics } from '@kbn/security-solution-plugin/server/usage/detections/types';
import type { RiskEngineMetrics } from '@kbn/security-solution-plugin/server/usage/risk_engine/types';
import {
ELASTIC_HTTP_VERSION_HEADER,
X_ELASTIC_INTERNAL_ORIGIN_REQUEST,
} from '@kbn/core-http-common';
import { getStatsUrl } from '../../../../detection_engine_api_integration/utils/get_stats_url';
import {
getDetectionMetricsFromBody,
getRiskEngineMetricsFromBody,
} from '../../../../detection_engine_api_integration/utils/get_detection_metrics_from_body';
/**
* Gets the stats from the stats endpoint.
* @param supertest The supertest agent.
* @returns The detection metrics
*/
export const getStats = async (
supertest: SuperTest.SuperTest<SuperTest.Test>,
log: ToolingLog
): Promise<DetectionMetrics> => {
const response = await supertest
.post(getStatsUrl())
.set('kbn-xsrf', 'true')
.set(ELASTIC_HTTP_VERSION_HEADER, '2')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.send({ unencrypted: true, refreshCache: true });
if (response.status !== 200) {
log.error(
`Did not get an expected 200 "ok" when getting the stats for detections. CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify(
response.body
)}, status: ${JSON.stringify(response.status)}`
);
}
return getDetectionMetricsFromBody(response.body);
};
import { getStatsUrl } from '../../detections_response/utils/get_stats_url';
import { getRiskEngineMetricsFromBody } from './get_risk_engine_metrics_from_body';
/**
* Gets the stats from the stats endpoint.