mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# 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:
parent
ce08437e98
commit
e51bb2215e
104 changed files with 243 additions and 1510 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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エージェントにこのポリシーが割り当てられていることを確認し、互換性があるイベントを取り込みます。",
|
||||
|
|
|
@ -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 代理已分配此策略以采集兼容的事件。",
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export enum PrebuiltRulesCustomizationDisabledReason {
|
||||
License = 'License',
|
||||
FeatureFlag = 'FeatureFlag',
|
||||
}
|
||||
|
||||
export interface PrebuiltRulesCustomizationStatus {
|
||||
isRulesCustomizationEnabled: boolean;
|
||||
customizationDisabledReason?: PrebuiltRulesCustomizationDisabledReason;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -58,11 +58,7 @@ function createKibanaServicesMock(overrides?: Partial<CoreStart>) {
|
|||
function createMockStore() {
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
app: () => ({
|
||||
enableExperimental: {
|
||||
prebuiltRulesCustomizationEnabled: true,
|
||||
},
|
||||
}),
|
||||
app: jest.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -96,7 +96,6 @@ export const bulkEditRules = async ({
|
|||
baseRule: baseVersionsMap.get(nextRule.rule_id),
|
||||
currentRule: convertAlertingRuleToRuleResponse(currentRule),
|
||||
nextRule,
|
||||
ruleCustomizationStatus,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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: '',
|
||||
});
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
};
|
|
@ -40,7 +40,6 @@ describe('ruleSourceImporter', () => {
|
|||
config,
|
||||
prebuiltRuleAssetsClient: ruleAssetsClientMock,
|
||||
prebuiltRuleObjectsClient: ruleObjectsClientMock,
|
||||
ruleCustomizationStatus: { isRulesCustomizationEnabled: true },
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -157,7 +157,6 @@ export class RequestContextFactory implements IRequestContextFactory {
|
|||
actionsClient,
|
||||
savedObjectsClient: coreContext.savedObjects.client,
|
||||
mlAuthz,
|
||||
experimentalFeatures: config.experimentalFeatures,
|
||||
productFeaturesService,
|
||||
license: licensing.license,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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: [],
|
||||
});
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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 }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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,
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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',
|
||||
},
|
||||
});
|
|
@ -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'));
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
|
@ -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: [],
|
||||
});
|
|
@ -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'));
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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: [],
|
||||
});
|
|
@ -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'));
|
||||
});
|
||||
}
|
|
@ -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]',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -65,15 +65,6 @@ describe(
|
|||
'Detection rules, Prebuilt Rules Customization workflow',
|
||||
{
|
||||
tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
|
||||
env: {
|
||||
ftrConfig: {
|
||||
kbnServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'prebuiltRulesCustomizationEnabled',
|
||||
])}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
() => {
|
||||
|
|
|
@ -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',
|
||||
])}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
() => {
|
||||
|
|
|
@ -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}`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue