From 9a357c9fef3490ff67e4dfaa1e91980f598e9b31 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 5 Jun 2024 22:45:39 -0400 Subject: [PATCH 1/8] adds simple diff tests --- .../trial_license_complete_tier/index.ts | 1 + .../upgrade_review_prebuilt_rules.ts | 599 ++++++++++++++++++ 2 files changed, 600 insertions(+) create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts index 5625d00b5293..0656288e2b4c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts @@ -16,5 +16,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./upgrade_prebuilt_rules')); loadTestFile(require.resolve('./upgrade_prebuilt_rules_with_historical_versions')); loadTestFile(require.resolve('./fleet_integration')); + loadTestFile(require.resolve('./upgrade_review_prebuilt_rules')); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts new file mode 100644 index 000000000000..66cc4a3a54b8 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts @@ -0,0 +1,599 @@ +/* + * 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 { + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { FtrProviderContext } from '../../../../../../ftr_provider_context'; +import { + deleteAllTimelines, + deleteAllPrebuiltRuleAssets, + createRuleAssetSavedObject, + installPrebuiltRules, + createPrebuiltRuleAssetSavedObjects, + reviewPrebuiltRulesToUpgrade, + patchRule, + createHistoricalPrebuiltRuleAssetSavedObjects, +} from '../../../../utils'; +import { deleteAllRules } from '../../../../../../../common/utils/security_solution'; + +export default ({ getService }: FtrProviderContext): void => { + const es = getService('es'); + const supertest = getService('supertest'); + const log = getService('log'); + + describe('@ess @serverless @skipInServerlessMKI review prebuilt rules updates from package with mock rule assets', () => { + beforeEach(async () => { + await deleteAllRules(supertest, log); + await deleteAllTimelines(es, log); + await deleteAllPrebuiltRuleAssets(es, log); + }); + + describe(`single line string fields`, () => { + const getRuleAssetSavedObjects = () => [ + createRuleAssetSavedObject({ rule_id: 'rule-1', version: 1, name: 'A' }), + ]; + + describe("when rule field doesn't have an update and has no custom value", () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); + await installPrebuiltRules(es, supertest); + + // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules).toEqual([]); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + }); + }); + + describe("when rule field doesn't have an update but has a custom value", () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); + await installPrebuiltRules(es, supertest); + + // Customize a single line string field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + name: 'B', + }); + + // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules).toEqual([]); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + }); + }); + + describe('when rule field has an update but does not have a custom value', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Increment the version of the installed rule, update a single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + name: 'B', + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + name: { + base_version: 'A', + current_version: 'A', + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 'B', + target_version: 'B', + }, + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + + describe('when rule field has an update and a custom value that are the same', () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Customize a single line string field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + name: 'B', + }); + + // Increment the version of the installed rule, update a single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + name: 'B', + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // but does NOT contain single line string field + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule field has an update and a custom value that are different', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Customize a single line string field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + name: 'B', + }); + + // Increment the version of the installed rule, update a single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + name: 'C', + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // and single line string field update has conflict + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + name: { + base_version: 'A', + current_version: 'B', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + has_conflict: true, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Conflict, + merged_version: 'B', + target_version: 'C', + }, + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(true); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule base version does not exist', () => { + describe('when rule field has an update and a custom value that are the same', () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Clear previous rule assets + await deleteAllPrebuiltRuleAssets(es, log); + + // Customize a single line string field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + name: 'B', + }); + + // Increment the version of the installed rule, update a single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + name: 'B', + }), + ]; + await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // but does NOT contain single line string field + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule field has an update and a custom value that are different', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Clear previous rule assets + await deleteAllPrebuiltRuleAssets(es, log); + + // Customize a single line string field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + name: 'B', + }); + + // Increment the version of the installed rule, update a single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + name: 'C', + }), + ]; + await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // and single line string field update does not have conflict + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + name: { + current_version: 'B', + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 'C', + target_version: 'C', + }, + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + }); + }); + }); + + describe(`number fields`, () => { + const getRuleAssetSavedObjects = () => [ + createRuleAssetSavedObject({ rule_id: 'rule-1', version: 1, risk_score: 1 }), + ]; + + describe("when rule field doesn't have an update and has no custom value", () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); + await installPrebuiltRules(es, supertest); + + // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules).toEqual([]); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + }); + }); + + describe("when rule field doesn't have an update but has a custom value", () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); + await installPrebuiltRules(es, supertest); + + // Customize a number field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + risk_score: 2, + }); + + // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules).toEqual([]); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + }); + }); + + describe('when rule field has an update but does not have a custom value', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Increment the version of the installed rule, update a number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + risk_score: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + risk_score: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + + describe('when rule field has an update and a custom value that are the same', () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Customize a number field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + risk_score: 2, + }); + + // Increment the version of the installed rule, update a number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + risk_score: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // but does NOT contain number field + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule field has an update and a custom value that are different', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Customize a number field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + risk_score: 2, + }); + + // Increment the version of the installed rule, update a number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + risk_score: 3, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // and number field update has conflict + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + risk_score: { + base_version: 1, + current_version: 2, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + has_conflict: true, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Conflict, + merged_version: 2, + target_version: 3, + }, + version: { + base_version: 1, + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(true); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule base version does not exist', () => { + describe('when rule field has an update and a custom value that are the same', () => { + it('should not show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Clear previous rule assets + await deleteAllPrebuiltRuleAssets(es, log); + + // Customize a number field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + risk_score: 2, + }); + + // Increment the version of the installed rule, update a number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + risk_score: 2, + }), + ]; + await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // but does NOT contain number field + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + + describe('when rule field has an update and a custom value that are different', () => { + it('should show in the upgrade/_review API response', async () => { + // Install all prebuilt detection rules + const ruleAssetSavedObjects = getRuleAssetSavedObjects(); + await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + await installPrebuiltRules(es, supertest); + + // Clear previous rule assets + await deleteAllPrebuiltRuleAssets(es, log); + + // Customize a number field on the installed rule + await patchRule(supertest, log, { + rule_id: 'rule-1', + risk_score: 2, + }); + + // Increment the version of the installed rule, update a number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + risk_score: 3, + }), + ]; + await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update + // and number field update does not have conflict + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + risk_score: { + current_version: 2, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 3, + target_version: 3, + }, + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); + }); + }); + }); + }); + }); + }); +}; From 0c319e7b7669e8afed83cc49a2f248a62973771c Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 5 Jun 2024 23:20:33 -0400 Subject: [PATCH 2/8] updates diff fields --- .../diff/calculation/algorithms/index.ts | 10 ++++++++ .../algorithms/number_diff_algorithm.ts | 5 +++- .../single_line_string_diff_algorithm.ts | 5 +++- .../calculation/calculate_rule_fields_diff.ts | 25 ++++++++++--------- 4 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts new file mode 100644 index 000000000000..57dd3553cb4c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { numberDiffAlgorithm } from './number_diff_algorithm'; +export { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorithm'; +export { simpleDiffAlgorithm } from './simple_diff_algorithm'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.ts index 513d9047c7a5..30c32a475ecf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.ts @@ -5,6 +5,9 @@ * 2.0. */ +import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { simpleDiffAlgorithm } from './simple_diff_algorithm'; -export const numberDiffAlgorithm = simpleDiffAlgorithm; +export const numberDiffAlgorithm = ( + versions: ThreeVersionsOf +) => simpleDiffAlgorithm(versions); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.ts index 901bb6c050e5..f80d8b63c8da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.ts @@ -5,6 +5,9 @@ * 2.0. */ +import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { simpleDiffAlgorithm } from './simple_diff_algorithm'; -export const singleLineStringDiffAlgorithm = simpleDiffAlgorithm; +export const singleLineStringDiffAlgorithm = ( + versions: ThreeVersionsOf +) => simpleDiffAlgorithm(versions); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts index 5639cc07ebed..e8408f48ff0c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts @@ -38,6 +38,7 @@ import type { ThreeVersionsOf } from '../../../../../../../common/api/detection_ import { MissingVersion } from '../../../../../../../common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; import { calculateFieldsDiffFor } from './diff_calculation_helpers'; import { simpleDiffAlgorithm } from './algorithms/simple_diff_algorithm'; +import { numberDiffAlgorithm, singleLineStringDiffAlgorithm } from './algorithms'; const BASE_TYPE_ERROR = `Base version can't be of different rule type`; const TARGET_TYPE_ERROR = `Target version can't be of different rule type`; @@ -168,14 +169,14 @@ const calculateCommonFieldsDiff = ( const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { rule_id: simpleDiffAlgorithm, - version: simpleDiffAlgorithm, + version: numberDiffAlgorithm, meta: simpleDiffAlgorithm, - name: simpleDiffAlgorithm, + name: singleLineStringDiffAlgorithm, tags: simpleDiffAlgorithm, description: simpleDiffAlgorithm, - severity: simpleDiffAlgorithm, + severity: singleLineStringDiffAlgorithm, severity_mapping: simpleDiffAlgorithm, - risk_score: simpleDiffAlgorithm, + risk_score: numberDiffAlgorithm, risk_score_mapping: simpleDiffAlgorithm, references: simpleDiffAlgorithm, false_positives: simpleDiffAlgorithm, @@ -185,12 +186,12 @@ const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor related_integrations: simpleDiffAlgorithm, required_fields: simpleDiffAlgorithm, author: simpleDiffAlgorithm, - license: simpleDiffAlgorithm, + license: singleLineStringDiffAlgorithm, rule_schedule: simpleDiffAlgorithm, actions: simpleDiffAlgorithm, throttle: simpleDiffAlgorithm, exceptions_list: simpleDiffAlgorithm, - max_signals: simpleDiffAlgorithm, + max_signals: numberDiffAlgorithm, rule_name_override: simpleDiffAlgorithm, timestamp_override: simpleDiffAlgorithm, timeline_template: simpleDiffAlgorithm, @@ -233,9 +234,9 @@ const eqlFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor = { type: simpleDiffAlgorithm, eql_query: simpleDiffAlgorithm, data_source: simpleDiffAlgorithm, - event_category_override: simpleDiffAlgorithm, - timestamp_field: simpleDiffAlgorithm, - tiebreaker_field: simpleDiffAlgorithm, + event_category_override: singleLineStringDiffAlgorithm, + timestamp_field: singleLineStringDiffAlgorithm, + tiebreaker_field: singleLineStringDiffAlgorithm, }; const calculateEsqlFieldsDiff = ( @@ -262,7 +263,7 @@ const threatMatchFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor Date: Fri, 7 Jun 2024 13:01:42 -0400 Subject: [PATCH 3/8] update imports --- .../logic/diff/calculation/calculate_rule_fields_diff.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts index e8408f48ff0c..fd4e559805c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts @@ -37,8 +37,11 @@ import type { FieldsDiffAlgorithmsFor } from '../../../../../../../common/api/de import type { ThreeVersionsOf } from '../../../../../../../common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; import { MissingVersion } from '../../../../../../../common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; import { calculateFieldsDiffFor } from './diff_calculation_helpers'; -import { simpleDiffAlgorithm } from './algorithms/simple_diff_algorithm'; -import { numberDiffAlgorithm, singleLineStringDiffAlgorithm } from './algorithms'; +import { + simpleDiffAlgorithm, + numberDiffAlgorithm, + singleLineStringDiffAlgorithm, +} from './algorithms'; const BASE_TYPE_ERROR = `Base version can't be of different rule type`; const TARGET_TYPE_ERROR = `Target version can't be of different rule type`; From be1cfbbafa6887cbbc4b3e6efa603498d834dbd0 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 10 Jun 2024 11:21:34 -0400 Subject: [PATCH 4/8] matches updated test plan --- .../installation_and_upgrade.md | 4 +- .../review_rule_upgrade_route.ts | 12 ++- .../upgrade_review_prebuilt_rules.ts | 98 +++++++++++++++++-- 3 files changed, 101 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md index e6454eb42414..e2f98296e199 100644 --- a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md @@ -893,8 +893,8 @@ And field is customized by the user (current version != base versio And field is updated by Elastic in this upgrade (target version != base version) And customized field is the same as the Elastic update in this upgrade (current version == target version) Then for field the diff algorithm should output the current version as the merged one without a conflict -And field should not be returned from the `upgrade/_review` API endpoint -And field should not be shown in the upgrade preview UI +And field should be returned from the `upgrade/_review` API endpoint +And field should be shown in the upgrade preview UI Examples: | field_name | base_version | current_version | target_version | diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts index 2270c6fea739..204d316af809 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts @@ -7,7 +7,10 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { pickBy } from 'lodash'; -import { REVIEW_RULE_UPGRADE_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules'; +import { + REVIEW_RULE_UPGRADE_URL, + ThreeWayDiffOutcome, +} from '../../../../../../common/api/detection_engine/prebuilt_rules'; import type { ReviewRuleUpgradeResponseBody, RuleUpgradeInfoForReview, @@ -120,7 +123,12 @@ const calculateRuleInfos = (results: CalculateRuleDiffResult[]): RuleUpgradeInfo diff: { fields: pickBy>( ruleDiff.fields, - (fieldDiff) => fieldDiff.has_update || fieldDiff.has_conflict + (fieldDiff) => + fieldDiff.has_update || + fieldDiff.has_conflict || + // For full transparancy we display all user-customized fields, even if nothing changes in the field's target or merged versions + fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueNoUpdate || + fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueSameUpdate ), has_conflict: ruleDiff.has_conflict, }, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts index 66cc4a3a54b8..5c85718d7203 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts @@ -64,10 +64,41 @@ export default ({ getService }: FtrProviderContext): void => { name: 'B', }); - // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + // Increment the version of the installed rule, do NOT update the related single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + name: 'A', + version: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that single line string diff field is returned but field does not have an update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules).toEqual([]); - expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + name: { + base_version: 'A', + current_version: 'B', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + has_conflict: false, + has_update: false, + merge_outcome: ThreeWayMergeOutcome.Current, + merged_version: 'B', + target_version: 'A', + }, + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); }); }); @@ -140,9 +171,18 @@ export default ({ getService }: FtrProviderContext): void => { await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update - // but does NOT contain single line string field const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + name: { + base_version: 'A', + current_version: 'B', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + has_conflict: false, + has_update: false, + merge_outcome: ThreeWayMergeOutcome.Current, + merged_version: 'B', + target_version: 'B', + }, version: { base_version: 1, current_version: 1, @@ -345,10 +385,41 @@ export default ({ getService }: FtrProviderContext): void => { risk_score: 2, }); - // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + // Increment the version of the installed rule, do NOT update the related number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + risk_score: 1, + version: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that number diff field is returned but field does not have an update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules).toEqual([]); - expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + risk_score: { + base_version: 1, + current_version: 2, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + has_conflict: false, + has_update: false, + merge_outcome: ThreeWayMergeOutcome.Current, + merged_version: 2, + target_version: 1, + }, + version: { + current_version: 1, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + has_conflict: false, + has_update: true, + merge_outcome: ThreeWayMergeOutcome.Target, + merged_version: 2, + target_version: 2, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); }); }); @@ -420,10 +491,19 @@ export default ({ getService }: FtrProviderContext): void => { ]; await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); - // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update - // but does NOT contain number field + // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update and contains number field const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + risk_score: { + base_version: 1, + current_version: 2, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + has_conflict: false, + has_update: false, + merge_outcome: ThreeWayMergeOutcome.Current, + merged_version: 2, + target_version: 2, + }, version: { base_version: 1, current_version: 1, From 073455bc87e4396a157cb1996d79e829d1087801 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 10 Jun 2024 16:12:09 -0400 Subject: [PATCH 5/8] fix translation bugs --- .../rule_creation_ui/components/step_about_rule/schema.tsx | 2 +- .../rule_management/components/rule_details/translations.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx index b4fa6376ea81..83d79debb757 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx @@ -217,7 +217,7 @@ export const schema: FormSchema = { label: i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldMitreThreatLabel', { - defaultMessage: 'MITRE ATT&CK\\u2122', + defaultMessage: 'MITRE ATT&CK\u2122', } ), labelAppend: OptionalFieldLabel, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts index 9025b184af1d..3e75677d54da 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts @@ -171,7 +171,7 @@ export const RULE_NAME_OVERRIDE_FIELD_LABEL = i18n.translate( export const THREAT_FIELD_LABEL = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.threatFieldLabel', { - defaultMessage: 'MITRE ATT&CK\\u2122', + defaultMessage: 'MITRE ATT&CK\u2122', } ); From 2cb9535b012b172f65a7a4e0a6351042bea3f6d0 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 11 Jun 2024 11:19:07 -0400 Subject: [PATCH 6/8] adds filter for unsupported display type --- .../components/rule_details/helpers.ts | 20 +++++++++++++++++++ .../rule_details/per_field_rule_diff_tab.tsx | 8 +++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts index f08187800789..fd933ba33ae7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { RuleFieldsDiff, ThreeWayDiff } from '../../../../../common/api/detection_engine'; +import { ThreeWayDiffOutcome } from '../../../../../common/api/detection_engine'; import type { FieldsGroupDiff } from '../../model/rule_details/rule_field_diff'; import { ABOUT_UPGRADE_FIELD_ORDER, @@ -36,3 +38,21 @@ export const getSectionedFieldDiffs = (fields: FieldsGroupDiff[]) => { setupFields, }; }; + +/** + * Filters out any fields that have a `diff_outcome` of `CustomizedValueNoUpdate` + * or `CustomizedValueSameUpdate` as they are not supported for display in the + * current per-field rule diff flyout + */ +export const filterUnsupportedDiffOutcomes = ( + fields: Partial +): Partial => + Object.fromEntries( + Object.entries(fields).filter(([key, value]) => { + const diff = value as ThreeWayDiff; + return ( + diff.diff_outcome !== ThreeWayDiffOutcome.CustomizedValueNoUpdate && + diff.diff_outcome !== ThreeWayDiffOutcome.CustomizedValueSameUpdate + ); + }) + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.tsx index 4a90f8624d21..f03cd8ed23bb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.tsx @@ -10,7 +10,7 @@ import type { PartialRuleDiff, RuleFieldsDiff } from '../../../../../common/api/ import { getFormattedFieldDiffGroups } from './per_field_diff/get_formatted_field_diff'; import { UPGRADE_FIELD_ORDER } from './constants'; import { RuleDiffHeaderBar, RuleDiffSection } from './diff_components'; -import { getSectionedFieldDiffs } from './helpers'; +import { filterUnsupportedDiffOutcomes, getSectionedFieldDiffs } from './helpers'; import type { FieldsGroupDiff } from '../../model/rule_details/rule_field_diff'; import * as i18n from './translations'; @@ -21,9 +21,11 @@ interface PerFieldRuleDiffTabProps { export const PerFieldRuleDiffTab = ({ ruleDiff }: PerFieldRuleDiffTabProps) => { const fieldsToRender = useMemo(() => { const fields: FieldsGroupDiff[] = []; - for (const field of Object.keys(ruleDiff.fields)) { + // Filter out diff outcomes that we don't support displaying in the per-field diff flyout + const filteredFieldDiffs = filterUnsupportedDiffOutcomes(ruleDiff.fields); + for (const field of Object.keys(filteredFieldDiffs)) { const typedField = field as keyof RuleFieldsDiff; - const formattedDiffs = getFormattedFieldDiffGroups(typedField, ruleDiff.fields); + const formattedDiffs = getFormattedFieldDiffGroups(typedField, filteredFieldDiffs); fields.push({ formattedDiffs, fieldsGroupName: typedField }); } const sortedFields = fields.sort( From edd3b03abbf4492c83bc465b557774656b75eba4 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 12 Jun 2024 10:59:21 -0400 Subject: [PATCH 7/8] addresses changes --- .../review_rule_upgrade_route.ts | 2 +- .../calculation/calculate_rule_fields_diff.ts | 2 +- .../upgrade_review_prebuilt_rules.ts | 280 ++++++++++-------- 3 files changed, 160 insertions(+), 124 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts index 204d316af809..bb4df9dd1dd8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts @@ -126,7 +126,7 @@ const calculateRuleInfos = (results: CalculateRuleDiffResult[]): RuleUpgradeInfo (fieldDiff) => fieldDiff.has_update || fieldDiff.has_conflict || - // For full transparancy we display all user-customized fields, even if nothing changes in the field's target or merged versions + // For full transparency we display all user-customized fields, even if nothing changes in the field's target or merged versions fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueNoUpdate || fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueSameUpdate ), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts index fd4e559805c3..ea482858650f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/calculate_rule_fields_diff.ts @@ -38,8 +38,8 @@ import type { ThreeVersionsOf } from '../../../../../../../common/api/detection_ import { MissingVersion } from '../../../../../../../common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff'; import { calculateFieldsDiffFor } from './diff_calculation_helpers'; import { - simpleDiffAlgorithm, numberDiffAlgorithm, + simpleDiffAlgorithm, singleLineStringDiffAlgorithm, } from './algorithms'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts index 5c85718d7203..9ad266e20740 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.ts @@ -41,20 +41,42 @@ export default ({ getService }: FtrProviderContext): void => { describe("when rule field doesn't have an update and has no custom value", () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules + // Install base prebuilt detection rule await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); - // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + // Increment the version of the installed rule, do NOT update the related single line string field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + name: 'A', + version: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that there is 1 rule eligable for update but single line string field is NOT returned const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules).toEqual([]); - expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + expect(reviewResponse.rules[0].diff.fields).toEqual({ + version: { + base_version: 1, + current_version: 1, + target_version: 2, + merged_version: 2, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_conflict: false, + has_update: true, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); }); }); describe("when rule field doesn't have an update but has a custom value", () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules + // Install base prebuilt detection rule await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); @@ -76,25 +98,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that single line string diff field is returned but field does not have an update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ name: { base_version: 'A', current_version: 'B', + target_version: 'A', + merged_version: 'B', diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, has_conflict: false, has_update: false, - merge_outcome: ThreeWayMergeOutcome.Current, - merged_version: 'B', - target_version: 'A', }, version: { + base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -104,9 +127,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update but does not have a custom value', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Increment the version of the installed rule, update a single line string field, and create the new rule assets @@ -121,26 +143,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ name: { base_version: 'A', current_version: 'A', + target_version: 'B', + merged_version: 'B', diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 'B', - target_version: 'B', }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -148,10 +170,9 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('when rule field has an update and a custom value that are the same', () => { - it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + it('should show in the upgrade/_review API response', async () => { + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Customize a single line string field on the installed rule @@ -172,26 +193,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ name: { base_version: 'A', current_version: 'B', + target_version: 'B', + merged_version: 'B', diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, has_conflict: false, has_update: false, - merge_outcome: ThreeWayMergeOutcome.Current, - merged_version: 'B', - target_version: 'B', }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -201,9 +222,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update and a custom value that are different', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Customize a single line string field on the installed rule @@ -225,26 +245,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update // and single line string field update has conflict const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ name: { base_version: 'A', current_version: 'B', + target_version: 'C', + merged_version: 'B', diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Conflict, has_conflict: true, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Conflict, - merged_version: 'B', - target_version: 'C', }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(true); @@ -255,9 +275,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule base version does not exist', () => { describe('when rule field has an update and a custom value that are the same', () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Clear previous rule assets @@ -282,15 +301,15 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update // but does NOT contain single line string field const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ version: { current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -300,9 +319,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update and a custom value that are different', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Clear previous rule assets @@ -325,26 +343,26 @@ export default ({ getService }: FtrProviderContext): void => { await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update - // and single line string field update does not have conflict + // and single line string field update does not have a conflict const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ name: { current_version: 'B', + target_version: 'C', + merged_version: 'C', diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 'C', - target_version: 'C', }, version: { current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -362,20 +380,42 @@ export default ({ getService }: FtrProviderContext): void => { describe("when rule field doesn't have an update and has no custom value", () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules + // Install base prebuilt detection rule await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); - // Call the upgrade review prebuilt rules endpoint and check that no rules are eligible for update + // Increment the version of the installed rule, do NOT update the related number field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + risk_score: 1, + version: 2, + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that there is 1 rule eligable for update but number field is NOT returned const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules).toEqual([]); - expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(0); + expect(reviewResponse.rules[0].diff.fields).toEqual({ + version: { + base_version: 1, + current_version: 1, + target_version: 2, + merged_version: 2, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_conflict: false, + has_update: true, + }, + }); + expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); + expect(reviewResponse.stats.num_rules_to_upgrade_total).toBe(1); }); }); describe("when rule field doesn't have an update but has a custom value", () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules + // Install base prebuilt detection rule await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); @@ -397,25 +437,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that number diff field is returned but field does not have an update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ risk_score: { base_version: 1, current_version: 2, + target_version: 1, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, has_conflict: false, has_update: false, - merge_outcome: ThreeWayMergeOutcome.Current, - merged_version: 2, - target_version: 1, }, version: { + base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -425,9 +466,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update but does not have a custom value', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Increment the version of the installed rule, update a number field, and create the new rule assets @@ -442,26 +482,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ risk_score: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -469,10 +509,9 @@ export default ({ getService }: FtrProviderContext): void => { }); describe('when rule field has an update and a custom value that are the same', () => { - it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + it('should show in the upgrade/_review API response', async () => { + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Customize a number field on the installed rule @@ -493,26 +532,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update and contains number field const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ risk_score: { base_version: 1, current_version: 2, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, has_conflict: false, has_update: false, - merge_outcome: ThreeWayMergeOutcome.Current, - merged_version: 2, - target_version: 2, }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -522,9 +561,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update and a custom value that are different', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createHistoricalPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Customize a number field on the installed rule @@ -546,26 +584,26 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update // and number field update has conflict const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ risk_score: { base_version: 1, current_version: 2, + target_version: 3, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Conflict, has_conflict: true, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Conflict, - merged_version: 2, - target_version: 3, }, version: { base_version: 1, current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(true); @@ -576,9 +614,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule base version does not exist', () => { describe('when rule field has an update and a custom value that are the same', () => { it('should not show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Clear previous rule assets @@ -603,15 +640,15 @@ export default ({ getService }: FtrProviderContext): void => { // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update // but does NOT contain number field const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ version: { current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); @@ -621,9 +658,8 @@ export default ({ getService }: FtrProviderContext): void => { describe('when rule field has an update and a custom value that are different', () => { it('should show in the upgrade/_review API response', async () => { - // Install all prebuilt detection rules - const ruleAssetSavedObjects = getRuleAssetSavedObjects(); - await createPrebuiltRuleAssetSavedObjects(es, ruleAssetSavedObjects); + // Install base prebuilt detection rule + await createPrebuiltRuleAssetSavedObjects(es, getRuleAssetSavedObjects()); await installPrebuiltRules(es, supertest); // Clear previous rule assets @@ -646,26 +682,26 @@ export default ({ getService }: FtrProviderContext): void => { await createPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); // Call the upgrade review prebuilt rules endpoint and check that one rule is eligible for update - // and number field update does not have conflict + // and number field update does not have a conflict const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); - expect(reviewResponse.rules[0].diff.fields).toMatchObject({ + expect(reviewResponse.rules[0].diff.fields).toEqual({ risk_score: { current_version: 2, + target_version: 3, + merged_version: 3, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 3, - target_version: 3, }, version: { current_version: 1, + target_version: 2, + merged_version: 2, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, has_conflict: false, has_update: true, - merge_outcome: ThreeWayMergeOutcome.Target, - merged_version: 2, - target_version: 2, }, }); expect(reviewResponse.rules[0].diff.has_conflict).toBe(false); From 2828693a435d8e6ca229634f66a68b892504c8b1 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 12 Jun 2024 23:14:51 -0400 Subject: [PATCH 8/8] addresses comments --- .../api/review_rule_upgrade/review_rule_upgrade_route.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts index bb4df9dd1dd8..de7db929790d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts @@ -123,12 +123,7 @@ const calculateRuleInfos = (results: CalculateRuleDiffResult[]): RuleUpgradeInfo diff: { fields: pickBy>( ruleDiff.fields, - (fieldDiff) => - fieldDiff.has_update || - fieldDiff.has_conflict || - // For full transparency we display all user-customized fields, even if nothing changes in the field's target or merged versions - fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueNoUpdate || - fieldDiff.diff_outcome === ThreeWayDiffOutcome.CustomizedValueSameUpdate + (fieldDiff) => fieldDiff.diff_outcome !== ThreeWayDiffOutcome.StockValueNoUpdate ), has_conflict: ruleDiff.has_conflict, },