diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts index 86956c001843e..2dfb1583de190 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/index.ts @@ -21,3 +21,5 @@ export * from './model/diff/rule_diff/rule_diff'; export * from './model/diff/three_way_diff/three_way_diff_outcome'; export * from './model/diff/three_way_diff/three_way_diff'; export * from './model/diff/three_way_diff/three_way_diff_conflict'; +export * from './model/diff/three_way_diff/three_way_merge_outcome'; + diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts index 2fcb86d780f02..7b71bb6f16c7a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff.ts @@ -7,6 +7,7 @@ import type { ThreeWayDiffOutcome } from './three_way_diff_outcome'; import type { ThreeWayDiffConflict } from './three_way_diff_conflict'; +import type { ThreeWayMergeOutcome } from './three_way_merge_outcome'; /** * A symbol that represents a missing value and used when a base version of a @@ -110,6 +111,14 @@ export interface ThreeWayDiff { */ diff_outcome: ThreeWayDiffOutcome; + /** + * The type of result of an automatic three-way merge of three values: + * - current version + * - target version + * - merged version + */ + merge_outcome: ThreeWayMergeOutcome; + /** * Boolean which determines if a base version was found and returned for the three-way-diff of the field * - true: the base version of the field was found and is either defined or undefined diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts index afa63c01744e1..19a675b8de279 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts @@ -27,6 +27,12 @@ export enum ThreeWayDiffOutcome { /** Customized rule, the value has changed in the target version and is not equal to the current version. */ CustomizedValueCanUpdate = 'BASE=A, CURRENT=B, TARGET=C', + + /** Missing base, the value hasn't changed in the target version. */ + MissingBaseNoUpdate = 'BASE=-, CURRENT=A, TARGET=A', + + /** Missing base, the value changed in the target version. */ + MissingBaseCanUpdate = 'BASE=-, CURRENT=A, TARGET=B', } export const determineDiffOutcome = ( @@ -85,12 +91,12 @@ const getThreeWayDiffOutcome = ({ if (!hasBaseVersion) { /** * We couldn't find the base version of the rule in the package so further - * version comparison is not possible. We assume that the rule is not + * version comparison is not possible. We assume that the rule is * customized and the value can be updated if there's an update. */ return currentEqlTarget - ? ThreeWayDiffOutcome.StockValueNoUpdate - : ThreeWayDiffOutcome.StockValueCanUpdate; + ? ThreeWayDiffOutcome.MissingBaseNoUpdate + : ThreeWayDiffOutcome.MissingBaseCanUpdate; } if (baseEqlCurrent) { diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts new file mode 100644 index 0000000000000..d770125553744 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_merge_outcome.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * The type of result of an automatic three-way merge of three values: + * - current version + * - target version + * - merged version + */ +export enum ThreeWayMergeOutcome { + /** Took current version and returned as the merged one. */ + Current = 'CURRENT', + + /** Took target version and returned as the merged one. */ + Target = 'TARGET', + + /** Merged three versions successfully into a new one. */ + Merged = 'MERGED', +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.test.tsx index 4d2dc56ac744e..5e417b3d862ed 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/per_field_rule_diff_tab.test.tsx @@ -9,6 +9,7 @@ import { KqlQueryType, ThreeWayDiffConflict, ThreeWayDiffOutcome, + ThreeWayMergeOutcome, } from '../../../../../common/api/detection_engine'; import type { PartialRuleDiff } from '../../../../../common/api/detection_engine'; import { TestProviders } from '../../../../common/mock'; @@ -21,6 +22,7 @@ const ruleFieldsDiffBaseFieldsMock = { conflict: ThreeWayDiffConflict.NONE, has_update: true, has_base_version: true, + merge_outcome: ThreeWayMergeOutcome.Target, }; const ruleFieldsDiffMock: PartialRuleDiff = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.test.ts index cc9c73d5c9a1a..dd1d6abaa04b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.test.ts @@ -8,6 +8,7 @@ import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { ThreeWayDiffOutcome, + ThreeWayMergeOutcome, MissingVersion, ThreeWayDiffConflict, } from '../../../../../../../../common/api/detection_engine'; @@ -28,6 +29,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, conflict: ThreeWayDiffConflict.NONE, + merge_outcome: ThreeWayMergeOutcome.Current, }) ); }); @@ -46,6 +48,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, conflict: ThreeWayDiffConflict.NONE, + merge_outcome: ThreeWayMergeOutcome.Current, }) ); }); @@ -64,6 +67,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: mockVersions.target_version, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, conflict: ThreeWayDiffConflict.NONE, + merge_outcome: ThreeWayMergeOutcome.Target, }) ); }); @@ -82,6 +86,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, conflict: ThreeWayDiffConflict.NONE, + merge_outcome: ThreeWayMergeOutcome.Current, }) ); }); @@ -103,6 +108,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, conflict: ThreeWayDiffConflict.SOLVABLE, + merge_outcome: ThreeWayMergeOutcome.Merged, }) ); }); @@ -121,6 +127,7 @@ describe('multiLineStringDiffAlgorithm', () => { merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, conflict: ThreeWayDiffConflict.NON_SOLVABLE, + merge_outcome: ThreeWayMergeOutcome.Current, }) ); }); @@ -141,7 +148,8 @@ describe('multiLineStringDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.current_version, - diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -161,7 +169,8 @@ describe('multiLineStringDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.target_version, - diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.SOLVABLE, }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.ts index 30ff8fb37ef6e..9cb326102ae6d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/multi_line_string_diff_algorithm.ts @@ -14,9 +14,10 @@ import type { import { determineDiffOutcome, determineIfValueCanUpdate, - ThreeWayDiffOutcome, MissingVersion, ThreeWayDiffConflict, + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, } from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; /** @@ -36,7 +37,7 @@ export const multiLineStringDiffAlgorithm = ( const hasBaseVersion = baseVersion !== MissingVersion; - const { conflict, mergedVersion } = mergeVersions({ + const { mergeOutcome, conflict, mergedVersion } = mergeVersions({ hasBaseVersion, baseVersion, currentVersion, @@ -50,6 +51,7 @@ export const multiLineStringDiffAlgorithm = ( current_version: currentVersion, target_version: targetVersion, merged_version: mergedVersion, + merge_outcome: mergeOutcome, diff_outcome: diffOutcome, conflict, @@ -58,6 +60,7 @@ export const multiLineStringDiffAlgorithm = ( }; interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; mergedVersion: string; conflict: ThreeWayDiffConflict; } @@ -78,33 +81,26 @@ const mergeVersions = ({ diffOutcome, }: MergeArgs): MergeResult => { switch (diffOutcome) { - case ThreeWayDiffOutcome.StockValueNoUpdate: // Scenarios AAA and -AA - case ThreeWayDiffOutcome.CustomizedValueNoUpdate: // Scenario ABA - case ThreeWayDiffOutcome.CustomizedValueSameUpdate: // Scenario ABB + // Scenario -AA is treated as scenario AAA: + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseNoUpdate: + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: return { conflict: ThreeWayDiffConflict.NONE, mergedVersion: currentVersion, + mergeOutcome: ThreeWayMergeOutcome.Current, }; case ThreeWayDiffOutcome.StockValueCanUpdate: { - if (!hasBaseVersion) { - // Scenario -AB. Treated as scenario ABC, returns target - // version and marked as "SOLVABLE" conflict. - // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 - return { - mergedVersion: targetVersion, - conflict: ThreeWayDiffConflict.SOLVABLE, - }; - } - - // Scenario AAB return { conflict: ThreeWayDiffConflict.NONE, mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, }; } - // Scenario ABC case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { // TS does not realize that in ABC scenario, baseVersion cannot be missing // Missing baseVersion scenarios were handled as -AA and -AB. @@ -116,12 +112,25 @@ const mergeVersions = ({ ? { conflict: ThreeWayDiffConflict.NON_SOLVABLE, mergedVersion: currentVersion, + mergeOutcome: ThreeWayMergeOutcome.Current, } : { conflict: ThreeWayDiffConflict.SOLVABLE, mergedVersion: mergedVersion.result.join(''), + mergeOutcome: ThreeWayMergeOutcome.Merged, }; } + + // Scenario -AB is treated as scenario ABC, but marked as + // SOLVABLE, and returns the target version as the merged version + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseCanUpdate: { + return { + mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.SOLVABLE, + }; + } default: return assertUnreachable(diffOutcome); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts index 36d8875a55eed..837fb85c3b2c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/number_diff_algorithm.test.ts @@ -8,6 +8,7 @@ import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { ThreeWayDiffOutcome, + ThreeWayMergeOutcome, MissingVersion, ThreeWayDiffConflict, } from '../../../../../../../../common/api/detection_engine'; @@ -27,6 +28,7 @@ describe('numberDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -45,6 +47,7 @@ describe('numberDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -63,6 +66,7 @@ describe('numberDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.target_version, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -81,6 +85,7 @@ describe('numberDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -99,6 +104,7 @@ describe('numberDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NON_SOLVABLE, }) ); @@ -119,7 +125,8 @@ describe('numberDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.current_version, - diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -139,7 +146,8 @@ describe('numberDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.target_version, - diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.SOLVABLE, }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts index b8aaffb6fd771..f041aa139bf41 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.test.ts @@ -8,6 +8,7 @@ import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { ThreeWayDiffOutcome, + ThreeWayMergeOutcome, MissingVersion, ThreeWayDiffConflict, } from '../../../../../../../../common/api/detection_engine'; @@ -27,6 +28,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -45,6 +47,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -63,6 +66,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.target_version, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -81,6 +85,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -100,6 +105,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, conflict: ThreeWayDiffConflict.SOLVABLE, }) ); @@ -120,7 +126,8 @@ describe('scalarArrayDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.current_version, - diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -140,7 +147,8 @@ describe('scalarArrayDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.target_version, - diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.SOLVABLE, }) ); @@ -161,6 +169,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -181,6 +190,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -200,6 +210,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -219,6 +230,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -238,6 +250,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: expectedMergedVersion, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, conflict: ThreeWayDiffConflict.SOLVABLE, }) ); @@ -258,6 +271,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -276,6 +290,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -294,6 +309,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.target_version, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -312,6 +328,7 @@ describe('scalarArrayDiffAlgorithm', () => { expect.objectContaining({ merged_version: [], diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts index cb84ab8633b9a..3b69b7c73fa46 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts @@ -17,6 +17,7 @@ import { ThreeWayDiffOutcome, MissingVersion, ThreeWayDiffConflict, + ThreeWayMergeOutcome, } from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; /** @@ -37,7 +38,8 @@ export const scalarArrayDiffAlgorithm = ( const valueCanUpdate = determineIfValueCanUpdate(diffOutcome); const hasBaseVersion = baseVersion !== MissingVersion; - const { conflict, mergedVersion } = mergeVersions({ + + const { mergeOutcome, conflict, mergedVersion } = mergeVersions({ hasBaseVersion, baseVersion: hasBaseVersion ? baseVersion : undefined, currentVersion, @@ -51,6 +53,7 @@ export const scalarArrayDiffAlgorithm = ( current_version: currentVersion, target_version: targetVersion, merged_version: mergedVersion, + merge_outcome: mergeOutcome, diff_outcome: diffOutcome, conflict, @@ -59,6 +62,7 @@ export const scalarArrayDiffAlgorithm = ( }; interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; mergedVersion: TValue[]; conflict: ThreeWayDiffConflict; } @@ -83,33 +87,26 @@ const mergeVersions = ({ const dedupedTargetVersion = uniq(targetVersion); switch (diffOutcome) { - case ThreeWayDiffOutcome.StockValueNoUpdate: // Scenarios AAA and -AA - case ThreeWayDiffOutcome.CustomizedValueNoUpdate: // Scenario ABA - case ThreeWayDiffOutcome.CustomizedValueSameUpdate: // Scenario ABB + // Scenario -AA is treated as scenario AAA: + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseNoUpdate: + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: return { conflict: ThreeWayDiffConflict.NONE, mergedVersion: dedupedCurrentVersion, + mergeOutcome: ThreeWayMergeOutcome.Current, }; case ThreeWayDiffOutcome.StockValueCanUpdate: { - if (!hasBaseVersion) { - // Scenario -AB. Treated as scenario ABC, returns target - // version and marked as "SOLVABLE" conflict. - // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 - return { - mergedVersion: targetVersion, - conflict: ThreeWayDiffConflict.SOLVABLE, - }; - } - - // Scenario AAB return { conflict: ThreeWayDiffConflict.NONE, mergedVersion: dedupedTargetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, }; } - // Scenario ABC case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { const addedCurrent = difference(dedupedCurrentVersion, dedupedBaseVersion as TValue[]); const removedCurrent = difference(dedupedBaseVersion, dedupedCurrentVersion); @@ -125,6 +122,18 @@ const mergeVersions = ({ return { conflict: ThreeWayDiffConflict.SOLVABLE, mergedVersion: merged, + mergeOutcome: ThreeWayMergeOutcome.Merged, + }; + } + + // Scenario -AB is treated as scenario ABC, but marked as + // SOLVABLE, and returns the target version as the merged version + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseCanUpdate: { + return { + mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.SOLVABLE, }; } default: diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts index 40e9767c08bc6..1673b87003a00 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/simple_diff_algorithm.ts @@ -16,6 +16,7 @@ import { MissingVersion, ThreeWayDiffConflict, ThreeWayDiffOutcome, + ThreeWayMergeOutcome, } from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; /** @@ -37,7 +38,7 @@ export const simpleDiffAlgorithm = ( const hasBaseVersion = baseVersion !== MissingVersion; - const { conflict, mergedVersion } = mergeVersions({ + const { mergeOutcome, conflict, mergedVersion } = mergeVersions({ hasBaseVersion, currentVersion, targetVersion, @@ -50,6 +51,7 @@ export const simpleDiffAlgorithm = ( current_version: currentVersion, target_version: targetVersion, merged_version: mergedVersion, + merge_outcome: mergeOutcome, diff_outcome: diffOutcome, has_update: valueCanUpdate, @@ -58,6 +60,7 @@ export const simpleDiffAlgorithm = ( }; interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; mergedVersion: TValue; conflict: ThreeWayDiffConflict; } @@ -76,37 +79,41 @@ const mergeVersions = ({ diffOutcome, }: MergeArgs): MergeResult => { switch (diffOutcome) { - case ThreeWayDiffOutcome.StockValueNoUpdate: // Scenarios AAA and -AA - case ThreeWayDiffOutcome.CustomizedValueNoUpdate: // Scenario ABA - case ThreeWayDiffOutcome.CustomizedValueSameUpdate: // Scenario ABB + // Scenario -AA is treated as scenario AAA: + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseNoUpdate: + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: return { - mergedVersion: currentVersion, conflict: ThreeWayDiffConflict.NONE, + mergedVersion: currentVersion, + mergeOutcome: ThreeWayMergeOutcome.Current, }; case ThreeWayDiffOutcome.StockValueCanUpdate: { - if (!hasBaseVersion) { - // Scenario -AB. Treated as scenario ABC, returns target - // version and marked as "SOLVABLE" conflict. - // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 - return { - mergedVersion: targetVersion, - conflict: ThreeWayDiffConflict.SOLVABLE, - }; - } - - // Scenario AAB return { - mergedVersion: targetVersion, conflict: ThreeWayDiffConflict.NONE, + mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, }; } - - // Scenario ABC case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { return { - mergedVersion: currentVersion, conflict: ThreeWayDiffConflict.NON_SOLVABLE, + mergedVersion: currentVersion, + mergeOutcome: ThreeWayMergeOutcome.Current, + }; + } + + // Scenario -AB is treated as scenario ABC, but marked as + // SOLVABLE, and returns the target version as the merged version + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseCanUpdate: { + return { + mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.SOLVABLE, }; } default: diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts index 4c91f9e5f3e2a..af238283ca21f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/single_line_string_diff_algorithm.test.ts @@ -8,6 +8,7 @@ import type { ThreeVersionsOf } from '../../../../../../../../common/api/detection_engine'; import { ThreeWayDiffOutcome, + ThreeWayMergeOutcome, MissingVersion, ThreeWayDiffConflict, } from '../../../../../../../../common/api/detection_engine'; @@ -27,6 +28,7 @@ describe('singleLineStringDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -45,6 +47,7 @@ describe('singleLineStringDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -63,6 +66,7 @@ describe('singleLineStringDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.target_version, diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -81,6 +85,7 @@ describe('singleLineStringDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -99,6 +104,7 @@ describe('singleLineStringDiffAlgorithm', () => { expect.objectContaining({ merged_version: mockVersions.current_version, diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NON_SOLVABLE, }) ); @@ -119,7 +125,8 @@ describe('singleLineStringDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.current_version, - diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, conflict: ThreeWayDiffConflict.NONE, }) ); @@ -139,7 +146,8 @@ describe('singleLineStringDiffAlgorithm', () => { has_base_version: false, base_version: undefined, merged_version: mockVersions.target_version, - diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, conflict: ThreeWayDiffConflict.SOLVABLE, }) );