mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
# Backport This will backport the following commits from `main` to `8.18`: - [[Security Solution] Allow prebuilt rules import and export (#212509)](https://github.com/elastic/kibana/pull/212509) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Nikita Indik","email":"nikita.indik@elastic.co"},"sourceCommit":{"committedDate":"2025-03-06T15:58:33Z","message":"[Security Solution] Allow prebuilt rules import and export (#212509)\n\n**Resolves: https://github.com/elastic/security-team/issues/11502**\n(internal)\n\nThis PR implements following changes and adds API integration tests for\nthem:\n- [x] Users with any license can export prebuilt rules (with enabled\nfeature flag)\n- [x] Users with Basic/Essentials license can import prebuilt rules only\nif they are non-customized and the feature flag is enabled\n- [x] Users with Enterprise/Complete license can import prebuilt rules\nwithout restrictions\n\nFlaky test runner (had to create 4 separate runs to test all configs):\n-\n[1](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7987)\n-\n[2](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7986)\n-\n[3](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7988)\n-\n[4](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7989)","sha":"ebe90e5c80e1dbe34d96ccd8a1e8e34d032affa4","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Detections and Resp","Team: SecuritySolution","Team:Detection Rule Management","Feature:Prebuilt Detection Rules","Feature:Rule Import/Export","backport:version","v8.18.0","v9.1.0","v8.19.0"],"title":"[Security Solution] Allow prebuilt rules import and export","number":212509,"url":"https://github.com/elastic/kibana/pull/212509","mergeCommit":{"message":"[Security Solution] Allow prebuilt rules import and export (#212509)\n\n**Resolves: https://github.com/elastic/security-team/issues/11502**\n(internal)\n\nThis PR implements following changes and adds API integration tests for\nthem:\n- [x] Users with any license can export prebuilt rules (with enabled\nfeature flag)\n- [x] Users with Basic/Essentials license can import prebuilt rules only\nif they are non-customized and the feature flag is enabled\n- [x] Users with Enterprise/Complete license can import prebuilt rules\nwithout restrictions\n\nFlaky test runner (had to create 4 separate runs to test all configs):\n-\n[1](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7987)\n-\n[2](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7986)\n-\n[3](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7988)\n-\n[4](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7989)","sha":"ebe90e5c80e1dbe34d96ccd8a1e8e34d032affa4"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.18","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/212509","number":212509,"mergeCommit":{"message":"[Security Solution] Allow prebuilt rules import and export (#212509)\n\n**Resolves: https://github.com/elastic/security-team/issues/11502**\n(internal)\n\nThis PR implements following changes and adds API integration tests for\nthem:\n- [x] Users with any license can export prebuilt rules (with enabled\nfeature flag)\n- [x] Users with Basic/Essentials license can import prebuilt rules only\nif they are non-customized and the feature flag is enabled\n- [x] Users with Enterprise/Complete license can import prebuilt rules\nwithout restrictions\n\nFlaky test runner (had to create 4 separate runs to test all configs):\n-\n[1](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7987)\n-\n[2](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7986)\n-\n[3](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7988)\n-\n[4](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7989)","sha":"ebe90e5c80e1dbe34d96ccd8a1e8e34d032affa4"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Nikita Indik <nikita.indik@elastic.co>
This commit is contained in:
parent
d24ab7c2c7
commit
b38b24112b
41 changed files with 981 additions and 65 deletions
|
@ -81,6 +81,13 @@ disabled:
|
|||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/trial_license_complete_tier/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_non_customized_prebuilt_rules/feature_enabled/configs/serverless_essentials_tier.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_non_customized_prebuilt_rules/feature_disabled/configs/serverless_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_enabled/configs/serverless_complete_tier.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_disabled/configs/serverless_essentials_tier.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_disabled/configs/serverless_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/export_prebuilt_rules/feature_enabled/configs/serverless_essentials_tier.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/export_prebuilt_rules/feature_disabled/configs/serverless_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/basic_license_essentials_tier/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_read/trial_license_complete_tier/configs/serverless.config.ts
|
||||
|
|
|
@ -70,6 +70,13 @@ enabled:
|
|||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/trial_license_complete_tier/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/basic_license_essentials_tier/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_non_customized_prebuilt_rules/feature_enabled/configs/ess_basic_license.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_non_customized_prebuilt_rules/feature_disabled/configs/ess_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_enabled/configs/ess_enterprise_license.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_disabled/configs/ess_basic_license.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/import_customized_prebuilt_rules/feature_disabled/configs/ess_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/export_prebuilt_rules/feature_enabled/configs/ess_basic_license.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/export_prebuilt_rules/feature_disabled/configs/ess_feature_flag_disabled.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_read/trial_license_complete_tier/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_read/basic_license_essentials_tier/configs/ess.config.ts
|
||||
|
|
|
@ -25,8 +25,8 @@ import { useDownloadExportedRules } from '../../../rule_management/logic/bulk_ac
|
|||
import { useHasActionsPrivileges } from './use_has_actions_privileges';
|
||||
import type { TimeRange } from '../../../rule_gaps/types';
|
||||
import { useScheduleRuleRun } from '../../../rule_gaps/logic/use_schedule_rule_run';
|
||||
import { usePrebuiltRulesCustomizationStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
|
||||
import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
|
||||
export const useRulesTableActions = ({
|
||||
showExceptionsDuplicateConfirmation,
|
||||
|
@ -47,7 +47,9 @@ export const useRulesTableActions = ({
|
|||
const { bulkExport } = useBulkExport();
|
||||
const downloadExportedRules = useDownloadExportedRules();
|
||||
const { scheduleRuleRun } = useScheduleRuleRun();
|
||||
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
|
||||
const isPrebuiltRulesCustomizationFeatureFlagEnabled = useIsExperimentalFeatureEnabled(
|
||||
'prebuiltRulesCustomizationEnabled'
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
|
@ -118,7 +120,7 @@ export const useRulesTableActions = ({
|
|||
await downloadExportedRules(response);
|
||||
}
|
||||
},
|
||||
enabled: (rule: Rule) => isRulesCustomizationEnabled || !rule.immutable,
|
||||
enabled: (rule: Rule) => isPrebuiltRulesCustomizationFeatureFlagEnabled || !rule.immutable,
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { usePrebuiltRulesCustomizationStatus } from '../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
|
||||
import { useScheduleRuleRun } from '../../../../detection_engine/rule_gaps/logic/use_schedule_rule_run';
|
||||
import type { TimeRange } from '../../../../detection_engine/rule_gaps/types';
|
||||
import { APP_UI_ID, SecurityPageName } from '../../../../../common/constants';
|
||||
|
@ -36,6 +35,7 @@ import { useDownloadExportedRules } from '../../../../detection_engine/rule_mana
|
|||
import * as i18nActions from '../../../pages/detection_engine/rules/translations';
|
||||
import * as i18n from './translations';
|
||||
import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
|
||||
const MyEuiButtonIcon = styled(EuiButtonIcon)`
|
||||
&.euiButtonIcon {
|
||||
|
@ -73,7 +73,9 @@ const RuleActionsOverflowComponent = ({
|
|||
application: { navigateToApp },
|
||||
telemetry,
|
||||
} = useKibana().services;
|
||||
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
|
||||
const isPrebuiltRulesCustomizationFeatureFlagEnabled = useIsExperimentalFeatureEnabled(
|
||||
'prebuiltRulesCustomizationEnabled'
|
||||
);
|
||||
const { startTransaction } = useStartTransaction();
|
||||
const { executeBulkAction } = useExecuteBulkAction({ suppressSuccessToast: true });
|
||||
const { bulkExport } = useBulkExport();
|
||||
|
@ -140,7 +142,8 @@ const RuleActionsOverflowComponent = ({
|
|||
key={i18nActions.EXPORT_RULE}
|
||||
icon="exportAction"
|
||||
disabled={
|
||||
!userHasPermissions || (isRulesCustomizationEnabled === false && rule.immutable)
|
||||
!userHasPermissions ||
|
||||
(isPrebuiltRulesCustomizationFeatureFlagEnabled === false && rule.immutable)
|
||||
}
|
||||
data-test-subj="rules-details-export-rule"
|
||||
onClick={async () => {
|
||||
|
@ -210,7 +213,7 @@ const RuleActionsOverflowComponent = ({
|
|||
rule,
|
||||
canDuplicateRuleWithActions,
|
||||
userHasPermissions,
|
||||
isRulesCustomizationEnabled,
|
||||
isPrebuiltRulesCustomizationFeatureFlagEnabled,
|
||||
startTransaction,
|
||||
closePopover,
|
||||
showBulkDuplicateExceptionsConfirmation,
|
||||
|
|
|
@ -43,6 +43,7 @@ import { bulkEnableDisableRules } from './bulk_enable_disable_rules';
|
|||
import { fetchRulesByQueryOrIds } from './fetch_rules_by_query_or_ids';
|
||||
import { bulkScheduleBackfill } from './bulk_schedule_rule_run';
|
||||
import { createPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client';
|
||||
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
|
||||
|
||||
const MAX_RULES_TO_PROCESS_TOTAL = 10000;
|
||||
// Set a lower limit for bulk edit as the rules client might fail with a "Query
|
||||
|
@ -277,6 +278,14 @@ export const performBulkActionRoute = (
|
|||
break;
|
||||
}
|
||||
case BulkActionTypeEnum.export: {
|
||||
const prebuiltRulesCustomizationStatus =
|
||||
detectionRulesClient.getRuleCustomizationStatus();
|
||||
|
||||
const isPrebuiltRulesExportAllowed =
|
||||
prebuiltRulesCustomizationStatus.isRulesCustomizationEnabled ||
|
||||
prebuiltRulesCustomizationStatus.customizationDisabledReason ===
|
||||
PrebuiltRulesCustomizationDisabledReason.License;
|
||||
|
||||
const exported = await getExportByObjectIds(
|
||||
rulesClient,
|
||||
exceptionsClient,
|
||||
|
@ -284,7 +293,7 @@ export const performBulkActionRoute = (
|
|||
exporter,
|
||||
request,
|
||||
actionsClient,
|
||||
detectionRulesClient.getRuleCustomizationStatus().isRulesCustomizationEnabled
|
||||
isPrebuiltRulesExportAllowed
|
||||
);
|
||||
|
||||
const responseBody = `${exported.rulesNdjson}${exported.exceptionLists}${exported.actionConnectors}${exported.exportDetails}`;
|
||||
|
|
|
@ -23,6 +23,7 @@ import { getExportByObjectIds } from '../../../logic/export/get_export_by_object
|
|||
import { getExportAll } from '../../../logic/export/get_export_all';
|
||||
import { buildSiemResponse } from '../../../../routes/utils';
|
||||
import { RULE_MANAGEMENT_IMPORT_EXPORT_SOCKET_TIMEOUT_MS } from '../../timeouts';
|
||||
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
|
||||
|
||||
export const exportRulesRoute = (
|
||||
router: SecuritySolutionPluginRouter,
|
||||
|
@ -73,7 +74,12 @@ export const exportRulesRoute = (
|
|||
|
||||
const client = getClient({ includedHiddenTypes: ['action'] });
|
||||
const actionsExporter = getExporter(client);
|
||||
const { isRulesCustomizationEnabled } = detectionRulesClient.getRuleCustomizationStatus();
|
||||
const prebuiltRulesCustomizationStatus = detectionRulesClient.getRuleCustomizationStatus();
|
||||
|
||||
const isPrebuiltRulesExportAllowed =
|
||||
prebuiltRulesCustomizationStatus.isRulesCustomizationEnabled ||
|
||||
prebuiltRulesCustomizationStatus.customizationDisabledReason ===
|
||||
PrebuiltRulesCustomizationDisabledReason.License;
|
||||
|
||||
try {
|
||||
const exportSizeLimit = config.maxRuleImportExportSize;
|
||||
|
@ -85,7 +91,7 @@ export const exportRulesRoute = (
|
|||
} else {
|
||||
let rulesCount = 0;
|
||||
|
||||
if (isRulesCustomizationEnabled) {
|
||||
if (isPrebuiltRulesExportAllowed) {
|
||||
rulesCount = await getRulesCount({
|
||||
rulesClient,
|
||||
filter: '',
|
||||
|
@ -95,6 +101,7 @@ export const exportRulesRoute = (
|
|||
rulesClient,
|
||||
});
|
||||
}
|
||||
|
||||
if (rulesCount > exportSizeLimit) {
|
||||
return siemResponse.error({
|
||||
statusCode: 400,
|
||||
|
@ -112,7 +119,7 @@ export const exportRulesRoute = (
|
|||
actionsExporter,
|
||||
request,
|
||||
actionsClient,
|
||||
isRulesCustomizationEnabled
|
||||
isPrebuiltRulesExportAllowed
|
||||
)
|
||||
: await getExportAll(
|
||||
rulesClient,
|
||||
|
@ -120,7 +127,7 @@ export const exportRulesRoute = (
|
|||
actionsExporter,
|
||||
request,
|
||||
actionsClient,
|
||||
isRulesCustomizationEnabled
|
||||
isPrebuiltRulesExportAllowed
|
||||
);
|
||||
|
||||
const responseBody = request.query.exclude_export_details
|
||||
|
|
|
@ -31,6 +31,7 @@ import { getQueryRuleParams } from '../../../../rule_schema/mocks';
|
|||
import { importRulesRoute } from './route';
|
||||
import { HttpAuthzError } from '../../../../../machine_learning/validation';
|
||||
import { createPrebuiltRuleAssetsClient as createPrebuiltRuleAssetsClientMock } from '../../../../prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client';
|
||||
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
|
||||
|
||||
jest.mock('../../../../../machine_learning/authz');
|
||||
|
||||
|
@ -59,6 +60,7 @@ describe('Import rules route', () => {
|
|||
clients.detectionRulesClient.importRule.mockResolvedValue(getRulesSchemaMock());
|
||||
clients.detectionRulesClient.getRuleCustomizationStatus.mockReturnValue({
|
||||
isRulesCustomizationEnabled: false,
|
||||
customizationDisabledReason: PrebuiltRulesCustomizationDisabledReason.FeatureFlag,
|
||||
});
|
||||
clients.actionsClient.getAll.mockResolvedValue([]);
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
|
@ -71,7 +73,6 @@ describe('Import rules route', () => {
|
|||
describe('status codes', () => {
|
||||
test('returns 200 when importing a single rule with a valid actionClient and alertClient', async () => {
|
||||
const response = await server.inject(request, requestContextMock.convertContext(context));
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
});
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import {
|
|||
migrateLegacyActionsIds,
|
||||
} from '../../../utils/utils';
|
||||
import { RULE_MANAGEMENT_IMPORT_EXPORT_SOCKET_TIMEOUT_MS } from '../../timeouts';
|
||||
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
|
||||
|
||||
const CHUNK_PARSED_OBJECT_SIZE = 50;
|
||||
|
||||
|
@ -86,7 +87,7 @@ export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: C
|
|||
]);
|
||||
|
||||
const detectionRulesClient = ctx.securitySolution.getDetectionRulesClient();
|
||||
const { isRulesCustomizationEnabled } = detectionRulesClient.getRuleCustomizationStatus();
|
||||
const ruleCustomizationStatus = detectionRulesClient.getRuleCustomizationStatus();
|
||||
const actionsClient = ctx.actions.getActionsClient();
|
||||
const actionSOClient = ctx.core.savedObjects.getClient({
|
||||
includedHiddenTypes: ['action'],
|
||||
|
@ -166,15 +167,10 @@ export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: C
|
|||
|
||||
let importRuleResponse: ImportRuleResponse[] = [];
|
||||
|
||||
if (isRulesCustomizationEnabled) {
|
||||
importRuleResponse = await importRules({
|
||||
ruleChunks,
|
||||
overwriteRules: request.query.overwrite,
|
||||
allowMissingConnectorSecrets: !!actionConnectors.length,
|
||||
ruleSourceImporter,
|
||||
detectionRulesClient,
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
ruleCustomizationStatus.customizationDisabledReason ===
|
||||
PrebuiltRulesCustomizationDisabledReason.FeatureFlag
|
||||
) {
|
||||
importRuleResponse = await importRulesLegacy({
|
||||
ruleChunks,
|
||||
overwriteRules: request.query.overwrite,
|
||||
|
@ -182,6 +178,14 @@ export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: C
|
|||
detectionRulesClient,
|
||||
savedObjectsClient,
|
||||
});
|
||||
} else {
|
||||
importRuleResponse = await importRules({
|
||||
ruleChunks,
|
||||
overwriteRules: request.query.overwrite,
|
||||
allowMissingConnectorSecrets: !!actionConnectors.length,
|
||||
ruleSourceImporter,
|
||||
detectionRulesClient,
|
||||
});
|
||||
}
|
||||
|
||||
const parseErrors = parsedRuleErrors.map((error) =>
|
||||
|
|
|
@ -104,7 +104,7 @@ export const validateBulkEditRule = async ({
|
|||
}
|
||||
|
||||
// Rule customization is disabled; only certain actions can be applied to immutable rules
|
||||
const canRuleBeEdited = istEditApplicableToImmutableRule(edit);
|
||||
const canRuleBeEdited = isEditApplicableToImmutableRule(edit);
|
||||
if (!canRuleBeEdited) {
|
||||
await throwDryRunError(
|
||||
() => invariant(canRuleBeEdited, "Elastic rule can't be edited"),
|
||||
|
@ -120,7 +120,7 @@ export const validateBulkEditRule = async ({
|
|||
/**
|
||||
* add_rule_actions, set_rule_actions can be applied to prebuilt/immutable rules
|
||||
*/
|
||||
const istEditApplicableToImmutableRule = (edit: BulkActionEditPayload[]): boolean => {
|
||||
const isEditApplicableToImmutableRule = (edit: BulkActionEditPayload[]): boolean => {
|
||||
const applicableActions: BulkActionEditType[] = [
|
||||
BulkActionEditTypeEnum.set_rule_actions,
|
||||
BulkActionEditTypeEnum.add_rule_actions,
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { checkRuleExceptionReferences } from '../../import/check_rule_exception_references';
|
||||
import { getReferencedExceptionLists } from '../../import/gather_referenced_exceptions';
|
||||
import type { IDetectionRulesClient } from '../detection_rules_client_interface';
|
||||
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
|
||||
|
||||
/**
|
||||
* Imports rules
|
||||
|
@ -39,6 +40,8 @@ export const importRules = async ({
|
|||
rules: RuleToImport[];
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
}): Promise<Array<RuleResponse | RuleImportErrorObject>> => {
|
||||
const ruleCustomizationStatus = detectionRulesClient.getRuleCustomizationStatus();
|
||||
|
||||
const existingLists = await getReferencedExceptionLists({
|
||||
rules,
|
||||
savedObjectsClient,
|
||||
|
@ -69,6 +72,25 @@ export const importRules = async ({
|
|||
}
|
||||
|
||||
const { immutable, ruleSource } = ruleSourceImporter.calculateRuleSource(rule);
|
||||
const isCustomized = (ruleSource.type === 'external' && ruleSource.is_customized) ?? false;
|
||||
|
||||
if (
|
||||
isCustomized &&
|
||||
ruleCustomizationStatus.customizationDisabledReason ===
|
||||
PrebuiltRulesCustomizationDisabledReason.License
|
||||
) {
|
||||
return createRuleImportErrorObject({
|
||||
message: i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.rules.licenseInsufficientToImportCustomizedPrebuiltRule',
|
||||
{
|
||||
defaultMessage:
|
||||
'Importing prebuilt rules is not supported if the they were modified. Upgrade your license to import modified prebuilt rules [rule_id: {ruleId}].',
|
||||
values: { ruleId: rule.rule_id },
|
||||
}
|
||||
),
|
||||
ruleId: rule.rule_id,
|
||||
});
|
||||
}
|
||||
|
||||
const [exceptionErrors, exceptions] = checkRuleExceptionReferences({
|
||||
rule,
|
||||
|
|
|
@ -24,15 +24,14 @@ export const getExportAll = async (
|
|||
actionsExporter: ISavedObjectsExporter,
|
||||
request: KibanaRequest,
|
||||
actionsClient: ActionsClient,
|
||||
prebuiltRulesCustomizationEnabled?: boolean
|
||||
isPrebuiltRulesExportAllowed?: boolean
|
||||
): Promise<{
|
||||
rulesNdjson: string;
|
||||
exportDetails: string;
|
||||
exceptionLists: string | null;
|
||||
actionConnectors: string;
|
||||
prebuiltRulesCustomizationEnabled?: boolean;
|
||||
}> => {
|
||||
const ruleAlertTypes = prebuiltRulesCustomizationEnabled
|
||||
const ruleAlertTypes = isPrebuiltRulesExportAllowed
|
||||
? await getRules({ rulesClient, filter: '' })
|
||||
: await getNonPackagedRules({ rulesClient });
|
||||
const rules = transformAlertsToRules(ruleAlertTypes);
|
||||
|
|
|
@ -30,19 +30,18 @@ export const getExportByObjectIds = async (
|
|||
actionsExporter: ISavedObjectsExporter,
|
||||
request: KibanaRequest,
|
||||
actionsClient: ActionsClient,
|
||||
prebuiltRulesCustomizationEnabled?: boolean
|
||||
isPrebuiltRulesExportAllowed?: boolean
|
||||
): Promise<{
|
||||
rulesNdjson: string;
|
||||
exportDetails: string;
|
||||
exceptionLists: string | null;
|
||||
actionConnectors: string;
|
||||
prebuiltRulesCustomizationEnabled?: boolean;
|
||||
}> =>
|
||||
withSecuritySpan('getExportByObjectIds', async () => {
|
||||
const rulesAndErrors = await fetchRulesByIds(
|
||||
rulesClient,
|
||||
ruleIds,
|
||||
prebuiltRulesCustomizationEnabled
|
||||
isPrebuiltRulesExportAllowed
|
||||
);
|
||||
const { rules, missingRuleIds } = rulesAndErrors;
|
||||
|
||||
|
@ -83,7 +82,7 @@ interface FetchRulesResult {
|
|||
const fetchRulesByIds = async (
|
||||
rulesClient: RulesClient,
|
||||
ruleIds: string[],
|
||||
prebuiltRulesCustomizationEnabled?: boolean
|
||||
isPrebuiltRulesExportAllowed?: boolean
|
||||
): Promise<FetchRulesResult> => {
|
||||
// It's important to avoid too many clauses in the request otherwise ES will fail to process the request
|
||||
// with `too_many_clauses` error (see https://github.com/elastic/kibana/issues/170015). The clauses limit
|
||||
|
@ -117,7 +116,7 @@ const fetchRulesByIds = async (
|
|||
|
||||
return matchingRule != null &&
|
||||
hasValidRuleType(matchingRule) &&
|
||||
(prebuiltRulesCustomizationEnabled || matchingRule.params.immutable !== true)
|
||||
(isPrebuiltRulesExportAllowed || matchingRule.params.immutable !== true)
|
||||
? {
|
||||
rule: transformRuleToExportableFormat(internalRuleToAPIResponse(matchingRule)),
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName: 'Rules Management - Rule Patch Integration Tests - ESS Env - Basic License',
|
||||
reportName:
|
||||
'Rules Management - Rule Import/Export Integration Tests - ESS Env - Basic License',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ export default createTestConfig({
|
|||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Patch Integration Tests - Serverless Env - Essentials Tier ',
|
||||
'Rules Management - Rule Import/Export Integration Tests - Serverless Env - Essentials Tier',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.trial')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Prebuilt Rule Export Integration Tests - Customization disabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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:
|
||||
'Rules Management - Prebuilt Rule Export Integration Tests - Customization disabled - Serverless Env',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { BulkActionTypeEnum } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import {
|
||||
binaryToString,
|
||||
createPrebuiltRuleAssetSavedObjects,
|
||||
createRuleAssetSavedObject,
|
||||
deleteAllPrebuiltRuleAssets,
|
||||
installPrebuiltRules,
|
||||
} from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Prebuilt rule export - feature disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it("Export API - doesn't export prebuilt rules when the feature is disabled", async () => {
|
||||
const ruleId = 'prebuilt-rule-1';
|
||||
const ruleAsset = createRuleAssetSavedObject({ rule_id: ruleId, version: 1 });
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
await installPrebuiltRules(es, supertest);
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.exportRules({ query: {}, body: null })
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
const exportDetails = JSON.parse(body.toString());
|
||||
|
||||
expect(exportDetails).toMatchObject({
|
||||
exported_rules_count: 0,
|
||||
missing_rules: [], // Prebuilt rules are not in missing rules, even though they are not exported
|
||||
});
|
||||
});
|
||||
|
||||
it("Bulk actions export API - doesn't export prebuilt rules when the feature is disabled", async () => {
|
||||
const ruleAsset = createRuleAssetSavedObject({ rule_id: 'prebuilt-rule-1', version: 1 });
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
await installPrebuiltRules(es, supertest);
|
||||
|
||||
const findResponse = await securitySolutionApi.findRules({ query: {} });
|
||||
const installedRule = findResponse.body.data[0];
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.performRulesBulkAction({
|
||||
query: {},
|
||||
body: { action: BulkActionTypeEnum.export, ids: [installedRule.id] },
|
||||
})
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
const exportDetails = JSON.parse(body.toString());
|
||||
|
||||
expect(exportDetails).toMatchObject({
|
||||
exported_rules_count: 0,
|
||||
missing_rules: [{ rule_id: 'prebuilt-rule-1' }],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Prebuilt rule export', function () {
|
||||
loadTestFile(require.resolve('./export_prebuilt_rules_feature_disabled'));
|
||||
});
|
||||
}
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.trial')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Prebuilt Rule Export Integration Tests - Customization enabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
testConfig.kbnTestServer.serverArgs = testConfig.kbnTestServer.serverArgs.map((arg: string) => {
|
||||
// Override the default value of `--xpack.securitySolution.enableExperimental` to enable the prebuilt rules customization feature
|
||||
if (arg.includes('--xpack.securitySolution.enableExperimental')) {
|
||||
return `--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`;
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base.essentials';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Prebuilt Rule Export Integration Tests - Customization enabled - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`,
|
||||
],
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 { BulkActionTypeEnum } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import {
|
||||
binaryToString,
|
||||
createPrebuiltRuleAssetSavedObjects,
|
||||
createRuleAssetSavedObject,
|
||||
deleteAllPrebuiltRuleAssets,
|
||||
installPrebuiltRules,
|
||||
} from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Prebuilt rule export', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it("Export API - exports prebuilt all rules if rule_id's are not specified", async () => {
|
||||
const ruleId = 'prebuilt-rule-1';
|
||||
const ruleAsset = createRuleAssetSavedObject({ rule_id: ruleId, version: 1 });
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
await installPrebuiltRules(es, supertest);
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.exportRules({ query: {}, body: null })
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
const [ruleJson, exportDetailsJson] = body.toString().split(/\n/);
|
||||
|
||||
expect(JSON.parse(ruleJson)).toMatchObject({
|
||||
rule_id: ruleId,
|
||||
rule_source: {
|
||||
type: 'external',
|
||||
is_customized: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(JSON.parse(exportDetailsJson)).toMatchObject({
|
||||
exported_rules_count: 1,
|
||||
missing_rules: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('Export API - exports specified prebuilt rules', async () => {
|
||||
const ruleId = 'prebuilt-rule-1';
|
||||
const ruleAsset = createRuleAssetSavedObject({ rule_id: ruleId, version: 1 });
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
await installPrebuiltRules(es, supertest);
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.exportRules({
|
||||
query: {},
|
||||
body: {
|
||||
objects: [{ rule_id: ruleId }],
|
||||
},
|
||||
})
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
const [ruleJson, exportDetailsJson] = body.toString().split(/\n/);
|
||||
|
||||
expect(JSON.parse(ruleJson)).toMatchObject({
|
||||
rule_id: ruleId,
|
||||
rule_source: {
|
||||
type: 'external',
|
||||
is_customized: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(JSON.parse(exportDetailsJson)).toMatchObject({
|
||||
exported_rules_count: 1,
|
||||
missing_rules: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('Bulk actions export API - exports prebuilt rules', async () => {
|
||||
const ruleAsset = createRuleAssetSavedObject({ rule_id: 'prebuilt-rule-1', version: 1 });
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
await installPrebuiltRules(es, supertest);
|
||||
|
||||
const findResponse = await securitySolutionApi.findRules({ query: {} });
|
||||
const installedRule = findResponse.body.data[0];
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.performRulesBulkAction({
|
||||
query: {},
|
||||
body: { action: BulkActionTypeEnum.export, ids: [installedRule.id] },
|
||||
})
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
const [ruleJson, exportDetailsJson] = body.toString().split(/\n/);
|
||||
|
||||
expect(JSON.parse(ruleJson)).toMatchObject({
|
||||
id: installedRule.id,
|
||||
rule_source: {
|
||||
type: 'external',
|
||||
is_customized: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(JSON.parse(exportDetailsJson)).toMatchObject({
|
||||
missing_rules: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Prebuilt rule export', function () {
|
||||
loadTestFile(require.resolve('./export_prebuilt_rules_feature_enabled'));
|
||||
});
|
||||
}
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.basic')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization enabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
testConfig.kbnTestServer.serverArgs = testConfig.kbnTestServer.serverArgs.map((arg: string) => {
|
||||
// Override the default value of `--xpack.securitySolution.enableExperimental` to enable the prebuilt rules customization feature
|
||||
if (arg.includes('--xpack.securitySolution.enableExperimental')) {
|
||||
return `--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`;
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.basic')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization disabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base.essentials';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization enabled - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`,
|
||||
],
|
||||
});
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization disabled - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [],
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Rule Import API - Customized prebuilt rules', function () {
|
||||
loadTestFile(require.resolve('./not_allowed_importing_customized_prebuilt_rules'));
|
||||
});
|
||||
}
|
|
@ -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 'expect';
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import { deleteAllPrebuiltRuleAssets, getCustomQueryRuleParams } from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
import { combineToNdJson } from '../../../../utils/combine_to_ndjson';
|
||||
import { createPrebuiltRuleAssetSavedObjects, createRuleAssetSavedObject } from '../../../../utils';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Prebuilt rule import', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it(`does NOT import customized prebuilt rules when rule customization is disabled`, async () => {
|
||||
const ruleId = 'prebuilt-rule-to-be-customized';
|
||||
const ruleParams = getCustomQueryRuleParams({
|
||||
rule_id: ruleId,
|
||||
// @ts-expect-error the API supports this param, but we only need it in {@link RuleToImport}
|
||||
immutable: true,
|
||||
rule_source: { type: 'external', is_customized: false },
|
||||
version: 1,
|
||||
});
|
||||
const ruleAsset = createRuleAssetSavedObject(ruleParams);
|
||||
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
|
||||
// Customizing the rule before importing
|
||||
const ndjson = combineToNdJson({ ...ruleParams, name: 'My customized rule' });
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.importRules({ query: {} })
|
||||
.attach('file', Buffer.from(ndjson), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
success: false,
|
||||
errors: [
|
||||
{
|
||||
rule_id: 'prebuilt-rule-to-be-customized',
|
||||
error: {
|
||||
status_code: 400,
|
||||
message: expect.stringContaining('rule_id: prebuilt-rule-to-be-customized]'),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import {
|
||||
createPrebuiltRuleAssetSavedObjects,
|
||||
createRuleAssetSavedObject,
|
||||
deleteAllPrebuiltRuleAssets,
|
||||
getCustomQueryRuleParams,
|
||||
} from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
import { combineToNdJson } from '../../../../utils/combine_to_ndjson';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Prebuilt rule import', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it(`imports customized prebuilt rules`, async () => {
|
||||
const ruleId = 'prebuilt-rule-to-be-customized';
|
||||
const ruleParams = getCustomQueryRuleParams({
|
||||
rule_id: ruleId,
|
||||
// @ts-expect-error the API supports this param, but we only need it in {@link RuleToImport}
|
||||
immutable: true,
|
||||
rule_source: { type: 'external', is_customized: false },
|
||||
version: 1,
|
||||
});
|
||||
const ruleAsset = createRuleAssetSavedObject(ruleParams);
|
||||
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
|
||||
// Customizing the rule before importing
|
||||
const ndjson = combineToNdJson({ ...ruleParams, name: 'My customized rule' });
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.importRules({ query: {} })
|
||||
.attach('file', Buffer.from(ndjson), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
success: true,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const { body: importedRule } = await securitySolutionApi
|
||||
.readRule({
|
||||
query: { rule_id: ruleId },
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(importedRule).toMatchObject({
|
||||
...ruleParams,
|
||||
name: 'My customized rule',
|
||||
rule_source: {
|
||||
type: 'external',
|
||||
is_customized: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.trial')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization enabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
testConfig.kbnTestServer.serverArgs = testConfig.kbnTestServer.serverArgs.map((arg: string) => {
|
||||
// Override the default value of `--xpack.securitySolution.enableExperimental` to enable the prebuilt rules customization feature
|
||||
if (arg.includes('--xpack.securitySolution.enableExperimental')) {
|
||||
return `--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`;
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization disabled - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`,
|
||||
],
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Rule Import API - Customized prebuilt rules', function () {
|
||||
loadTestFile(require.resolve('./allowed_importing_customized_prebuilt_rules'));
|
||||
});
|
||||
}
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.trial')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing non-customized prebuilt rules - Customization disabled - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing non-customized prebuilt rules - Customization disabled - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [],
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Rule Import API - Non-customized prebuilt rules', function () {
|
||||
loadTestFile(require.resolve('./not_allowed_importing_non_customized_prebuilt_rules'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import { deleteAllPrebuiltRuleAssets, getCustomQueryRuleParams } from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
import { combineToNdJson } from '../../../../utils/combine_to_ndjson';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Prebuilt rule import', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it(`does NOT allow importing non-customized prebuilt rules`, async () => {
|
||||
const ruleToImport = getCustomQueryRuleParams({
|
||||
rule_id: 'non-customized-prebuilt-rule',
|
||||
// @ts-expect-error the API supports this param, but we only need it in {@link RuleToImport}
|
||||
immutable: true,
|
||||
rule_source: { type: 'external', is_customized: false },
|
||||
});
|
||||
const ndjson = combineToNdJson(ruleToImport);
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.importRules({ query: {} })
|
||||
.attach('file', Buffer.from(ndjson), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
success: false,
|
||||
errors: [
|
||||
{
|
||||
rule_id: 'non-customized-prebuilt-rule',
|
||||
error: {
|
||||
status_code: 400,
|
||||
message:
|
||||
'Importing prebuilt rules is not supported. To import this rule as a custom rule, first duplicate the rule and then export it. [rule_id: non-customized-prebuilt-rule]',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
import {
|
||||
createPrebuiltRuleAssetSavedObjects,
|
||||
createRuleAssetSavedObject,
|
||||
deleteAllPrebuiltRuleAssets,
|
||||
getCustomQueryRuleParams,
|
||||
} from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
import { combineToNdJson } from '../../../../utils/combine_to_ndjson';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const es = getService('es');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
const supertest = getService('supertest');
|
||||
const log = getService('log');
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI Import - Customization Enabled', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
});
|
||||
|
||||
it(`imports non-customized prebuilt rules`, async () => {
|
||||
const ruleId = 'prebuilt-rule';
|
||||
const ruleParams = getCustomQueryRuleParams({
|
||||
rule_id: ruleId,
|
||||
// @ts-expect-error the API supports this param, but we only need it in {@link RuleToImport}
|
||||
immutable: true,
|
||||
rule_source: { type: 'external', is_customized: false },
|
||||
version: 1,
|
||||
});
|
||||
const ruleAsset = createRuleAssetSavedObject(ruleParams);
|
||||
|
||||
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
|
||||
|
||||
const ndjson = combineToNdJson(ruleParams);
|
||||
|
||||
const { body } = await securitySolutionApi
|
||||
.importRules({ query: {} })
|
||||
.attach('file', Buffer.from(ndjson), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
success: true,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const { body: importedRule } = await securitySolutionApi
|
||||
.readRule({
|
||||
query: { rule_id: ruleId },
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(importedRule).toMatchObject(ruleParams);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../../../config/ess/config.base.basic')
|
||||
);
|
||||
|
||||
const testConfig = {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing non-customized prebuilt rules - ESS Env',
|
||||
},
|
||||
};
|
||||
|
||||
testConfig.kbnTestServer.serverArgs = testConfig.kbnTestServer.serverArgs.map((arg: string) => {
|
||||
// Override the default value of `--xpack.securitySolution.enableExperimental` to enable the prebuilt rules customization feature
|
||||
if (arg.includes('--xpack.securitySolution.enableExperimental')) {
|
||||
return `--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`;
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
|
||||
return testConfig;
|
||||
}
|
|
@ -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 { createTestConfig } from '../../../../../../../config/serverless/config.base.essentials';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName:
|
||||
'Rules Management - Rule Import Integration Tests - Importing non-customized prebuilt rules - Serverless Env',
|
||||
},
|
||||
kbnTestServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`,
|
||||
],
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rules Management - Rule Import API - Non-customized prebuilt rules', function () {
|
||||
loadTestFile(require.resolve('./allowed_importing_non_customized_prebuilt_rules'));
|
||||
});
|
||||
}
|
|
@ -1625,36 +1625,6 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
|
||||
describe('supporting prebuilt rule customization', () => {
|
||||
describe('compatibility with prebuilt rule fields', () => {
|
||||
it('rejects rules with "immutable: true" when the feature flag is disabled', async () => {
|
||||
const rule = getCustomQueryRuleParams({
|
||||
rule_id: 'rule-immutable',
|
||||
// @ts-expect-error the API supports this param, but we only need it in {@link RuleToImport}
|
||||
immutable: true,
|
||||
});
|
||||
const ndjson = combineToNdJson(rule);
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_IMPORT_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.attach('file', Buffer.from(ndjson), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
success: false,
|
||||
errors: [
|
||||
{
|
||||
rule_id: 'rule-immutable',
|
||||
error: {
|
||||
status_code: 400,
|
||||
message:
|
||||
'Importing prebuilt rules is not supported. To import this rule as a custom rule, first duplicate the rule and then export it. [rule_id: rule-immutable]',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('imports custom rules alongside prebuilt rules when feature flag is disabled', async () => {
|
||||
const ndjson = combineToNdJson(
|
||||
getCustomQueryRuleParams({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue