[8.x] [Security Solution] Enable prebuilt rules customization feature flag (#212761) (#214024)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution] Enable prebuilt rules customization feature flag
(#212761)](https://github.com/elastic/kibana/pull/212761)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Maxim
Palenov","email":"maxim.palenov@elastic.co"},"sourceCommit":{"committedDate":"2025-03-11T21:45:49Z","message":"[Security
Solution] Enable prebuilt rules customization feature flag
(#212761)\n\n**Addresses:**
https://github.com/elastic/kibana/issues/180267\n\n## Summary\n\nThis PR
enables `prebuiltRulesCustomizationEnabled` feature flag.\n\n##
Details\n\nBesides simply enabling `prebuiltRulesCustomizationEnabled`
feature flag the following required changes were done\n\n- failed tests
due enabling the FF were fixed\n- FF setting was removed from test
configurations (integrations and Cypress tests)\n- FF logic was removed
from the codebase. Disabling the FF would require roll back test changes
as well. So just in case we have to disable the FF it's simpler to roll
back the PR's
commit.","sha":"f7d4f19096e0711021b8587fb4d0575998d18f3e","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","impact:high","v9.0.0","Team:Detections
and Resp","Team: SecuritySolution","Team:Detection Rule
Management","Feature:Prebuilt Detection
Rules","backport:version","v8.18.0","v9.1.0","v8.19.0"],"title":"[Security
Solution] Enable prebuilt rules customization feature
flag","number":212761,"url":"https://github.com/elastic/kibana/pull/212761","mergeCommit":{"message":"[Security
Solution] Enable prebuilt rules customization feature flag
(#212761)\n\n**Addresses:**
https://github.com/elastic/kibana/issues/180267\n\n## Summary\n\nThis PR
enables `prebuiltRulesCustomizationEnabled` feature flag.\n\n##
Details\n\nBesides simply enabling `prebuiltRulesCustomizationEnabled`
feature flag the following required changes were done\n\n- failed tests
due enabling the FF were fixed\n- FF setting was removed from test
configurations (integrations and Cypress tests)\n- FF logic was removed
from the codebase. Disabling the FF would require roll back test changes
as well. So just in case we have to disable the FF it's simpler to roll
back the PR's
commit.","sha":"f7d4f19096e0711021b8587fb4d0575998d18f3e"}},"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/212761","number":212761,"mergeCommit":{"message":"[Security
Solution] Enable prebuilt rules customization feature flag
(#212761)\n\n**Addresses:**
https://github.com/elastic/kibana/issues/180267\n\n## Summary\n\nThis PR
enables `prebuiltRulesCustomizationEnabled` feature flag.\n\n##
Details\n\nBesides simply enabling `prebuiltRulesCustomizationEnabled`
feature flag the following required changes were done\n\n- failed tests
due enabling the FF were fixed\n- FF setting was removed from test
configurations (integrations and Cypress tests)\n- FF logic was removed
from the codebase. Disabling the FF would require roll back test changes
as well. So just in case we have to disable the FF it's simpler to roll
back the PR's
commit.","sha":"f7d4f19096e0711021b8587fb4d0575998d18f3e"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Maxim Palenov <maxim.palenov@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-03-12 15:02:09 +11:00 committed by GitHub
parent ce08437e98
commit e51bb2215e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
104 changed files with 243 additions and 1510 deletions

View file

@ -74,7 +74,6 @@ disabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/diffable_rule_fields/common_fields/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/diffable_rule_fields/type_specific_fields/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless_complete_tier.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless_essentials_tier.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/serverless.config.ts
@ -82,12 +81,8 @@ disabled:
- 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

View file

@ -63,7 +63,6 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/diffable_rule_fields/common_fields/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/diffable_rule_fields/type_specific_fields/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess_basic_license.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess_trial_license.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/ess.config.ts
@ -71,12 +70,8 @@ enabled:
- 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

View file

@ -38416,7 +38416,6 @@
"xpack.securitySolution.detectionEngine.queryPreview.rulePreviewError": "Impossible d'afficher un aperçu de la règle",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsAriaLabel": "Afficher les détails",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsForRowAriaLabel": "Afficher les détails pour l'alerte ou l'événement de la ligne {ariaRowindex}, avec les colonnes {columnValues}",
"xpack.securitySolution.detectionEngine.relatedIntegrations.badgeTitle": "intégrations",
"xpack.securitySolution.detectionEngine.relatedIntegrations.disabledTitle": "Désactivé",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTitle": "Activé",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTooltip": "L'intégration est installée et une politique d'intégration avec la configuration requise existe. Assurez-vous que des agents Elastic sont affectés à cette politique pour ingérer des événements compatibles.",

View file

@ -38273,7 +38273,6 @@
"xpack.securitySolution.detectionEngine.queryPreview.rulePreviewError": "ルールをプレビューできませんでした",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsAriaLabel": "詳細を表示",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsForRowAriaLabel": "行 {ariaRowindex}、列 {columnValues} のアラートまたはイベントの詳細を表示",
"xpack.securitySolution.detectionEngine.relatedIntegrations.badgeTitle": "統合",
"xpack.securitySolution.detectionEngine.relatedIntegrations.disabledTitle": "無効",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTitle": "有効",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTooltip": "統合はインストールされ、必要な構成が行われている統合ポリシーが存在します。Elasticエージェントにこのポリシーが割り当てられていることを確認し、互換性があるイベントを取り込みます。",

View file

@ -38368,7 +38368,6 @@
"xpack.securitySolution.detectionEngine.queryPreview.rulePreviewError": "无法预览规则",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsAriaLabel": "查看详情",
"xpack.securitySolution.detectionEngine.queryPreview.viewDetailsForRowAriaLabel": "查看第 {ariaRowindex} 行的告警或事件的详细信息,其中列为 {columnValues}",
"xpack.securitySolution.detectionEngine.relatedIntegrations.badgeTitle": "集成",
"xpack.securitySolution.detectionEngine.relatedIntegrations.disabledTitle": "已禁用",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTitle": "已启用",
"xpack.securitySolution.detectionEngine.relatedIntegrations.enabledTooltip": "集成已安装,并且存在具有所需配置的集成策略。确保 Elastic 代理已分配此策略以采集兼容的事件。",

View file

@ -5,12 +5,6 @@
* 2.0.
*/
export enum PrebuiltRulesCustomizationDisabledReason {
License = 'License',
FeatureFlag = 'FeatureFlag',
}
export interface PrebuiltRulesCustomizationStatus {
isRulesCustomizationEnabled: boolean;
customizationDisabledReason?: PrebuiltRulesCustomizationDisabledReason;
}

View file

@ -184,17 +184,6 @@ export const allowedExperimentalValues = Object.freeze({
*/
jamfDataInAnalyzerEnabled: true,
/**
* Enables an ability to customize Elastic prebuilt rules.
*
* Ticket: https://github.com/elastic/kibana/issues/174168
* Owners: https://github.com/orgs/elastic/teams/security-detection-rule-management
* Added: on Jun 24, 2024 in https://github.com/elastic/kibana/pull/186823
* Turned: TBD
* Expires: TBD
*/
prebuiltRulesCustomizationEnabled: false,
/**
* Makes Elastic Defend integration's Malware On-Write Scan option available to edit.
*/

View file

@ -73,7 +73,6 @@ import { useRuleForms, useRuleIndexPattern } from '../form';
import { useEsqlIndex, useEsqlQueryForAboutStep } from '../../hooks';
import { CustomHeaderPageMemo } from '..';
import { usePrebuiltRulesCustomizationStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { ALERT_SUPPRESSION_FIELDS_FIELD_NAME } from '../../../rule_creation/components/alert_suppression_edit';
import { usePrebuiltRuleCustomizationUpsellingMessage } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rule_customization_upselling_message';
import { useRuleUpdateCallout } from '../../../rule_management/hooks/use_rule_update_callout';
@ -94,8 +93,7 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => {
const { application, triggersActionsUi } = useKibana().services;
const { navigateToApp } = application;
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
const canEditRule = isRulesCustomizationEnabled || !rule.immutable;
const prebuiltCustomizationUpsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
@ -219,7 +217,7 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => {
});
const customizationDisabledTooltip =
!canEditRule && customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.License
!canEditRule && !isRulesCustomizationEnabled
? prebuiltCustomizationUpsellingMessage
: undefined;

View file

@ -9,8 +9,6 @@ import { EuiBadge } from '@elastic/eui';
import React from 'react';
import type { RuleResponse } from '../../../../../common/api/detection_engine';
import { isCustomizedPrebuiltRule } from '../../../../../common/api/detection_engine';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { usePrebuiltRulesCustomizationStatus } from '../../logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import * as i18n from './translations';
interface CustomizedPrebuiltRuleBadgeProps {
@ -20,16 +18,6 @@ interface CustomizedPrebuiltRuleBadgeProps {
export const CustomizedPrebuiltRuleBadge: React.FC<CustomizedPrebuiltRuleBadgeProps> = ({
rule,
}) => {
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
if (
!isRulesCustomizationEnabled &&
customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.FeatureFlag
) {
return null;
}
if (rule === null || !isCustomizedPrebuiltRule(rule)) {
return null;
}

View file

@ -58,11 +58,7 @@ function createKibanaServicesMock(overrides?: Partial<CoreStart>) {
function createMockStore() {
const store = configureStore({
reducer: {
app: () => ({
enableExperimental: {
prebuiltRulesCustomizationEnabled: true,
},
}),
app: jest.fn(),
},
});

View file

@ -10,7 +10,6 @@ import { EuiButton, EuiToolTip } from '@elastic/eui';
import type { ReviewPrebuiltRuleUpgradeFilter } from '../../../../common/api/detection_engine/prebuilt_rules/common/review_prebuilt_rules_upgrade_filter';
import { FieldUpgradeStateEnum, type RuleUpgradeState } from '../model/prebuilt_rule_upgrade';
import { PerFieldRuleDiffTab } from '../components/rule_details/per_field_rule_diff_tab';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { useIsUpgradingSecurityPackages } from '../logic/use_upgrade_security_packages';
import { usePrebuiltRulesCustomizationStatus } from '../logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import { usePerformUpgradeRules } from '../logic/prebuilt_rules/use_perform_rule_upgrade';
@ -65,8 +64,7 @@ export function usePrebuiltRulesUpgrade({
filter,
onUpgrade,
}: UsePrebuiltRulesUpgradeParams) {
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
const isUpgradingSecurityPackages = useIsUpgradingSecurityPackages();
const [loadingRules, setLoadingRules] = useState<RuleSignatureId[]>([]);
@ -285,10 +283,7 @@ export function usePrebuiltRulesUpgrade({
ruleUpgradeState.current_rule.rule_source.is_customized;
let headerCallout = null;
if (
hasCustomizations &&
customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.License
) {
if (hasCustomizations && !isRulesCustomizationEnabled) {
headerCallout = <CustomizationDisabledCallout />;
} else if (hasRuleTypeChange && isRulesCustomizationEnabled) {
headerCallout = <RuleTypeChangeCallout hasCustomizations={hasCustomizations} />;
@ -339,12 +334,7 @@ export function usePrebuiltRulesUpgrade({
return [updatesTab, jsonViewTab];
},
[
rulesUpgradeState,
customizationDisabledReason,
isRulesCustomizationEnabled,
setRuleFieldResolvedValue,
]
[rulesUpgradeState, isRulesCustomizationEnabled, setRuleFieldResolvedValue]
);
const { rulePreviewFlyout, openRulePreview } = useRulePreviewFlyout({
rules: ruleUpgradeStates.map(({ target_rule: targetRule }) => targetRule),

View file

@ -6,7 +6,6 @@
*/
import type { UpsellingMessageId } from '@kbn/security-solution-upselling/service';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useUpsellingMessage } from '../../../../common/hooks/use_upselling';
/**
@ -17,9 +16,5 @@ import { useUpsellingMessage } from '../../../../common/hooks/use_upselling';
export const usePrebuiltRuleCustomizationUpsellingMessage = (messageId: UpsellingMessageId) => {
// Upselling message is returned when the license level is insufficient,
// otherwise it's undefined
const upsellingMessage = useUpsellingMessage(messageId);
// We show the upselling message only if the feature flag is enabled
const isFeatureFlagEnabled = useIsExperimentalFeatureEnabled('prebuiltRulesCustomizationEnabled');
return isFeatureFlagEnabled ? upsellingMessage : undefined;
return useUpsellingMessage(messageId);
};

View file

@ -6,8 +6,6 @@
*/
import type { PrebuiltRulesCustomizationStatus } from '../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { usePrebuiltRuleCustomizationUpsellingMessage } from './use_prebuilt_rule_customization_upselling_message';
/**
@ -17,23 +15,13 @@ import { usePrebuiltRuleCustomizationUpsellingMessage } from './use_prebuilt_rul
* enabled and returns the reason why it's disabled if it's the case.
*/
export const usePrebuiltRulesCustomizationStatus = (): PrebuiltRulesCustomizationStatus => {
const isFeatureFlagEnabled = useIsExperimentalFeatureEnabled('prebuiltRulesCustomizationEnabled');
// Upselling message is returned when the license level is insufficient,
// otherwise it's undefined
const upsellingMessage = usePrebuiltRuleCustomizationUpsellingMessage(
'prebuilt_rule_customization'
);
const isRulesCustomizationEnabled = isFeatureFlagEnabled && !upsellingMessage;
let customizationDisabledReason;
if (!isRulesCustomizationEnabled) {
customizationDisabledReason = !isFeatureFlagEnabled
? PrebuiltRulesCustomizationDisabledReason.FeatureFlag
: PrebuiltRulesCustomizationDisabledReason.License;
}
return {
isRulesCustomizationEnabled,
customizationDisabledReason,
isRulesCustomizationEnabled: !upsellingMessage,
};
};

View file

@ -32,7 +32,6 @@ import { getNormalizedSeverity } from '../helpers';
import type { UpgradePrebuiltRulesTableActions } from './upgrade_prebuilt_rules_table_context';
import { useUpgradePrebuiltRulesTableContext } from './upgrade_prebuilt_rules_table_context';
import { usePrebuiltRulesCustomizationStatus } from '../../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
export type TableColumn = EuiBasicTableColumn<RuleUpgradeState>;
@ -110,7 +109,7 @@ const INTEGRATIONS_COLUMN: TableColumn = {
return <IntegrationsPopover relatedIntegrations={integrations} />;
},
width: '143px',
width: '70px',
truncateText: true,
};
@ -241,21 +240,12 @@ export const useUpgradePrebuiltRulesTableColumns = (): TableColumn[] => {
actions: { upgradeRules, openRulePreview },
} = useUpgradePrebuiltRulesTableContext();
const isDisabled = isRefetching || isUpgradingSecurityPackages;
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
const shouldShowModifiedColumn =
isRulesCustomizationEnabled ||
customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.License;
if (shouldShowModifiedColumn) {
INTEGRATIONS_COLUMN.width = '70px';
}
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
return useMemo(
() => [
RULE_NAME_COLUMN,
...(shouldShowModifiedColumn ? [MODIFIED_COLUMN] : []),
MODIFIED_COLUMN,
CONFLICT_COLUMN,
...(showRelatedIntegrations ? [INTEGRATIONS_COLUMN] : []),
TAGS_COLUMN,
@ -293,7 +283,6 @@ export const useUpgradePrebuiltRulesTableColumns = (): TableColumn[] => {
: []),
],
[
shouldShowModifiedColumn,
showRelatedIntegrations,
hasCRUDPermissions,
upgradeRules,

View file

@ -46,8 +46,6 @@ import { useRulesTableActions } from './use_rules_table_actions';
import { MlRuleWarningPopover } from '../ml_rule_warning_popover/ml_rule_warning_popover';
import { getMachineLearningJobId } from '../../../../detections/pages/detection_engine/rules/helpers';
import type { TimeRange } from '../../../rule_gaps/types';
import { usePrebuiltRulesCustomizationStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
export type TableColumn = EuiBasicTableColumn<Rule> | EuiTableActionsColumnType<Rule>;
@ -260,7 +258,7 @@ const MODIFIED_COLUMN: TableColumn = {
</EuiToolTip>
);
},
width: '90px',
width: '70px',
truncateText: true,
};
@ -296,11 +294,6 @@ export const useRulesColumns = ({
});
const ruleNameColumn = useRuleNameColumn();
const [showRelatedIntegrations] = useUiSetting$<boolean>(SHOW_RELATED_INTEGRATIONS_SETTING);
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
const shouldShowModifiedColumn =
isRulesCustomizationEnabled ||
customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.License;
const enabledColumn = useEnabledColumn({
hasCRUDPermissions,
@ -316,14 +309,10 @@ export const useRulesColumns = ({
});
const snoozeColumn = useRuleSnoozeColumn();
if (shouldShowModifiedColumn) {
INTEGRATIONS_COLUMN.width = '70px';
}
return useMemo(
() => [
ruleNameColumn,
...(shouldShowModifiedColumn ? [MODIFIED_COLUMN] : []),
MODIFIED_COLUMN,
...(showRelatedIntegrations ? [INTEGRATIONS_COLUMN] : []),
TAGS_COLUMN,
{
@ -395,7 +384,6 @@ export const useRulesColumns = ({
],
[
ruleNameColumn,
shouldShowModifiedColumn,
showRelatedIntegrations,
executionStatusColumn,
snoozeColumn,
@ -423,11 +411,6 @@ export const useMonitoringColumns = ({
});
const ruleNameColumn = useRuleNameColumn();
const [showRelatedIntegrations] = useUiSetting$<boolean>(SHOW_RELATED_INTEGRATIONS_SETTING);
const { isRulesCustomizationEnabled, customizationDisabledReason } =
usePrebuiltRulesCustomizationStatus();
const shouldShowModifiedColumn =
isRulesCustomizationEnabled ||
customizationDisabledReason === PrebuiltRulesCustomizationDisabledReason.License;
const enabledColumn = useEnabledColumn({
hasCRUDPermissions,
@ -442,17 +425,13 @@ export const useMonitoringColumns = ({
mlJobs,
});
if (shouldShowModifiedColumn) {
INTEGRATIONS_COLUMN.width = '70px';
}
return useMemo(
() => [
{
...ruleNameColumn,
width: '28%',
},
...(shouldShowModifiedColumn ? [MODIFIED_COLUMN] : []),
MODIFIED_COLUMN,
...(showRelatedIntegrations ? [INTEGRATIONS_COLUMN] : []),
TAGS_COLUMN,
{
@ -572,7 +551,6 @@ export const useMonitoringColumns = ({
executionStatusColumn,
hasCRUDPermissions,
ruleNameColumn,
shouldShowModifiedColumn,
showRelatedIntegrations,
]
);

View file

@ -26,7 +26,6 @@ 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 { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
export const useRulesTableActions = ({
showExceptionsDuplicateConfirmation,
@ -47,9 +46,6 @@ export const useRulesTableActions = ({
const { bulkExport } = useBulkExport();
const downloadExportedRules = useDownloadExportedRules();
const { scheduleRuleRun } = useScheduleRuleRun();
const isPrebuiltRulesCustomizationFeatureFlagEnabled = useIsExperimentalFeatureEnabled(
'prebuiltRulesCustomizationEnabled'
);
return [
{
@ -120,7 +116,6 @@ export const useRulesTableActions = ({
await downloadExportedRules(response);
}
},
enabled: (rule: Rule) => isPrebuiltRulesCustomizationFeatureFlagEnabled || !rule.immutable,
},
{
type: 'icon',

View file

@ -15,8 +15,6 @@ import {
EuiText,
EuiSpacer,
} from '@elastic/eui';
import { usePrebuiltRulesCustomizationStatus } from '../../../../../detection_engine/rule_management/logic/prebuilt_rules/use_prebuilt_rules_customization_status';
import type { RelatedIntegrationArray } from '../../../../../../common/api/detection_engine/model/rule_schema';
import { IntegrationDescription } from '../integrations_description';
import { useRelatedIntegrations } from '../use_related_integrations';
@ -55,7 +53,6 @@ const IntegrationListItem = styled('li')`
const IntegrationsPopoverComponent = ({ relatedIntegrations }: IntegrationsPopoverProps) => {
const [isPopoverOpen, setPopoverOpen] = useState(false);
const { integrations, isLoaded } = useRelatedIntegrations(relatedIntegrations);
const { isRulesCustomizationEnabled } = usePrebuiltRulesCustomizationStatus();
const enabledIntegrations = useMemo(() => {
return integrations.filter(
@ -66,14 +63,10 @@ const IntegrationsPopoverComponent = ({ relatedIntegrations }: IntegrationsPopov
const numIntegrations = integrations.length;
const numIntegrationsEnabled = enabledIntegrations.length;
const badgeTitle = useMemo(() => {
if (isRulesCustomizationEnabled) {
return isLoaded ? `${numIntegrationsEnabled}/${numIntegrations}` : `${numIntegrations}`;
}
return isLoaded
? `${numIntegrationsEnabled}/${numIntegrations} ${i18n.INTEGRATIONS_BADGE}`
: `${numIntegrations} ${i18n.INTEGRATIONS_BADGE}`;
}, [isLoaded, isRulesCustomizationEnabled, numIntegrations, numIntegrationsEnabled]);
const badgeTitle = useMemo(
() => (isLoaded ? `${numIntegrationsEnabled}/${numIntegrations}` : `${numIntegrations}`),
[isLoaded, numIntegrations, numIntegrationsEnabled]
);
return (
<IntegrationsPopoverWrapper

View file

@ -52,13 +52,6 @@ export const INTEGRATIONS_ENABLED_TOOLTIP = i18n.translate(
}
);
export const INTEGRATIONS_BADGE = i18n.translate(
'xpack.securitySolution.detectionEngine.relatedIntegrations.badgeTitle',
{
defaultMessage: 'integrations',
}
);
export const INTEGRATIONS_POPOVER_TITLE = (integrationsCount: number) =>
i18n.translate('xpack.securitySolution.detectionEngine.relatedIntegrations.popoverTitle', {
values: { integrationsCount },

View file

@ -35,7 +35,6 @@ 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,9 +72,6 @@ const RuleActionsOverflowComponent = ({
application: { navigateToApp },
telemetry,
} = useKibana().services;
const isPrebuiltRulesCustomizationFeatureFlagEnabled = useIsExperimentalFeatureEnabled(
'prebuiltRulesCustomizationEnabled'
);
const { startTransaction } = useStartTransaction();
const { executeBulkAction } = useExecuteBulkAction({ suppressSuccessToast: true });
const { bulkExport } = useBulkExport();
@ -141,10 +137,7 @@ const RuleActionsOverflowComponent = ({
<EuiContextMenuItem
key={i18nActions.EXPORT_RULE}
icon="exportAction"
disabled={
!userHasPermissions ||
(isPrebuiltRulesCustomizationFeatureFlagEnabled === false && rule.immutable)
}
disabled={!userHasPermissions}
data-test-subj="rules-details-export-rule"
onClick={async () => {
startTransaction({ name: SINGLE_RULE_ACTIONS.EXPORT });
@ -213,7 +206,6 @@ const RuleActionsOverflowComponent = ({
rule,
canDuplicateRuleWithActions,
userHasPermissions,
isPrebuiltRulesCustomizationFeatureFlagEnabled,
startTransaction,
closePopover,
showBulkDuplicateExceptionsConfirmation,

View file

@ -43,7 +43,6 @@ 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
@ -278,22 +277,13 @@ 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,
rules.map(({ params }) => params.ruleId),
exporter,
request,
actionsClient,
isPrebuiltRulesExportAllowed
actionsClient
);
const responseBody = `${exported.rulesNdjson}${exported.exceptionLists}${exported.actionConnectors}${exported.exportDetails}`;

View file

@ -15,15 +15,11 @@ import {
} from '../../../../../../../common/api/detection_engine/rule_management';
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
import type { ConfigType } from '../../../../../../config';
import {
getNonPackagedRulesCount,
getRulesCount,
} from '../../../logic/search/get_existing_prepackaged_rules';
import { getRulesCount } from '../../../logic/search/get_existing_prepackaged_rules';
import { getExportByObjectIds } from '../../../logic/export/get_export_by_object_ids';
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,
@ -68,18 +64,11 @@ export const exportRulesRoute = (
const rulesClient = await ctx.alerting.getRulesClient();
const exceptionsClient = ctx.lists?.getExceptionListClient();
const actionsClient = ctx.actions.getActionsClient();
const detectionRulesClient = ctx.securitySolution.getDetectionRulesClient();
const { getExporter, getClient } = ctx.core.savedObjects;
const client = getClient({ includedHiddenTypes: ['action'] });
const actionsExporter = getExporter(client);
const prebuiltRulesCustomizationStatus = detectionRulesClient.getRuleCustomizationStatus();
const isPrebuiltRulesExportAllowed =
prebuiltRulesCustomizationStatus.isRulesCustomizationEnabled ||
prebuiltRulesCustomizationStatus.customizationDisabledReason ===
PrebuiltRulesCustomizationDisabledReason.License;
try {
const exportSizeLimit = config.maxRuleImportExportSize;
@ -89,18 +78,10 @@ export const exportRulesRoute = (
body: `Can't export more than ${exportSizeLimit} rules`,
});
} else {
let rulesCount = 0;
if (isPrebuiltRulesExportAllowed) {
rulesCount = await getRulesCount({
rulesClient,
filter: '',
});
} else {
rulesCount = await getNonPackagedRulesCount({
rulesClient,
});
}
const rulesCount = await getRulesCount({
rulesClient,
filter: '',
});
if (rulesCount > exportSizeLimit) {
return siemResponse.error({
@ -118,16 +99,14 @@ export const exportRulesRoute = (
request.body.objects.map((obj) => obj.rule_id),
actionsExporter,
request,
actionsClient,
isPrebuiltRulesExportAllowed
actionsClient
)
: await getExportAll(
rulesClient,
exceptionsClient,
actionsExporter,
request,
actionsClient,
isPrebuiltRulesExportAllowed
actionsClient
);
const responseBody = request.query.exclude_export_details

View file

@ -31,7 +31,6 @@ 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');
@ -41,7 +40,11 @@ jest.mock('../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_cli
createPrebuiltRuleAssetsClient: () => mockPrebuiltRuleAssetsClient,
}));
describe('Import rules route', () => {
// Skipped in https://github.com/elastic/kibana/pull/212761
// We have to find a way to use original detectionRulesClient.importRules() while mocking detectionRulesClient.importRule().
// detectionRulesClient.importRules() uses detectionRulesClient.importRule() under the hood.
// Without proper mocking this test suite will test the mock.
describe.skip('Import rules route', () => {
let config: ReturnType<typeof configMock.createDefault>;
let server: ReturnType<typeof serverMock.create>;
let request: ReturnType<typeof requestMock.create>;
@ -58,10 +61,6 @@ describe('Import rules route', () => {
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
clients.detectionRulesClient.createCustomRule.mockResolvedValue(getRulesSchemaMock());
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(
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())

View file

@ -18,7 +18,6 @@ import {
import { DETECTION_ENGINE_RULES_IMPORT_URL } from '../../../../../../../common/constants';
import type { ConfigType } from '../../../../../../config';
import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../../../types';
import type { ImportRuleResponse } from '../../../../routes/utils';
import {
buildSiemResponse,
createBulkErrorObject,
@ -29,8 +28,7 @@ import { createPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic
import { importRuleActionConnectors } from '../../../logic/import/action_connectors/import_rule_action_connectors';
import { createRuleSourceImporter } from '../../../logic/import/rule_source_importer';
import { importRules } from '../../../logic/import/import_rules';
// eslint-disable-next-line no-restricted-imports
import { importRulesLegacy } from '../../../logic/import/import_rules_legacy';
import { createPromiseFromRuleImportStream } from '../../../logic/import/create_promise_from_rule_import_stream';
import { importRuleExceptions } from '../../../logic/import/import_rule_exceptions';
import { isRuleToImport } from '../../../logic/import/utils';
@ -39,7 +37,6 @@ 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';
import { createPrebuiltRuleObjectsClient } from '../../../../prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client';
const CHUNK_PARSED_OBJECT_SIZE = 50;
@ -89,7 +86,6 @@ export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: C
const rulesClient = await ctx.alerting.getRulesClient();
const detectionRulesClient = ctx.securitySolution.getDetectionRulesClient();
const ruleCustomizationStatus = detectionRulesClient.getRuleCustomizationStatus();
const actionsClient = ctx.actions.getActionsClient();
const actionSOClient = ctx.core.savedObjects.getClient({
includedHiddenTypes: ['action'],
@ -162,34 +158,18 @@ export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: C
context: ctx.securitySolution,
prebuiltRuleAssetsClient: createPrebuiltRuleAssetsClient(savedObjectsClient),
prebuiltRuleObjectsClient: createPrebuiltRuleObjectsClient(rulesClient),
ruleCustomizationStatus: detectionRulesClient.getRuleCustomizationStatus(),
});
const [parsedRules, parsedRuleErrors] = partition(isRuleToImport, parsedRuleStream);
const ruleChunks = chunk(CHUNK_PARSED_OBJECT_SIZE, parsedRules);
let importRuleResponse: ImportRuleResponse[] = [];
if (
ruleCustomizationStatus.customizationDisabledReason ===
PrebuiltRulesCustomizationDisabledReason.FeatureFlag
) {
importRuleResponse = await importRulesLegacy({
ruleChunks,
overwriteRules: request.query.overwrite,
allowMissingConnectorSecrets: !!actionConnectors.length,
detectionRulesClient,
savedObjectsClient,
});
} else {
importRuleResponse = await importRules({
ruleChunks,
overwriteRules: request.query.overwrite,
allowMissingConnectorSecrets: !!actionConnectors.length,
ruleSourceImporter,
detectionRulesClient,
});
}
const importRuleResponse = await importRules({
ruleChunks,
overwriteRules: request.query.overwrite,
allowMissingConnectorSecrets: !!actionConnectors.length,
ruleSourceImporter,
detectionRulesClient,
});
const parseErrors = parsedRuleErrors.map((error) =>
createBulkErrorObject({

View file

@ -96,7 +96,6 @@ export const bulkEditRules = async ({
baseRule: baseVersionsMap.get(nextRule.rule_id),
currentRule: convertAlertingRuleToRuleResponse(currentRule),
nextRule,
ruleCustomizationStatus,
});
}

View file

@ -15,7 +15,6 @@ import {
BulkActionsDryRunErrCodeEnum,
} from '../../../../../../common/api/detection_engine/rule_management';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { isEsqlRule } from '../../../../../../common/detection_engine/utils';
import { isMlRule } from '../../../../../../common/machine_learning/helpers';
import { invariant } from '../../../../../../common/utils/invariant';
@ -108,10 +107,7 @@ export const validateBulkEditRule = async ({
if (!canRuleBeEdited) {
await throwDryRunError(
() => invariant(canRuleBeEdited, "Elastic rule can't be edited"),
ruleCustomizationStatus.customizationDisabledReason ===
PrebuiltRulesCustomizationDisabledReason.FeatureFlag
? BulkActionsDryRunErrCodeEnum.IMMUTABLE
: BulkActionsDryRunErrCodeEnum.PREBUILT_CUSTOMIZATION_LICENSE
BulkActionsDryRunErrCodeEnum.PREBUILT_CUSTOMIZATION_LICENSE
);
}
}

View file

@ -22,7 +22,6 @@ import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -53,7 +52,6 @@ describe('DetectionRulesClient.createCustomRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -22,7 +22,6 @@ import { throwAuthzError } from '../../../../machine_learning/validation';
import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -46,7 +45,6 @@ describe('DetectionRulesClient.createPrebuiltRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -14,7 +14,6 @@ import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
import type { ExperimentalFeatures } from '../../../../../../common';
jest.mock('../../../../machine_learning/authz');
@ -34,7 +33,6 @@ describe('DetectionRulesClient.deleteRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -19,7 +19,6 @@ import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { getRuleByRuleId } from './methods/get_rule_by_rule_id';
import { getValidatedRuleToImportMock } from '../../../../../../common/api/detection_engine/rule_management/mocks';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -55,7 +54,6 @@ describe('DetectionRulesClient.importRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -18,7 +18,6 @@ import { importRule } from './methods/import_rule';
import { createRuleImportErrorObject } from '../import/errors';
import { checkRuleExceptionReferences } from '../import/check_rule_exception_references';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('./methods/import_rule');
@ -36,7 +35,6 @@ describe('detectionRulesClient.importRules', () => {
mlAuthz: buildMlAuthz(),
savedObjectsClient: savedObjectsClientMock.create(),
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});

View file

@ -23,7 +23,6 @@ import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -54,7 +53,6 @@ describe('DetectionRulesClient.patchRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -12,7 +12,6 @@ import type { SavedObjectsClientContract } from '@kbn/core/server';
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
import type { ILicense } from '@kbn/licensing-plugin/server';
import type { RuleResponse } from '../../../../../../common/api/detection_engine/model/rule_schema';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { withSecuritySpan } from '../../../../../utils/with_security_span';
import type { MlAuthz } from '../../../../machine_learning/authz';
import type { ProductFeaturesService } from '../../../../product_features_service';
@ -37,14 +36,12 @@ import { patchRule } from './methods/patch_rule';
import { updateRule } from './methods/update_rule';
import { upgradePrebuiltRule } from './methods/upgrade_prebuilt_rule';
import { MINIMUM_RULE_CUSTOMIZATION_LICENSE } from '../../../../../../common/constants';
import type { ExperimentalFeatures } from '../../../../../../common';
interface DetectionRulesClientParams {
actionsClient: ActionsClient;
rulesClient: RulesClient;
savedObjectsClient: SavedObjectsClientContract;
mlAuthz: MlAuthz;
experimentalFeatures: ExperimentalFeatures;
productFeaturesService: ProductFeaturesService;
license: ILicense;
}
@ -54,7 +51,6 @@ export const createDetectionRulesClient = ({
rulesClient,
mlAuthz,
savedObjectsClient,
experimentalFeatures,
productFeaturesService,
license,
}: DetectionRulesClientParams): IDetectionRulesClient => {
@ -63,9 +59,7 @@ export const createDetectionRulesClient = ({
return {
getRuleCustomizationStatus() {
/**
* The prebuilt rules customization feature is gated by two things:
* 1. The feature flag `prebuiltRulesCustomizationEnabled` in the config.
* 2. The license level.
* The prebuilt rules customization feature is gated by the license level.
*
* The license level is verified against the minimum required level for
* the feature (Enterprise). However, since Serverless always operates at
@ -74,20 +68,11 @@ export const createDetectionRulesClient = ({
* unavailable features are disabled.
*/
const isRulesCustomizationEnabled =
experimentalFeatures.prebuiltRulesCustomizationEnabled &&
license.hasAtLeast(MINIMUM_RULE_CUSTOMIZATION_LICENSE) &&
productFeaturesService.isEnabled(ProductFeatureKey.prebuiltRuleCustomization);
let customizationDisabledReason;
if (!isRulesCustomizationEnabled) {
customizationDisabledReason = !experimentalFeatures.prebuiltRulesCustomizationEnabled
? PrebuiltRulesCustomizationDisabledReason.FeatureFlag
: PrebuiltRulesCustomizationDisabledReason.License;
}
return {
isRulesCustomizationEnabled,
customizationDisabledReason,
};
},
async createCustomRule(args: CreateCustomRuleArgs): Promise<RuleResponse> {
@ -130,7 +115,6 @@ export const createDetectionRulesClient = ({
prebuiltRuleAssetClient,
mlAuthz,
ruleUpdate,
ruleCustomizationStatus: this.getRuleCustomizationStatus(),
});
});
},
@ -143,7 +127,6 @@ export const createDetectionRulesClient = ({
prebuiltRuleAssetClient,
mlAuthz,
rulePatch,
ruleCustomizationStatus: this.getRuleCustomizationStatus(),
});
});
},
@ -162,7 +145,6 @@ export const createDetectionRulesClient = ({
ruleAsset,
mlAuthz,
prebuiltRuleAssetClient,
ruleCustomizationStatus: this.getRuleCustomizationStatus(),
});
});
},
@ -175,7 +157,6 @@ export const createDetectionRulesClient = ({
importRulePayload: args,
mlAuthz,
prebuiltRuleAssetClient,
ruleCustomizationStatus: this.getRuleCustomizationStatus(),
});
});
},

View file

@ -23,7 +23,6 @@ import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -54,7 +53,6 @@ describe('DetectionRulesClient.updateRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -24,7 +24,6 @@ import { createDetectionRulesClient } from './detection_rules_client';
import type { IDetectionRulesClient } from './detection_rules_client_interface';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
import type { ExperimentalFeatures } from '../../../../../../common';
import { createProductFeaturesServiceMock } from '../../../../product_features_service/mocks';
jest.mock('../../../../machine_learning/authz');
@ -52,7 +51,6 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => {
mlAuthz,
savedObjectsClient,
license: licenseMock.createLicenseMock(),
experimentalFeatures: { prebuiltRulesCustomizationEnabled: true } as ExperimentalFeatures,
productFeaturesService: createProductFeaturesServiceMock(),
});
});

View file

@ -19,16 +19,11 @@ import {
getSavedQuerySchemaMock,
getThreatMatchingSchemaMock,
} from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { createPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client';
import { applyRulePatch } from './apply_rule_patch';
const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient();
const ruleCustomizationStatus: PrebuiltRulesCustomizationStatus = {
isRulesCustomizationEnabled: true,
};
describe('applyRulePatch', () => {
describe('EQL', () => {
test('should accept EQL params when existing rule type is EQL', async () => {
@ -42,7 +37,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -71,7 +65,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -101,7 +94,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError(
'event_category_override: Expected string, received number, tiebreaker_field: Expected string, received number, timestamp_field: Expected string, received number'
@ -127,7 +119,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError('alert_suppression.group_by: Expected array, received string');
});
@ -143,7 +134,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -164,7 +154,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError(
'threat_query: Expected string, received number, threat_indicator_path: Expected string, received number'
@ -181,7 +170,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -202,7 +190,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError(
"index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'"
@ -219,7 +206,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -240,7 +226,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError(
"index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'"
@ -259,7 +244,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -284,7 +268,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError('threshold.value: Expected number, received string');
});
@ -302,7 +285,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -326,7 +308,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -349,7 +330,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -374,7 +354,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -397,7 +376,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -416,7 +394,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError('anomaly_threshold: Expected number, received string');
});
@ -433,7 +410,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
@ -456,7 +432,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({
@ -475,7 +450,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
})
).rejects.toThrowError('new_terms_fields: Expected array, received string');
});
@ -498,7 +472,6 @@ describe('applyRulePatch', () => {
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
expect(patchedRule).toEqual(
expect.objectContaining({

View file

@ -46,13 +46,11 @@ import {
import { assertUnreachable } from '../../../../../../../common/utility_types';
import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client';
import { calculateRuleSource } from './rule_source/calculate_rule_source';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
interface ApplyRulePatchProps {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
existingRule: RuleResponse;
rulePatch: PatchRuleRequestBody;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
// eslint-disable-next-line complexity
@ -60,7 +58,6 @@ export const applyRulePatch = async ({
rulePatch,
existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
}: ApplyRulePatchProps): Promise<RuleResponse> => {
const typeSpecificParams = patchTypeSpecificParams(rulePatch, existingRule);
@ -126,7 +123,6 @@ export const applyRulePatch = async ({
nextRule,
currentRule: existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
return nextRule;

View file

@ -9,7 +9,6 @@ import type {
RuleResponse,
RuleUpdateProps,
} from '../../../../../../../common/api/detection_engine/model/rule_schema';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client';
import { applyRuleDefaults } from './apply_rule_defaults';
import { calculateRuleSource } from './rule_source/calculate_rule_source';
@ -18,14 +17,12 @@ interface ApplyRuleUpdateProps {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
existingRule: RuleResponse;
ruleUpdate: RuleUpdateProps;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export const applyRuleUpdate = async ({
prebuiltRuleAssetClient,
existingRule,
ruleUpdate,
ruleCustomizationStatus,
}: ApplyRuleUpdateProps): Promise<RuleResponse> => {
const nextRule: RuleResponse = {
...applyRuleDefaults(ruleUpdate),
@ -50,7 +47,6 @@ export const applyRuleUpdate = async ({
nextRule,
currentRule: existingRule,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
});
return nextRule;

View file

@ -11,34 +11,19 @@ import type { PrebuiltRuleAsset } from '../../../../../prebuilt_rules';
import { calculateRuleFieldsDiff } from '../../../../../prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff';
import { convertRuleToDiffable } from '../../../../../../../../common/detection_engine/prebuilt_rules/diff/convert_rule_to_diffable';
import { convertPrebuiltRuleAssetToRuleResponse } from '../../converters/convert_prebuilt_rule_asset_to_rule_response';
import {
PrebuiltRulesCustomizationDisabledReason,
type PrebuiltRulesCustomizationStatus,
} from '../../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
interface CalculateIsCustomizedArgs {
baseRule: PrebuiltRuleAsset | undefined;
nextRule: RuleResponse;
// Current rule can be undefined in case of importing a prebuilt rule that is not installed
currentRule: RuleResponse | undefined;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export function calculateIsCustomized({
baseRule,
nextRule,
currentRule,
ruleCustomizationStatus,
}: CalculateIsCustomizedArgs) {
if (
ruleCustomizationStatus.customizationDisabledReason ===
PrebuiltRulesCustomizationDisabledReason.FeatureFlag
) {
// We don't want to accidentally mark rules as customized when customization
// is disabled.
return false;
}
if (baseRule) {
// Base version is available, so we can determine the customization status
// by comparing the base version with the next version

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { PrebuiltRulesCustomizationDisabledReason } from '../../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { createPrebuiltRuleAssetsClient } from '../../../../../prebuilt_rules/logic/rule_assets/__mocks__/prebuilt_rule_assets_client';
import { applyRuleDefaults } from '../apply_rule_defaults';
import { calculateRuleSource } from './calculate_rule_source';
@ -37,10 +35,6 @@ const getSampleRule = () => {
};
};
const ruleCustomizationStatus: PrebuiltRulesCustomizationStatus = {
isRulesCustomizationEnabled: true,
};
describe('calculateRuleSource', () => {
it('returns an internal rule source when the rule is not prebuilt', async () => {
const rule = getSampleRule();
@ -50,7 +44,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: undefined,
ruleCustomizationStatus,
});
expect(result).toEqual({
type: 'internal',
@ -68,7 +61,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -90,7 +82,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -114,7 +105,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -124,56 +114,6 @@ describe('calculateRuleSource', () => {
);
});
it('returns is_customized false when the rule is customized but customization feature flag is disabled', async () => {
const rule = getSampleRule();
rule.immutable = true;
rule.name = 'Updated name';
const baseRule = getSampleRuleAsset();
prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]);
const result = await calculateRuleSource({
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus: {
isRulesCustomizationEnabled: false,
customizationDisabledReason: PrebuiltRulesCustomizationDisabledReason.FeatureFlag,
},
});
expect(result).toEqual(
expect.objectContaining({
type: 'external',
is_customized: false,
})
);
});
it('returns is_customized true when the rule is customized and customization is disabled because of license', async () => {
const rule = getSampleRule();
rule.immutable = true;
rule.name = 'Updated name';
const baseRule = getSampleRuleAsset();
prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]);
const result = await calculateRuleSource({
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus: {
isRulesCustomizationEnabled: false,
customizationDisabledReason: PrebuiltRulesCustomizationDisabledReason.License,
},
});
expect(result).toEqual(
expect.objectContaining({
type: 'external',
is_customized: true,
})
);
});
describe('missing base versions', () => {
it('return is_customized false when the base version and current version are missing', async () => {
const rule = getSampleRule();
@ -186,7 +126,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: undefined,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -211,7 +150,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -236,7 +174,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule: rule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({
@ -266,7 +203,6 @@ describe('calculateRuleSource', () => {
prebuiltRuleAssetClient,
nextRule,
currentRule: rule,
ruleCustomizationStatus,
});
expect(result).toEqual(
expect.objectContaining({

View file

@ -9,7 +9,6 @@ import type {
RuleResponse,
RuleSource,
} from '../../../../../../../../common/api/detection_engine/model/rule_schema';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import type { PrebuiltRuleAsset } from '../../../../../prebuilt_rules';
import type { IPrebuiltRuleAssetsClient } from '../../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client';
import { calculateIsCustomized } from './calculate_is_customized';
@ -18,14 +17,12 @@ interface CalculateRuleSourceProps {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
nextRule: RuleResponse;
currentRule: RuleResponse | undefined;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export async function calculateRuleSource({
prebuiltRuleAssetClient,
nextRule,
currentRule,
ruleCustomizationStatus,
}: CalculateRuleSourceProps): Promise<RuleSource> {
if (nextRule.immutable) {
// This is a prebuilt rule and, despite the name, they are not immutable. So
@ -42,7 +39,6 @@ export async function calculateRuleSource({
baseRule,
nextRule,
currentRule,
ruleCustomizationStatus,
});
return {

View file

@ -19,7 +19,6 @@ import { validateMlAuth, toggleRuleEnabledOnUpdate } from '../utils';
import { createRule } from './create_rule';
import { getRuleByRuleId } from './get_rule_by_rule_id';
import { createRuleImportErrorObject } from '../../import/errors';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
interface ImportRuleOptions {
actionsClient: ActionsClient;
@ -27,7 +26,6 @@ interface ImportRuleOptions {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
importRulePayload: ImportRuleArgs;
mlAuthz: MlAuthz;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export const importRule = async ({
@ -36,7 +34,6 @@ export const importRule = async ({
importRulePayload,
prebuiltRuleAssetClient,
mlAuthz,
ruleCustomizationStatus,
}: ImportRuleOptions): Promise<RuleResponse> => {
const { ruleToImport, overwriteRules, overrideFields, allowMissingConnectorSecrets } =
importRulePayload;
@ -63,7 +60,6 @@ export const importRule = async ({
prebuiltRuleAssetClient,
existingRule,
ruleUpdate: rule,
ruleCustomizationStatus,
});
// applyRuleUpdate prefers the existing rule's values for `rule_source` and `immutable`, but we want to use the importing rule's calculated values
ruleWithUpdates = { ...ruleWithUpdates, ...overrideFields };

View file

@ -19,7 +19,6 @@ 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
@ -74,11 +73,7 @@ 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
) {
if (isCustomized && !ruleCustomizationStatus.isRulesCustomizationEnabled) {
return createRuleImportErrorObject({
message: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.licenseInsufficientToImportCustomizedPrebuiltRule',

View file

@ -21,7 +21,6 @@ import { convertAlertingRuleToRuleResponse } from '../converters/convert_alertin
import { convertRuleResponseToAlertingRule } from '../converters/convert_rule_response_to_alerting_rule';
import { ClientError, toggleRuleEnabledOnUpdate, validateMlAuth } from '../utils';
import { getRuleByIdOrRuleId } from './get_rule_by_id_or_rule_id';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
interface PatchRuleOptions {
actionsClient: ActionsClient;
@ -29,7 +28,6 @@ interface PatchRuleOptions {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
rulePatch: RulePatchProps;
mlAuthz: MlAuthz;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export const patchRule = async ({
@ -38,7 +36,6 @@ export const patchRule = async ({
prebuiltRuleAssetClient,
rulePatch,
mlAuthz,
ruleCustomizationStatus,
}: PatchRuleOptions): Promise<RuleResponse> => {
const { rule_id: ruleId, id } = rulePatch;
@ -61,7 +58,6 @@ export const patchRule = async ({
prebuiltRuleAssetClient,
existingRule,
rulePatch,
ruleCustomizationStatus,
});
const patchedInternalRule = await rulesClient.update({

View file

@ -20,7 +20,6 @@ import type { RuleUpdateProps } from '../../../../../../../common/api/detection_
import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic/rule_assets/prebuilt_rule_assets_client';
import { getRuleByIdOrRuleId } from './get_rule_by_id_or_rule_id';
import { convertAlertingRuleToRuleResponse } from '../converters/convert_alerting_rule_to_rule_response';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
interface UpdateRuleArguments {
actionsClient: ActionsClient;
@ -28,7 +27,6 @@ interface UpdateRuleArguments {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
ruleUpdate: RuleUpdateProps;
mlAuthz: MlAuthz;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}
export const updateRule = async ({
@ -37,7 +35,6 @@ export const updateRule = async ({
prebuiltRuleAssetClient,
ruleUpdate,
mlAuthz,
ruleCustomizationStatus,
}: UpdateRuleArguments): Promise<RuleResponse> => {
const { rule_id: ruleId, id } = ruleUpdate;
@ -60,7 +57,6 @@ export const updateRule = async ({
prebuiltRuleAssetClient,
existingRule,
ruleUpdate,
ruleCustomizationStatus,
});
const updatedRule = await rulesClient.update({

View file

@ -18,7 +18,6 @@ import { applyRuleUpdate } from '../mergers/apply_rule_update';
import { ClientError, validateMlAuth } from '../utils';
import { createRule } from './create_rule';
import { getRuleByRuleId } from './get_rule_by_rule_id';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
export const upgradePrebuiltRule = async ({
actionsClient,
@ -26,14 +25,12 @@ export const upgradePrebuiltRule = async ({
ruleAsset,
mlAuthz,
prebuiltRuleAssetClient,
ruleCustomizationStatus,
}: {
actionsClient: ActionsClient;
rulesClient: RulesClient;
ruleAsset: PrebuiltRuleAsset;
mlAuthz: MlAuthz;
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}): Promise<RuleResponse> => {
await validateMlAuth(mlAuthz, ruleAsset.type);
@ -76,7 +73,6 @@ export const upgradePrebuiltRule = async ({
prebuiltRuleAssetClient,
existingRule,
ruleUpdate: ruleAsset,
ruleCustomizationStatus,
});
const updatedInternalRule = await rulesClient.update({

View file

@ -11,7 +11,7 @@ import type { ISavedObjectsExporter, KibanaRequest } from '@kbn/core/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { RulesClient } from '@kbn/alerting-plugin/server';
import type { ActionsClient } from '@kbn/actions-plugin/server';
import { getNonPackagedRules, getRules } from '../search/get_existing_prepackaged_rules';
import { getRules } from '../search/get_existing_prepackaged_rules';
import { getExportDetailsNdjson } from './get_export_details_ndjson';
import { transformAlertsToRules } from '../../utils/utils';
import { getRuleExceptionsForExport } from './get_export_rule_exceptions';
@ -23,17 +23,14 @@ export const getExportAll = async (
exceptionsClient: ExceptionListClient | undefined,
actionsExporter: ISavedObjectsExporter,
request: KibanaRequest,
actionsClient: ActionsClient,
isPrebuiltRulesExportAllowed?: boolean
actionsClient: ActionsClient
): Promise<{
rulesNdjson: string;
exportDetails: string;
exceptionLists: string | null;
actionConnectors: string;
}> => {
const ruleAlertTypes = isPrebuiltRulesExportAllowed
? await getRules({ rulesClient, filter: '' })
: await getNonPackagedRules({ rulesClient });
const ruleAlertTypes = await getRules({ rulesClient, filter: '' });
const rules = transformAlertsToRules(ruleAlertTypes);
const exportRules = rules.map((r) => transformRuleToExportableFormat(r));

View file

@ -87,12 +87,21 @@ describe('getExportByObjectIds', () => {
expect(exports.actionConnectors).toBe('');
});
test('it DOES NOT export immutable rules', async () => {
test('it exports prebuilt rules', async () => {
const rulesClient = rulesClientMock.create();
const immutableRule = getRuleMock(getQueryRuleParams({ ruleId: 'rule-1', immutable: true }));
const prebuiltRule = getRuleMock(
getQueryRuleParams({
ruleId: 'rule-1',
immutable: true,
ruleSource: {
type: 'external',
isCustomized: false,
},
})
);
rulesClient.get.mockResolvedValue(immutableRule);
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [immutableRule] }));
rulesClient.get.mockResolvedValue(prebuiltRule);
rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [prebuiltRule] }));
const ruleIds = ['rule-1'];
const exports = await getExportByObjectIds(
@ -105,13 +114,13 @@ describe('getExportByObjectIds', () => {
);
expect(JSON.parse(exports.exportDetails)).toMatchObject({
exported_count: 0,
exported_rules_count: 0,
missing_rules: [{ rule_id: 'rule-1' }],
missing_rules_count: 1,
exported_count: 1,
exported_rules_count: 1,
missing_rules: [],
missing_rules_count: 0,
});
expect(exports).toMatchObject({
rulesNdjson: '',
rulesNdjson: expect.any(String),
exceptionLists: '',
actionConnectors: '',
});

View file

@ -29,8 +29,7 @@ export const getExportByObjectIds = async (
ruleIds: string[],
actionsExporter: ISavedObjectsExporter,
request: KibanaRequest,
actionsClient: ActionsClient,
isPrebuiltRulesExportAllowed?: boolean
actionsClient: ActionsClient
): Promise<{
rulesNdjson: string;
exportDetails: string;
@ -38,11 +37,7 @@ export const getExportByObjectIds = async (
actionConnectors: string;
}> =>
withSecuritySpan('getExportByObjectIds', async () => {
const rulesAndErrors = await fetchRulesByIds(
rulesClient,
ruleIds,
isPrebuiltRulesExportAllowed
);
const rulesAndErrors = await fetchRulesByIds(rulesClient, ruleIds);
const { rules, missingRuleIds } = rulesAndErrors;
// Retrieve exceptions
@ -81,8 +76,7 @@ interface FetchRulesResult {
const fetchRulesByIds = async (
rulesClient: RulesClient,
ruleIds: string[],
isPrebuiltRulesExportAllowed?: boolean
ruleIds: string[]
): 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
@ -114,9 +108,7 @@ const fetchRulesByIds = async (
const rulesAndErrors = ids.map((ruleId) => {
const matchingRule = rulesMap.get(ruleId);
return matchingRule != null &&
hasValidRuleType(matchingRule) &&
(isPrebuiltRulesExportAllowed || matchingRule.params.immutable !== true)
return matchingRule != null && hasValidRuleType(matchingRule)
? {
rule: transformRuleToExportableFormat(internalRuleToAPIResponse(matchingRule)),
}

View file

@ -6,14 +6,9 @@
*/
import { getRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import { getPrebuiltRuleMock } from '../../../prebuilt_rules/mocks';
import { calculateRuleSourceForImport } from './calculate_rule_source_for_import';
const ruleCustomizationStatus: PrebuiltRulesCustomizationStatus = {
isRulesCustomizationEnabled: true,
};
describe('calculateRuleSourceForImport', () => {
it('calculates as internal if no asset is found', () => {
const result = calculateRuleSourceForImport({
@ -21,7 +16,6 @@ describe('calculateRuleSourceForImport', () => {
currentRule: undefined,
prebuiltRuleAssetsByRuleId: {},
isKnownPrebuiltRule: false,
ruleCustomizationStatus,
});
expect(result).toEqual({
@ -41,7 +35,6 @@ describe('calculateRuleSourceForImport', () => {
currentRule: undefined,
prebuiltRuleAssetsByRuleId: {},
isKnownPrebuiltRule: true,
ruleCustomizationStatus,
});
expect(result).toEqual({
@ -62,7 +55,6 @@ describe('calculateRuleSourceForImport', () => {
currentRule: rule,
prebuiltRuleAssetsByRuleId: {},
isKnownPrebuiltRule: true,
ruleCustomizationStatus,
});
expect(result).toEqual({
@ -86,7 +78,6 @@ describe('calculateRuleSourceForImport', () => {
},
prebuiltRuleAssetsByRuleId: {},
isKnownPrebuiltRule: true,
ruleCustomizationStatus,
});
expect(result).toEqual({
@ -108,7 +99,6 @@ describe('calculateRuleSourceForImport', () => {
currentRule: undefined,
prebuiltRuleAssetsByRuleId,
isKnownPrebuiltRule: true,
ruleCustomizationStatus,
});
expect(result).toEqual({
@ -130,7 +120,6 @@ describe('calculateRuleSourceForImport', () => {
currentRule: undefined,
prebuiltRuleAssetsByRuleId,
isKnownPrebuiltRule: true,
ruleCustomizationStatus,
});
expect(result).toEqual({

View file

@ -10,7 +10,6 @@ import type {
RuleSource,
ValidatedRuleToImport,
} from '../../../../../../common/api/detection_engine';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import type { PrebuiltRuleAsset } from '../../../prebuilt_rules';
import { calculateIsCustomized } from '../detection_rules_client/mergers/rule_source/calculate_is_customized';
import { convertRuleToImportToRuleResponse } from './converters/convert_rule_to_import_to_rule_response';
@ -31,13 +30,11 @@ export const calculateRuleSourceForImport = ({
currentRule,
prebuiltRuleAssetsByRuleId,
isKnownPrebuiltRule,
ruleCustomizationStatus,
}: {
importedRule: ValidatedRuleToImport;
currentRule: RuleResponse | undefined;
prebuiltRuleAssetsByRuleId: Record<string, PrebuiltRuleAsset>;
isKnownPrebuiltRule: boolean;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}): { ruleSource: RuleSource; immutable: boolean } => {
if (!isKnownPrebuiltRule) {
return {
@ -57,7 +54,6 @@ export const calculateRuleSourceForImport = ({
baseRule,
nextRule,
currentRule,
ruleCustomizationStatus,
});
return {

View file

@ -1,110 +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 { SavedObjectsClientContract } from '@kbn/core/server';
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
import { getImportRulesSchemaMock } from '../../../../../../common/api/detection_engine/rule_management/mocks';
import { getRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
import { requestContextMock } from '../../../routes/__mocks__';
import { createRuleImportErrorObject } from './errors';
// eslint-disable-next-line no-restricted-imports
import { importRulesLegacy } from './import_rules_legacy';
describe('importRulesLegacy', () => {
const { clients, context } = requestContextMock.createTools();
const ruleToImport = getImportRulesSchemaMock();
let savedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
beforeEach(() => {
jest.clearAllMocks();
savedObjectsClient = savedObjectsClientMock.create();
});
it('returns an empty rules response if no rules to import', async () => {
const result = await importRulesLegacy({
ruleChunks: [],
overwriteRules: false,
detectionRulesClient: context.securitySolution.getDetectionRulesClient(),
savedObjectsClient,
});
expect(result).toEqual([]);
});
it('returns 409 error if DetectionRulesClient throws with 409 - existing rule', async () => {
clients.detectionRulesClient.importRule.mockImplementationOnce(async () => {
throw createRuleImportErrorObject({
ruleId: ruleToImport.rule_id,
type: 'conflict',
message: `rule_id: "${ruleToImport.rule_id}" already exists`,
});
});
const ruleChunk = [ruleToImport];
const result = await importRulesLegacy({
ruleChunks: [ruleChunk],
overwriteRules: false,
detectionRulesClient: context.securitySolution.getDetectionRulesClient(),
savedObjectsClient,
});
expect(result).toEqual([
{
error: {
message: `rule_id: "${ruleToImport.rule_id}" already exists`,
status_code: 409,
},
rule_id: ruleToImport.rule_id,
},
]);
});
it('creates rule if no matching existing rule found', async () => {
clients.detectionRulesClient.importRule.mockResolvedValue({
...getRulesSchemaMock(),
rule_id: ruleToImport.rule_id,
});
const ruleChunk = [ruleToImport];
const result = await importRulesLegacy({
ruleChunks: [ruleChunk],
overwriteRules: false,
detectionRulesClient: context.securitySolution.getDetectionRulesClient(),
savedObjectsClient,
});
expect(result).toEqual([{ rule_id: ruleToImport.rule_id, status_code: 200 }]);
});
it('rejects a prebuilt rule specifying an immutable value of true', async () => {
const prebuiltRuleToImport = {
...getImportRulesSchemaMock(),
immutable: true,
version: 1,
};
const result = await importRulesLegacy({
ruleChunks: [[prebuiltRuleToImport]],
overwriteRules: false,
detectionRulesClient: context.securitySolution.getDetectionRulesClient(),
savedObjectsClient,
});
expect(result).toEqual([
{
error: {
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: ${prebuiltRuleToImport.rule_id}]`,
status_code: 400,
},
rule_id: prebuiltRuleToImport.rule_id,
},
]);
});
});

View file

@ -1,139 +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 { i18n } from '@kbn/i18n';
import type { SavedObjectsClientContract } from '@kbn/core/server';
import type { RuleToImport } from '../../../../../../common/api/detection_engine';
import type { ImportRuleResponse } from '../../../routes/utils';
import { createBulkErrorObject } from '../../../routes/utils';
import { checkRuleExceptionReferences } from './check_rule_exception_references';
import type { IDetectionRulesClient } from '../detection_rules_client/detection_rules_client_interface';
import { getReferencedExceptionLists } from './gather_referenced_exceptions';
import { isRuleConflictError, isRuleImportError } from './errors';
/**
* Takes rules to be imported and either creates or updates rules
* based on user overwrite preferences.
*
* @deprecated Use {@link importRules} instead.
* @param ruleChunks {@link RuleToImport} - rules being imported
* @param overwriteRules {boolean} - whether to overwrite existing rules
* with imported rules if their rule_id matches
* @param detectionRulesClient {object}
* @returns {Promise} an array of error and success messages from import
*/
export const importRulesLegacy = async ({
ruleChunks,
overwriteRules,
detectionRulesClient,
allowMissingConnectorSecrets,
savedObjectsClient,
}: {
ruleChunks: RuleToImport[][];
overwriteRules: boolean;
detectionRulesClient: IDetectionRulesClient;
allowMissingConnectorSecrets?: boolean;
savedObjectsClient: SavedObjectsClientContract;
}): Promise<ImportRuleResponse[]> => {
const response: ImportRuleResponse[] = [];
if (ruleChunks.length === 0) {
return response;
}
while (ruleChunks.length) {
const batchParseObjects = ruleChunks.shift() ?? [];
const existingLists = await getReferencedExceptionLists({
rules: batchParseObjects,
savedObjectsClient,
});
const newImportRuleResponse = await Promise.all(
batchParseObjects.reduce<Array<Promise<ImportRuleResponse>>>((accum, parsedRule) => {
const importsWorkerPromise = new Promise<ImportRuleResponse>(async (resolve, reject) => {
try {
if (parsedRule.immutable) {
resolve(
createBulkErrorObject({
statusCode: 400,
message: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.importPrebuiltRulesUnsupported',
{
defaultMessage:
'Importing prebuilt rules is not supported. To import this rule as a custom rule, first duplicate the rule and then export it. [rule_id: {ruleId}]',
values: { ruleId: parsedRule.rule_id },
}
),
ruleId: parsedRule.rule_id,
})
);
return null;
}
try {
const [exceptionErrors, exceptions] = checkRuleExceptionReferences({
rule: parsedRule,
existingLists,
});
const exceptionBulkErrors = exceptionErrors.map((error) =>
createBulkErrorObject({
ruleId: error.error.ruleId,
statusCode: 400,
message: error.error.message,
})
);
response.push(...exceptionBulkErrors);
const importedRule = await detectionRulesClient.importRule({
ruleToImport: {
...parsedRule,
exceptions_list: [...exceptions],
},
overwriteRules,
allowMissingConnectorSecrets,
});
resolve({
rule_id: importedRule.rule_id,
status_code: 200,
});
} catch (err) {
const { error, statusCode, message } = err;
if (isRuleImportError(err)) {
resolve(
createBulkErrorObject({
message: err.error.message,
statusCode: isRuleConflictError(err) ? 409 : 400,
ruleId: err.error.ruleId,
})
);
return null;
}
resolve(
createBulkErrorObject({
ruleId: parsedRule.rule_id,
statusCode: statusCode ?? error?.status_code ?? 400,
message: message ?? error?.message ?? 'unknown error',
})
);
}
} catch (error) {
reject(error);
}
});
return [...accum, importsWorkerPromise];
}, [])
);
response.push(...newImportRuleResponse);
}
return response;
};

View file

@ -40,7 +40,6 @@ describe('ruleSourceImporter', () => {
config,
prebuiltRuleAssetsClient: ruleAssetsClientMock,
prebuiltRuleObjectsClient: ruleObjectsClientMock,
ruleCustomizationStatus: { isRulesCustomizationEnabled: true },
});
});

View file

@ -24,7 +24,6 @@ import type { IPrebuiltRuleAssetsClient } from '../../../../prebuilt_rules/logic
import { ensureLatestRulesPackageInstalled } from '../../../../prebuilt_rules/logic/ensure_latest_rules_package_installed';
import { calculateRuleSourceForImport } from '../calculate_rule_source_for_import';
import type { CalculatedRuleSource, IRuleSourceImporter } from './rule_source_importer_interface';
import type { PrebuiltRulesCustomizationStatus } from '../../../../../../../common/detection_engine/prebuilt_rules/prebuilt_rule_customization_status';
import type { IPrebuiltRuleObjectsClient } from '../../../../prebuilt_rules/logic/rule_objects/prebuilt_rule_objects_client';
interface RuleSpecifier {
@ -99,7 +98,6 @@ export class RuleSourceImporter implements IRuleSourceImporter {
private config: ConfigType;
private ruleAssetsClient: IPrebuiltRuleAssetsClient;
private ruleObjectsClient: IPrebuiltRuleObjectsClient;
private ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
private latestPackagesInstalled: boolean = false;
private matchingAssetsByRuleId: Record<string, PrebuiltRuleAsset> = {};
private currentRulesById: Record<string, RuleResponse> = {};
@ -111,19 +109,16 @@ export class RuleSourceImporter implements IRuleSourceImporter {
context,
prebuiltRuleAssetsClient,
prebuiltRuleObjectsClient,
ruleCustomizationStatus,
}: {
config: ConfigType;
context: SecuritySolutionApiRequestHandlerContext;
prebuiltRuleAssetsClient: IPrebuiltRuleAssetsClient;
prebuiltRuleObjectsClient: IPrebuiltRuleObjectsClient;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}) {
this.config = config;
this.ruleAssetsClient = prebuiltRuleAssetsClient;
this.ruleObjectsClient = prebuiltRuleObjectsClient;
this.context = context;
this.ruleCustomizationStatus = ruleCustomizationStatus;
}
/**
@ -159,7 +154,6 @@ export class RuleSourceImporter implements IRuleSourceImporter {
currentRule: this.currentRulesById[rule.rule_id],
prebuiltRuleAssetsByRuleId: this.matchingAssetsByRuleId,
isKnownPrebuiltRule: this.availableRuleAssetIds.has(rule.rule_id),
ruleCustomizationStatus: this.ruleCustomizationStatus,
});
}
@ -219,19 +213,16 @@ export const createRuleSourceImporter = ({
context,
prebuiltRuleAssetsClient,
prebuiltRuleObjectsClient,
ruleCustomizationStatus,
}: {
config: ConfigType;
context: SecuritySolutionApiRequestHandlerContext;
prebuiltRuleAssetsClient: IPrebuiltRuleAssetsClient;
prebuiltRuleObjectsClient: IPrebuiltRuleObjectsClient;
ruleCustomizationStatus: PrebuiltRulesCustomizationStatus;
}): RuleSourceImporter => {
return new RuleSourceImporter({
config,
context,
prebuiltRuleAssetsClient,
prebuiltRuleObjectsClient,
ruleCustomizationStatus,
});
};

View file

@ -17,7 +17,6 @@ import {
getNonPackagedRules,
getRules,
getRulesCount,
getNonPackagedRulesCount,
} from './get_existing_prepackaged_rules';
describe('get_existing_prepackaged_rules', () => {
@ -151,13 +150,4 @@ describe('get_existing_prepackaged_rules', () => {
expect(rules).toEqual(1);
});
});
describe('getNonPackagedRulesCount', () => {
test('it returns a count', async () => {
const rulesClient = rulesClientMock.create();
rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
const rules = await getNonPackagedRulesCount({ rulesClient });
expect(rules).toEqual(1);
});
});
});

View file

@ -17,14 +17,6 @@ import type { RuleAlertType } from '../../../rule_schema';
export const MAX_PREBUILT_RULES_COUNT = 10_000;
export const getNonPackagedRulesCount = async ({
rulesClient,
}: {
rulesClient: RulesClient;
}): Promise<number> => {
return getRulesCount({ rulesClient, filter: KQL_FILTER_MUTABLE_RULES });
};
export const getRulesCount = async ({
rulesClient,
filter,

View file

@ -157,7 +157,6 @@ export class RequestContextFactory implements IRequestContextFactory {
actionsClient,
savedObjectsClient: coreContext.savedObjects.client,
mlAuthz,
experimentalFeatures: config.experimentalFeatures,
productFeaturesService,
license: licensing.license,
});

View file

@ -20,15 +20,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'Rules Management - Prebuilt Rule Customization Disabled Integration Tests - ESS Env Basic License',
},
};
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;
}

View file

@ -1,25 +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 { 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 Customization Disabled Integration Tests - ESS Env Trial License',
},
};
return testConfig;
}

View file

@ -1,17 +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 { createTestConfig } from '../../../../../../../config/serverless/config.base';
export default createTestConfig({
testFiles: [require.resolve('..')],
junit: {
reportName:
'Rules Management - Prebuilt Rule Customization Disabled Integration Tests - Serverless Env Complete Tier',
},
kbnTestServerArgs: [],
});

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Prebuilt Rule Customization Disabled Integration Tests - Serverless Env Essentials Tier',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -11,5 +11,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization Disabled', function () {
loadTestFile(require.resolve('./is_customized_calculation'));
loadTestFile(require.resolve('./upgrade_perform_prebuilt_rules'));
loadTestFile(require.resolve('./rules_import/not_allowed_importing_customized_prebuilt_rules'));
loadTestFile(require.resolve('./rules_export/export_prebuilt_rules'));
});
};

View file

@ -7,15 +7,16 @@
import expect from 'expect';
import { BulkActionTypeEnum } from '@kbn/security-solution-plugin/common/api/detection_engine';
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
import { FtrProviderContext } from '../../../../../../../ftr_provider_context';
import {
binaryToString,
createPrebuiltRuleAssetSavedObjects,
createRuleAssetSavedObject,
deleteAllPrebuiltRuleAssets,
installPrebuiltRules,
} from '../../../../utils';
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
parseNdJson,
} from '../../../../../utils';
import { deleteAllRules } from '../../../../../../../../common/utils/security_solution';
export default ({ getService }: FtrProviderContext): void => {
const es = getService('es');
@ -29,7 +30,7 @@ export default ({ getService }: FtrProviderContext): void => {
await deleteAllPrebuiltRuleAssets(es, log);
});
it("Export API - doesn't export prebuilt rules when the feature is disabled", async () => {
it('Export API - exports prebuilt rules', async () => {
const ruleId = 'prebuilt-rule-1';
const ruleAsset = createRuleAssetSavedObject({ rule_id: ruleId, version: 1 });
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
@ -40,15 +41,17 @@ export default ({ getService }: FtrProviderContext): void => {
.expect(200)
.parse(binaryToString);
const exportDetails = JSON.parse(body.toString());
const exportDetails = parseNdJson(body);
expect(exportDetails).toMatchObject({
exported_rules_count: 0,
missing_rules: [], // Prebuilt rules are not in missing rules, even though they are not exported
});
expect(exportDetails).toEqual([
expect.objectContaining({
rule_id: ruleId,
}),
expect.objectContaining({ exported_rules_count: 1, missing_rules_count: 0 }),
]);
});
it("Bulk actions export API - doesn't export prebuilt rules when the feature is disabled", async () => {
it("Bulk actions export API - doesn't export prebuilt rules", async () => {
const ruleAsset = createRuleAssetSavedObject({ rule_id: 'prebuilt-rule-1', version: 1 });
await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]);
await installPrebuiltRules(es, supertest);
@ -64,12 +67,14 @@ export default ({ getService }: FtrProviderContext): void => {
.expect(200)
.parse(binaryToString);
const exportDetails = JSON.parse(body.toString());
const exportDetails = parseNdJson(body);
expect(exportDetails).toMatchObject({
exported_rules_count: 0,
missing_rules: [{ rule_id: 'prebuilt-rule-1' }],
});
expect(exportDetails).toEqual([
expect.objectContaining({
rule_id: 'prebuilt-rule-1',
}),
expect.objectContaining({ exported_rules_count: 1, missing_rules_count: 0 }),
]);
});
});
};

View file

@ -6,11 +6,14 @@
*/
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';
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');
@ -24,7 +27,7 @@ export default ({ getService }: FtrProviderContext): void => {
await deleteAllPrebuiltRuleAssets(es, log);
});
it(`does NOT import customized prebuilt rules when rule customization is disabled`, async () => {
it(`does NOT import customized prebuilt rules`, async () => {
const ruleId = 'prebuilt-rule-to-be-customized';
const ruleParams = getCustomQueryRuleParams({
rule_id: ruleId,

View file

@ -20,15 +20,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'Rules Management - Prebuilt Rule Customization Enabled Integration Tests - 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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Prebuilt Rule Customization Enabled Integration Tests - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -20,15 +20,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'Rules Management - Prebuilt Rule Customization Enabled Per Field Integration Tests - 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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Prebuilt Rule Customization Enabled Per Field Integration Tests - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -20,15 +20,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'Rules Management - Prebuilt Rule Customization Enabled Per Field Integration Tests - 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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Prebuilt Rule Customization Enabled Per Field Integration Tests - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -1520,7 +1520,7 @@ export default ({ getService }: FtrProviderContext): void => {
},
];
cases.forEach(({ type, value }) => {
it(`should return error when trying to apply "${type}" edit action to prebuilt rule`, async () => {
it(`should NOT return error when trying to apply "${type}" edit action to prebuilt rule`, async () => {
await installMockPrebuiltRules(supertest, es);
const prebuiltRule = await fetchPrebuiltRule();
@ -1535,23 +1535,11 @@ export default ({ getService }: FtrProviderContext): void => {
},
],
})
.expect(500);
.expect(200);
expect(body.attributes.summary).toEqual({
failed: 1,
skipped: 0,
succeeded: 0,
total: 1,
});
expect(body.attributes.errors[0]).toEqual({
message: "Elastic rule can't be edited",
status_code: 500,
rules: [
{
id: prebuiltRule.id,
name: prebuiltRule.name,
},
],
expect(body).toMatchObject({
success: true,
rules_count: 1,
});
});
});
@ -2066,63 +2054,6 @@ export default ({ getService }: FtrProviderContext): void => {
expect(prebuiltRule.version).toBe(readRule.version);
});
});
// if rule action is applied together with another edit action, that can't be applied to prebuilt rule (for example: tags action)
// bulk edit request should return error
it(`should return error if one of edit action is not eligible for prebuilt rule`, async () => {
await installMockPrebuiltRules(supertest, es);
const prebuiltRule = await fetchPrebuiltRule();
const webHookConnector = await createWebHookConnector();
const { body } = await postBulkAction()
.send({
ids: [prebuiltRule.id],
action: BulkActionTypeEnum.edit,
[BulkActionTypeEnum.edit]: [
{
type: BulkActionEditTypeEnum.set_rule_actions,
value: {
throttle: '1h',
actions: [
{
...webHookActionMock,
id: webHookConnector.id,
},
],
},
},
{
type: BulkActionEditTypeEnum.set_tags,
value: ['tag-1'],
},
],
})
.expect(500);
expect(body.attributes.summary).toEqual({
failed: 1,
skipped: 0,
succeeded: 0,
total: 1,
});
expect(body.attributes.errors[0]).toEqual({
message: "Elastic rule can't be edited",
status_code: 500,
rules: [
{
id: prebuiltRule.id,
name: prebuiltRule.name,
},
],
});
// Check that the updates were not made
const { body: readRule } = await fetchRule(prebuiltRule.rule_id).expect(200);
expect(readRule.actions).toEqual(prebuiltRule.actions);
expect(readRule.tags).toEqual(prebuiltRule.tags);
expect(readRule.version).toBe(prebuiltRule.version);
});
});
describe('throttle', () => {

View file

@ -201,47 +201,29 @@ export default ({ getService }: FtrProviderContext): void => {
expect(ruleBody.tags).toEqual(tags);
});
it('should validate immutable rule edit', async () => {
it('should allow prebuilt rules edit', async () => {
await installMockPrebuiltRules(supertest, es);
const { body: findBody } = await securitySolutionApi
.findRules({ query: { per_page: 1, filter: 'alert.attributes.params.immutable: true' } })
.expect(200);
const immutableRule = findBody.data[0];
const prebuiltRule = findBody.data[0];
const { body } = await securitySolutionApi
.performRulesBulkAction({
query: { dry_run: true },
body: {
ids: [immutableRule.id],
ids: [prebuiltRule.id],
action: BulkActionTypeEnum.edit,
[BulkActionTypeEnum.edit]: [
{ type: BulkActionEditTypeEnum.set_tags, value: ['reset-tag'] },
],
},
})
.expect(500);
.expect(200);
expect(body.attributes.summary).toEqual({ failed: 1, skipped: 0, succeeded: 0, total: 1 });
expect(body.attributes.results).toEqual({
updated: [],
skipped: [],
created: [],
deleted: [],
});
expect(body.attributes.errors).toHaveLength(1);
expect(body.attributes.errors[0]).toEqual({
err_code: 'IMMUTABLE',
message: "Elastic rule can't be edited",
status_code: 500,
rules: [
{
id: immutableRule.id,
name: immutableRule.name,
},
],
});
expect(body).toMatchObject({ success: true });
expect(body.attributes.summary).toMatchObject({ succeeded: 1, total: 1 });
});
describe('validate updating index pattern for machine learning rule', () => {

View file

@ -1,25 +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 { 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;
}

View file

@ -1,16 +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 { 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',
},
});

View file

@ -1,14 +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 '../../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Rules Management - Prebuilt rule export', function () {
loadTestFile(require.resolve('./export_prebuilt_rules_feature_disabled'));
});
}

View file

@ -21,15 +21,5 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
},
};
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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Prebuilt Rule Export Integration Tests - Customization enabled - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -1,35 +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 { 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;
}

View file

@ -1,25 +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 { 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;
}

View file

@ -1,21 +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 { 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',
])}`,
],
});

View file

@ -1,17 +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 { 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: [],
});

View file

@ -1,14 +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 '../../../../../../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'));
});
}

View file

@ -21,15 +21,5 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
},
};
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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Rule Import Integration Tests - Importing customized prebuilt rules - Customization disabled - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -1,25 +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 { 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;
}

View file

@ -1,17 +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 { 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: [],
});

View file

@ -1,14 +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 '../../../../../../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'));
});
}

View file

@ -1,55 +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 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]',
},
},
],
});
});
});
};

View file

@ -21,15 +21,5 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
},
};
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;
}

View file

@ -13,9 +13,4 @@ export default createTestConfig({
reportName:
'Rules Management - Rule Import Integration Tests - Importing non-customized prebuilt rules - Serverless Env',
},
kbnTestServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
});

View file

@ -1625,7 +1625,27 @@ export default ({ getService }: FtrProviderContext): void => {
describe('supporting prebuilt rule customization', () => {
describe('compatibility with prebuilt rule fields', () => {
it('imports custom rules alongside prebuilt rules when feature flag is disabled', async () => {
it('accepts rules with "immutable: true"', 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: true,
});
});
it('imports custom rules alongside prebuilt rules', async () => {
const ndjson = combineToNdJson(
getCustomQueryRuleParams({
rule_id: 'rule-immutable',
@ -1644,18 +1664,8 @@ export default ({ getService }: FtrProviderContext): void => {
.expect(200);
expect(body).toMatchObject({
success: false,
success_count: 1,
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]',
},
},
],
success: true,
success_count: 2,
});
});

View file

@ -16,3 +16,11 @@ export function combineArrayToNdJson(parts: unknown[]): string {
export function combineArraysToNdJson(...arrays: unknown[][]): string {
return arrays.map((array) => combineArrayToNdJson(array)).join('\n');
}
export function parseNdJson(ndJson: Buffer): unknown[] {
return ndJson
.toString()
.split('\n')
.filter((line) => !!line)
.map((line) => JSON.parse(line));
}

View file

@ -20,7 +20,7 @@ import {
selectAllRules,
selectRulesByName,
} from '../../../../tasks/alerts_detection_rules';
import { RULE_NAME, TOASTER_BODY } from '../../../../screens/alerts_detection_rules';
import { RULE_NAME, SUCCESS_TOASTER_BODY } from '../../../../screens/alerts_detection_rules';
import { createRuleAssetSavedObject } from '../../../../helpers/rules';
import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common';
import { createAndInstallMockedPrebuiltRules } from '../../../../tasks/api_calls/prebuilt_rules';
@ -36,15 +36,6 @@ describe(
'Detection rules, Prebuilt Rules Export workflow - With Rule Customization',
{
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
},
},
},
() => {
describe('Rule export workflow with single rules', () => {
@ -72,7 +63,7 @@ describe(
exportRule('Non-customized prebuilt rule');
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.prebuiltRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -83,7 +74,7 @@ describe(
exportRule('Customized prebuilt rule');
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.prebuiltRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -91,7 +82,7 @@ describe(
exportRule('Custom rule to export');
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.customRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -101,7 +92,7 @@ describe(
exportRuleFromDetailsPage();
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.prebuiltRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -113,7 +104,7 @@ describe(
exportRuleFromDetailsPage();
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.prebuiltRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -122,7 +113,7 @@ describe(
exportRuleFromDetailsPage();
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.customRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
});
@ -168,7 +159,7 @@ describe(
'eql',
expectedExportedRule(this.nonCustomizedPrebuiltRuleResponse)
);
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -180,7 +171,7 @@ describe(
'eql',
expectedExportedRule(this.customizedPrebuiltRuleResponse)
);
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -190,7 +181,7 @@ describe(
bulkExportRules();
cy.wait('@bulk_action').then(({ response }) => {
cy.wrap(response?.body).should('eql', expectedExportedRule(this.customRuleResponse));
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 1 of 1 rule.');
});
});
@ -206,7 +197,7 @@ describe(
this.customRuleResponse,
])
);
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 3 of 3 rules.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 3 of 3 rules.');
});
});
@ -222,7 +213,7 @@ describe(
this.customizedPrebuiltRuleResponse,
])
);
cy.get(TOASTER_BODY).should('have.text', 'Successfully exported 2 of 2 rules.');
cy.get(SUCCESS_TOASTER_BODY).should('have.text', 'Successfully exported 2 of 2 rules.');
});
});
});

View file

@ -25,15 +25,6 @@ describe(
'Detection rules, Prebuilt Rules Import workflow - With Rule Customization',
{
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
},
},
},
() => {
describe('when file is unmodified prebuilt rule with matching rule_id', () => {

View file

@ -66,6 +66,7 @@ import { visitRulesManagementTable } from '../../../../tasks/rules_management';
import {
deleteAlertsAndRules,
deleteDataView,
deletePrebuiltRulesAssets,
postDataView,
} from '../../../../tasks/api_calls/common';
import { enableRules, waitForRulesToFinishExecution } from '../../../../tasks/api_calls/rules';
@ -379,6 +380,7 @@ describe(
login();
resetRulesTableState();
deletePrebuiltRulesAssets();
deleteAlertsAndRules();
visitRulesManagementTable();
@ -1167,17 +1169,12 @@ describe(
openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name);
assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default
cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible');
cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible');
cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 2);
/* Version should be the first field in the order */
cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('Version').should('be.visible');
cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('1').should('be.visible');
cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('2').should('be.visible');
cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 1);
cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').should('be.visible');
// expand Name field section
cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').click();
cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Outdated rule 1').should('be.visible');
cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Updated rule 1').should('be.visible');
});

View file

@ -65,15 +65,6 @@ describe(
'Detection rules, Prebuilt Rules Customization workflow',
{
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
},
},
},
() => {

View file

@ -45,15 +45,6 @@ describe(
'Detection rules, Prebuilt Rules Installation and Update workflow - With Rule Customization, Rule Updates Table',
{
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'prebuiltRulesCustomizationEnabled',
])}`,
],
},
},
},
() => {

View file

@ -123,7 +123,7 @@ describe('Related integrations', { tags: ['@ess', '@serverless', '@skipInServerl
it('should display a badge with the installed integrations', () => {
cy.get(INTEGRATIONS_POPOVER).should(
'have.text',
`0/${EXPECTED_RELATED_INTEGRATIONS.length} integrations`
`0/${EXPECTED_RELATED_INTEGRATIONS.length}`
);
});
@ -199,7 +199,7 @@ describe('Related integrations', { tags: ['@ess', '@serverless', '@skipInServerl
it('should display a badge with the installed integrations', () => {
cy.get(INTEGRATIONS_POPOVER).should(
'have.text',
`2/${EXPECTED_RELATED_INTEGRATIONS.length} integrations`
`2/${EXPECTED_RELATED_INTEGRATIONS.length}`
);
});

View file

@ -5,10 +5,12 @@
* 2.0.
*/
import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common';
import {
deleteAlertsAndRules,
deletePrebuiltRulesAssets,
} from '../../../../../tasks/api_calls/common';
import {
MODAL_CONFIRMATION_BTN,
MODAL_CONFIRMATION_BODY,
RULES_TAGS_POPOVER_BTN,
MODAL_ERROR_BODY,
} from '../../../../../screens/alerts_detection_rules';
@ -36,12 +38,10 @@ import {
testAllTagsBadges,
testTagsBadge,
testMultipleSelectedRulesLabel,
filterByElasticRules,
clickErrorToastBtn,
cancelConfirmationModal,
selectRulesByName,
getRulesManagementTableRows,
selectAllRulesOnPage,
getRuleRow,
disableAutoRefresh,
} from '../../../../../tasks/alerts_detection_rules';
@ -51,17 +51,14 @@ import {
waitForBulkEditActionToFinish,
submitBulkEditForm,
clickAddIndexPatternsMenuItem,
checkPrebuiltRulesCannotBeModified,
checkMachineLearningRulesCannotBeModified,
checkEsqlRulesCannotBeModified,
waitForMixedRulesBulkEditModal,
openBulkEditAddTagsForm,
openBulkEditDeleteTagsForm,
typeTags,
openTagsSelect,
openBulkActionsMenu,
clickApplyTimelineTemplatesMenuItem,
clickAddTagsMenuItem,
checkOverwriteTagsCheckbox,
checkOverwriteIndexPatternsCheckbox,
openBulkEditAddIndexPatternsForm,
@ -107,7 +104,6 @@ import {
import {
createAndInstallMockedPrebuiltRules,
getAvailablePrebuiltRulesCount,
preventPrebuiltRulesPackageInstallation,
} from '../../../../../tasks/api_calls/prebuilt_rules';
import { setRowsPerPageTo, sortByTableColumn } from '../../../../../tasks/table_pagination';
@ -135,10 +131,26 @@ describe(
() => {
beforeEach(() => {
login();
preventPrebuiltRulesPackageInstallation(); // Make sure prebuilt rules aren't pulled from Fleet API
// Make sure persisted rules table state is cleared
resetRulesTableState();
deleteAlertsAndRules();
deletePrebuiltRulesAssets();
const PREBUILT_RULES = [
createRuleAssetSavedObject({
...defaultRuleData,
name: 'Prebuilt rule 1',
rule_id: 'rule_1',
}),
createRuleAssetSavedObject({
...defaultRuleData,
name: 'Prebuilt rule 2',
rule_id: 'rule_2',
}),
];
createAndInstallMockedPrebuiltRules(PREBUILT_RULES);
createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1', enabled: false }));
createRule(
getEqlRule({ ...defaultRuleData, rule_id: '2', name: 'New EQL Rule', enabled: false })
@ -146,7 +158,7 @@ describe(
createRule(
getMachineLearningRule({
name: 'New ML Rule Test',
tags: ['test-default-tag-1', 'test-default-tag-2'],
tags: prePopulatedTags,
investigation_fields: { field_names: prePopulatedInvestigationFields },
enabled: false,
})
@ -181,17 +193,6 @@ describe(
});
describe('Prerequisites', () => {
const PREBUILT_RULES = [
createRuleAssetSavedObject({
name: 'Prebuilt rule 1',
rule_id: 'rule_1',
}),
createRuleAssetSavedObject({
name: 'Prebuilt rule 2',
rule_id: 'rule_2',
}),
];
it('No rules selected', () => {
openBulkActionsMenu();
@ -201,77 +202,6 @@ describe(
cy.get(APPLY_TIMELINE_RULE_BULK_MENU_ITEM).should('be.disabled');
});
// github.com/elastic/kibana/issues/179954
it('Only prebuilt rules selected', { tags: ['@skipInServerlessMKI'] }, () => {
createAndInstallMockedPrebuiltRules(PREBUILT_RULES);
// select Elastic(prebuilt) rules, check if we can't proceed further, as Elastic rules are not editable
filterByElasticRules();
selectAllRulesOnPage();
clickApplyTimelineTemplatesMenuItem();
getRulesManagementTableRows().then((rows) => {
// check modal window for Elastic rule that can't be edited
checkPrebuiltRulesCannotBeModified(rows.length);
// the confirm button closes modal
cy.get(MODAL_CONFIRMATION_BTN).should('have.text', 'Close').click();
cy.get(MODAL_CONFIRMATION_BODY).should('not.exist');
});
});
// https://github.com/elastic/kibana/issues/179955
it(
'Prebuilt and custom rules selected: user proceeds with custom rules editing',
{ tags: ['@skipInServerlessMKI'] },
() => {
getRulesManagementTableRows().then((existedRulesRows) => {
createAndInstallMockedPrebuiltRules(PREBUILT_RULES);
// modal window should show how many rules can be edit, how many not
selectAllRules();
clickAddTagsMenuItem();
waitForMixedRulesBulkEditModal(existedRulesRows.length);
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
checkPrebuiltRulesCannotBeModified(availablePrebuiltRulesCount);
});
// user can proceed with custom rule editing
cy.get(MODAL_CONFIRMATION_BTN)
.should('have.text', `Edit ${existedRulesRows.length} rules`)
.click();
// action should finish
typeTags(['test-tag']);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: existedRulesRows.length });
});
}
);
// https://github.com/elastic/kibana/issues/179956
it(
'Prebuilt and custom rules selected: user cancels action',
{ tags: ['@skipInServerlessMKI'] },
() => {
createAndInstallMockedPrebuiltRules(PREBUILT_RULES);
getRulesManagementTableRows().then((rows) => {
// modal window should show how many rules can be edit, how many not
selectAllRules();
clickAddTagsMenuItem();
waitForMixedRulesBulkEditModal(rows.length);
checkPrebuiltRulesCannotBeModified(PREBUILT_RULES.length);
// user cancels action and modal disappears
cancelConfirmationModal();
});
}
);
it('should not lose rules selection after edit action', () => {
const rulesToUpdate = [RULE_NAME, 'New EQL Rule', 'New Terms Rule'] as const;
// Switch to 5 rules per page, to have few pages in pagination(ideal way to test auto refresh and selection of few items)
@ -313,7 +243,7 @@ describe(
});
});
it('Add tags to custom rules', () => {
it('Add tags', () => {
getRulesManagementTableRows().then((rows) => {
const tagsToBeAdded = ['tag-to-add-1', 'tag-to-add-2'];
const resultingTags = [...prePopulatedTags, ...tagsToBeAdded];
@ -356,7 +286,7 @@ describe(
});
});
it('Overwrite tags in custom rules', () => {
it('Overwrite tags', () => {
getRulesManagementTableRows().then((rows) => {
const tagsToOverwrite = ['overwrite-tag-1'];
@ -386,7 +316,7 @@ describe(
});
});
it('Delete tags from custom rules', () => {
it('Delete tags from', () => {
getRulesManagementTableRows().then((rows) => {
const tagsToDelete = prePopulatedTags.slice(0, 1);
const resultingTags = prePopulatedTags.slice(1);
@ -412,7 +342,7 @@ describe(
});
describe('Index patterns', () => {
it('Index pattern action applied to custom rules, including machine learning: user proceeds with edit of custom non machine learning rule', () => {
it('Index pattern action applied, including machine learning: user proceeds with edit of non machine learning rule', () => {
getRulesManagementTableRows().then((rows) => {
const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*'];
const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded];
@ -420,7 +350,7 @@ describe(
selectAllRules();
clickAddIndexPatternsMenuItem();
// confirm editing custom rules, that are not Machine Learning
// confirm editing all rules, that are not Machine Learning
checkMachineLearningRulesCannotBeModified(expectedNumberOfMachineLearningRulesToBeEdited);
cy.get(MODAL_CONFIRMATION_BTN).click();
@ -437,18 +367,18 @@ describe(
});
});
it('Index pattern action applied to custom rules, including machine learning: user cancels action', () => {
it('Index pattern action applied to all rules, including machine learning: user cancels action', () => {
selectAllRules();
clickAddIndexPatternsMenuItem();
// confirm editing custom rules, that are not Machine Learning
// confirm editing all rules, that are not Machine Learning
checkMachineLearningRulesCannotBeModified(expectedNumberOfMachineLearningRulesToBeEdited);
// user cancels action and modal disappears
cancelConfirmationModal();
});
it('Add index patterns to custom rules', () => {
it('Add index patterns', () => {
getRulesManagementTableRows().then((rows) => {
const indexPattersToBeAdded = ['index-to-add-1-*', 'index-to-add-2-*'];
const resultingIndexPatterns = [...prePopulatedIndexPatterns, ...indexPattersToBeAdded];
@ -460,6 +390,8 @@ describe(
'Threat Indicator Rule Test',
'Threshold Rule',
'New Terms Rule',
'Prebuilt rule 1',
'Prebuilt rule 2',
]);
openBulkEditAddIndexPatternsForm();
@ -487,6 +419,8 @@ describe(
'Threat Indicator Rule Test',
'Threshold Rule',
'New Terms Rule',
'Prebuilt rule 1',
'Prebuilt rule 2',
]);
openBulkEditAddIndexPatternsForm();
@ -499,13 +433,15 @@ describe(
});
});
it('Overwrite index patterns in custom rules', () => {
it('Overwrite index patterns', () => {
const rulesToSelect = [
RULE_NAME,
'New EQL Rule',
'Threat Indicator Rule Test',
'Threshold Rule',
'New Terms Rule',
'Prebuilt rule 1',
'Prebuilt rule 2',
] as const;
const indexPattersToWrite = ['index-to-write-1-*', 'index-to-write-2-*'];
@ -531,13 +467,15 @@ describe(
hasIndexPatterns(indexPattersToWrite.join(''));
});
it('Delete index patterns from custom rules', () => {
it('Delete index patterns', () => {
const rulesToSelect = [
RULE_NAME,
'New EQL Rule',
'Threat Indicator Rule Test',
'Threshold Rule',
'New Terms Rule',
'Prebuilt rule 1',
'Prebuilt rule 2',
] as const;
const indexPatternsToDelete = prePopulatedIndexPatterns.slice(0, 1);
const resultingIndexPatterns = prePopulatedIndexPatterns.slice(1);
@ -556,13 +494,15 @@ describe(
hasIndexPatterns(resultingIndexPatterns.join(''));
});
it('Delete all index patterns from custom rules', () => {
it('Delete all index patterns', () => {
const rulesToSelect = [
RULE_NAME,
'New EQL Rule',
'Threat Indicator Rule Test',
'Threshold Rule',
'New Terms Rule',
'Prebuilt rule 1',
'Prebuilt rule 2',
] as const;
// select only rules that are not ML
@ -582,14 +522,14 @@ describe(
});
describe('Investigation fields actions', () => {
it('Add investigation fields to custom rules', () => {
it('Add investigation fields', () => {
getRulesManagementTableRows().then((rows) => {
const fieldsToBeAdded = ['source.ip', 'destination.ip'];
const resultingFields = [...prePopulatedInvestigationFields, ...fieldsToBeAdded];
selectAllRules();
// open add custom highlighted fields form and add 2 new fields
// open add highlighted fields form and add 2 new fields
openBulkEditAddInvestigationFieldsForm();
typeInvestigationFields(fieldsToBeAdded);
submitBulkEditForm();
@ -601,7 +541,7 @@ describe(
});
});
it('Overwrite investigation fields in custom rules', () => {
it('Overwrite investigation fields', () => {
getRulesManagementTableRows().then((rows) => {
const fieldsToOverwrite = ['source.ip'];
@ -626,7 +566,7 @@ describe(
});
});
it('Delete investigation fields from custom rules', () => {
it('Delete investigation fields', () => {
getRulesManagementTableRows().then((rows) => {
const fieldsToDelete = prePopulatedInvestigationFields.slice(0, 1);
const resultingFields = prePopulatedInvestigationFields.slice(1);
@ -645,7 +585,7 @@ describe(
});
});
it('Delete all investigation fields from custom rules', () => {
it('Delete all investigation fields', () => {
getRulesManagementTableRows().then((rows) => {
selectAllRules();
@ -666,7 +606,7 @@ describe(
loadPrepackagedTimelineTemplates();
});
it('Apply timeline template to custom rules', () => {
it('Apply timeline template', () => {
getRulesManagementTableRows().then((rows) => {
const timelineTemplateName = 'Generic Endpoint Timeline';
@ -688,7 +628,7 @@ describe(
});
});
it('Reset timeline template to None for custom rules', () => {
it('Reset timeline template to None', () => {
getRulesManagementTableRows().then((rows) => {
const noneTimelineTemplate = 'None';
@ -722,7 +662,7 @@ describe(
});
});
it('Updates schedule for custom rules', () => {
it('Updates schedule', () => {
getRulesManagementTableRows().then((rows) => {
selectAllRules();
clickUpdateScheduleMenuItem();
@ -747,7 +687,7 @@ describe(
});
});
it('Validates invalid inputs when scheduling for custom rules', () => {
it('Validates invalid inputs when scheduling', () => {
getRulesManagementTableRows().then((rows) => {
selectAllRules();
clickUpdateScheduleMenuItem();
@ -834,7 +774,7 @@ describe('Detection rules, bulk edit, ES|QL rule type', { tags: ['@ess'] }, () =
selectAllRules();
clickAddIndexPatternsMenuItem();
// confirm editing custom rules, that are not Machine Learning
// confirm editing all rules, that are not Machine Learning
checkEsqlRulesCannotBeModified(1);
// user cancels action and modal disappears

View file

@ -7,16 +7,13 @@
import path from 'path';
import { deleteAlertsAndRules } from '../../../../../tasks/api_calls/common';
import {
deleteAlertsAndRules,
deletePrebuiltRulesAssets,
} from '../../../../../tasks/api_calls/common';
import { expectedExportedRule, getNewRule } from '../../../../../objects/rule';
import { TOASTER_BODY, TOASTER } from '../../../../../screens/alerts_detection_rules';
import {
TOASTER_BODY,
MODAL_CONFIRMATION_BODY,
MODAL_CONFIRMATION_BTN,
TOASTER,
} from '../../../../../screens/alerts_detection_rules';
import {
filterByElasticRules,
selectAllRules,
waitForRuleExecution,
exportRule,
@ -33,7 +30,6 @@ import { createRule } from '../../../../../tasks/api_calls/rules';
import { resetRulesTableState } from '../../../../../tasks/common';
import { login } from '../../../../../tasks/login';
import { visit } from '../../../../../tasks/navigation';
import { RULES_MANAGEMENT_URL } from '../../../../../urls/rules_management';
import {
createAndInstallMockedPrebuiltRules,
@ -56,9 +52,12 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI']
const downloadsFolder = Cypress.config('downloadsFolder');
beforeEach(() => {
preventPrebuiltRulesPackageInstallation();
login();
// Make sure persisted rules table state is cleared
resetRulesTableState();
deletePrebuiltRulesAssets();
deleteAlertsAndRules();
// Rules get exported via _bulk_action endpoint
cy.intercept('POST', '/api/detection_engine/rules/_bulk_action').as('bulk_action');
@ -93,25 +92,8 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI']
expectManagementTableRules(['Enabled rule to export']);
});
// https://github.com/elastic/kibana/issues/179959
it(
'shows a modal saying that no rules can be exported if all the selected rules are prebuilt',
{ tags: ['@skipInServerlessMKI'] },
function () {
createAndInstallMockedPrebuiltRules(prebuiltRules);
filterByElasticRules();
selectAllRules();
bulkExportRules();
cy.get(MODAL_CONFIRMATION_BODY).contains(
`${prebuiltRules.length} prebuilt Elastic rules (exporting prebuilt rules is not supported)`
);
}
);
// https://github.com/elastic/kibana/issues/179960
it('exports only custom rules', { tags: ['@skipInServerless'] }, function () {
it('exports all rules', { tags: ['@skipInServerless'] }, function () {
const expectedNumberCustomRulesToBeExported = 1;
createAndInstallMockedPrebuiltRules(prebuiltRules);
@ -119,23 +101,12 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI']
selectAllRules();
bulkExportRules();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
cy.get(MODAL_CONFIRMATION_BODY).contains(
`${availablePrebuiltRulesCount} prebuilt Elastic rules (exporting prebuilt rules is not supported)`
);
});
// proceed with exporting only custom rules
cy.get(MODAL_CONFIRMATION_BTN)
.should('have.text', `Export ${expectedNumberCustomRulesToBeExported} rule`)
.click();
getAvailablePrebuiltRulesCount().then((availablePrebuiltRulesCount) => {
const totalNumberOfRules =
expectedNumberCustomRulesToBeExported + availablePrebuiltRulesCount;
cy.get(TOASTER_BODY).should(
'contain',
`Successfully exported ${expectedNumberCustomRulesToBeExported} of ${totalNumberOfRules} rules. Prebuilt rules were excluded from the resulting file.`
`Successfully exported ${totalNumberOfRules} of ${totalNumberOfRules} rules.`
);
});
});
@ -166,18 +137,13 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI']
// https://github.com/elastic/kibana/issues/180029
it('exports custom rules with exceptions', { tags: ['@skipInServerlessMKI'] }, function () {
// one rule with exception, one without it
const expectedNumberCustomRulesToBeExported = 2;
const expectedNumberCustomRulesToBeExported = prebuiltRules.length + 2; // prebuilt rules + a custom rule + a rule with exceptions
createAndInstallMockedPrebuiltRules(prebuiltRules);
cy.reload();
selectAllRules();
bulkExportRules();
// should display correct number of custom rules when one of them has exceptions
cy.get(MODAL_CONFIRMATION_BTN)
.should('have.text', `Export ${expectedNumberCustomRulesToBeExported} rules`)
.click();
cy.get(TOASTER_BODY).should(
'contain',
`Successfully exported ${expectedNumberCustomRulesToBeExported}`

Some files were not shown because too many files have changed in this diff Show more