mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[9.0] [Security Solution] Disallow merging critical rule field values upon rule upgrade when base version is missing (#213757) (#213999)
# Backport This will backport the following commits from `main` to `9.0`: - [[Security Solution] Disallow merging critical rule field values upon rule upgrade when base version is missing #213757](https://github.com/elastic/kibana/pull/213757)
This commit is contained in:
parent
4885f7d354
commit
e9fb83e565
7 changed files with 504 additions and 354 deletions
|
@ -7,7 +7,7 @@
|
|||
|
||||
export { numberDiffAlgorithm } from './number_diff_algorithm';
|
||||
export { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorithm';
|
||||
export { scalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm';
|
||||
export { createScalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm';
|
||||
export { simpleDiffAlgorithm } from './simple_diff_algorithm';
|
||||
export { multiLineStringDiffAlgorithm } from './multi_line_string_diff_algorithm';
|
||||
export { dataSourceDiffAlgorithm } from './data_source_diff_algorithm';
|
||||
|
|
|
@ -12,324 +12,45 @@ import {
|
|||
MissingVersion,
|
||||
ThreeWayDiffConflict,
|
||||
} from '../../../../../../../../common/api/detection_engine';
|
||||
import { scalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm';
|
||||
import {
|
||||
ScalarArrayDiffMissingBaseVersionStrategy,
|
||||
createScalarArrayDiffAlgorithm,
|
||||
} from './scalar_array_diff_algorithm';
|
||||
|
||||
describe('scalarArrayDiffAlgorithm', () => {
|
||||
it('returns current_version as merged output if there is no update - scenario AAA', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
describe.each([
|
||||
[ScalarArrayDiffMissingBaseVersionStrategy.Merge],
|
||||
[ScalarArrayDiffMissingBaseVersionStrategy.UseTarget],
|
||||
])('with missingBaseCanUpdateMergeStrategy = %s', (mergeStrategy) => {
|
||||
const scalarArrayDiffAlgorithm = createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: mergeStrategy,
|
||||
});
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns current_version as merged output if current_version is different and there is no update - scenario ABA', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'four'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns target_version as merged output if current_version is the same and there is an update - scenario AAB', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns current_version as merged output if current version is different but it matches the update - scenario ABB', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'four'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns custom merged version as merged output if all three versions are different - scenario ABC', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['two', 'three', 'four', 'five'],
|
||||
target_version: ['one', 'three', 'four', 'six'],
|
||||
};
|
||||
const expectedMergedVersion = ['three', 'four', 'five', 'six'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('if base_version is missing', () => {
|
||||
describe('returns target_version as merged output if current_version and target_version are the same - scenario -AA', () => {
|
||||
it('returns NONE conflict if rule is not customized', () => {
|
||||
describe('base cases', () => {
|
||||
it('returns current_version as merged output if there is no update - scenario AAA', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns NONE conflict if rule is customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, true);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if current_version and target_version are different - scenario -AB', () => {
|
||||
it('returns target_version as merged output and NONE conflict if rule is not customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns merged version of current and target as merged output if rule is customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const expectedMergedVersion = ['one', 'two', 'three', 'four'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, true);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('compares arrays agnostic of order', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'two'],
|
||||
target_version: ['three', 'one', 'two'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('compares arrays deduplicated', () => {
|
||||
it('when values duplicated in base version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in current version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in target version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in all versions', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'two'],
|
||||
current_version: ['two', 'two', 'three'],
|
||||
target_version: ['one', 'one', 'three', 'three'],
|
||||
};
|
||||
const expectedMergedVersion = ['three'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('compares empty arrays', () => {
|
||||
it('when base version is empty', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: [],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when current version is empty', () => {
|
||||
it('returns current_version as merged output if current_version is different and there is no update - scenario ABA', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: [],
|
||||
target_version: ['one', 'two'],
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'four'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
@ -344,11 +65,11 @@ describe('scalarArrayDiffAlgorithm', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('when target version is empty', () => {
|
||||
it('returns target_version as merged output if current_version is the same and there is an update - scenario AAB', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: [],
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
@ -363,24 +84,348 @@ describe('scalarArrayDiffAlgorithm', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('when all versions are empty', () => {
|
||||
it('returns current_version as merged output if current version is different but it matches the update - scenario ABB', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: [],
|
||||
current_version: [],
|
||||
target_version: [],
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'four'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: [],
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns custom merged version as merged output if all three versions are different - scenario ABC', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['two', 'three', 'four', 'five'],
|
||||
target_version: ['one', 'three', 'four', 'six'],
|
||||
};
|
||||
const expectedMergedVersion = ['three', 'four', 'five', 'six'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('compares arrays agnostic of order', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'three'],
|
||||
current_version: ['one', 'three', 'two'],
|
||||
target_version: ['three', 'one', 'two'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('compares arrays deduplicated', () => {
|
||||
it('when values duplicated in base version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in current version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in target version', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two', 'two'],
|
||||
};
|
||||
const expectedMergedVersion = ['one', 'two'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when values are duplicated in all versions', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two', 'two'],
|
||||
current_version: ['two', 'two', 'three'],
|
||||
target_version: ['one', 'one', 'three', 'three'],
|
||||
};
|
||||
const expectedMergedVersion = ['three'];
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('compares empty arrays', () => {
|
||||
it('when base version is empty', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: [],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when current version is empty', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: [],
|
||||
target_version: ['one', 'two'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.current_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when target version is empty', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: ['one', 'two'],
|
||||
current_version: ['one', 'two'],
|
||||
target_version: [],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('when all versions are empty', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: [],
|
||||
current_version: [],
|
||||
target_version: [],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
merged_version: [],
|
||||
diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Current,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and base_version is missing', () => {
|
||||
describe('returns target_version as merged output if current_version and target_version are the same - scenario -AA', () => {
|
||||
it('returns NONE conflict if rule is not customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns NONE conflict if rule is customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'two', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, true);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if current_version and target_version are different - scenario -AB', () => {
|
||||
it('returns target_version as merged output and NONE conflict if rule is not customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, false);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: mockVersions.target_version,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when base_version is missing', () => {
|
||||
it('returns merged version of current and target as merged output if rule is customized', () => {
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: ['one', 'four', 'three'],
|
||||
};
|
||||
|
||||
const expectedMergedVersion = ['one', 'two', 'three', 'four'];
|
||||
|
||||
const scalarArrayDiffAlgorithm = createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.Merge,
|
||||
});
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, true);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: expectedMergedVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns target version of current and target as merged output if rule is customized', () => {
|
||||
const targetVersion = ['one', 'four', 'three'];
|
||||
|
||||
const mockVersions: ThreeVersionsOf<string[]> = {
|
||||
base_version: MissingVersion,
|
||||
current_version: ['one', 'two', 'three'],
|
||||
target_version: targetVersion,
|
||||
};
|
||||
|
||||
const scalarArrayDiffAlgorithm = createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.UseTarget,
|
||||
});
|
||||
const result = scalarArrayDiffAlgorithm(mockVersions, true);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
has_base_version: false,
|
||||
base_version: undefined,
|
||||
merged_version: targetVersion,
|
||||
diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
merge_outcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,47 +21,90 @@ import {
|
|||
} from '../../../../../../../../common/api/detection_engine/prebuilt_rules';
|
||||
import { mergeDedupedArrays } from './helpers';
|
||||
|
||||
type ScalarArrayDiffAlgorithm<TValue> = (
|
||||
versions: ThreeVersionsOf<TValue[]>,
|
||||
isRuleCustomized: boolean
|
||||
) => ThreeWayDiff<TValue[]>;
|
||||
|
||||
/**
|
||||
* This strategy applies when all these conditions are met:
|
||||
* 1) when the base version is missing;
|
||||
* 2) and there is an update from Elastic (current version != target version);
|
||||
* 3) and the rule IS marked as customized.
|
||||
*
|
||||
* When all that is true, the scalar array diff algorithm uses this strategy
|
||||
* to determine what to do, exactly.
|
||||
*/
|
||||
export enum ScalarArrayDiffMissingBaseVersionStrategy {
|
||||
/**
|
||||
* Merge the current and target versions and return the result as the merged version.
|
||||
*/
|
||||
Merge = 'Merge',
|
||||
|
||||
/**
|
||||
* Return the target version as the merged version.
|
||||
*/
|
||||
UseTarget = 'UseTarget',
|
||||
}
|
||||
|
||||
interface ScalarArrayDiffAlgorithmOptions {
|
||||
/**
|
||||
* Algorithm's behavior when the base version is missing and current field's
|
||||
* value differs from the target value.
|
||||
*/
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff algorithm used for arrays of scalar values (eg. numbers, strings, booleans, etc.)
|
||||
*
|
||||
* NOTE: Diffing logic will be agnostic to array order
|
||||
*/
|
||||
export const scalarArrayDiffAlgorithm = <TValue>(
|
||||
versions: ThreeVersionsOf<TValue[]>,
|
||||
isRuleCustomized: boolean
|
||||
): ThreeWayDiff<TValue[]> => {
|
||||
const {
|
||||
base_version: baseVersion,
|
||||
current_version: currentVersion,
|
||||
target_version: targetVersion,
|
||||
} = versions;
|
||||
export function createScalarArrayDiffAlgorithm<TValue>(
|
||||
options: ScalarArrayDiffAlgorithmOptions
|
||||
): ScalarArrayDiffAlgorithm<TValue> {
|
||||
return function scalarArrayDiffAlgorithm(
|
||||
versions: ThreeVersionsOf<TValue[]>,
|
||||
isRuleCustomized: boolean
|
||||
) {
|
||||
const {
|
||||
base_version: baseVersion,
|
||||
current_version: currentVersion,
|
||||
target_version: targetVersion,
|
||||
} = versions;
|
||||
|
||||
const diffOutcome = determineOrderAgnosticDiffOutcome(baseVersion, currentVersion, targetVersion);
|
||||
const valueCanUpdate = determineIfValueCanUpdate(diffOutcome);
|
||||
const diffOutcome = determineOrderAgnosticDiffOutcome(
|
||||
baseVersion,
|
||||
currentVersion,
|
||||
targetVersion
|
||||
);
|
||||
const valueCanUpdate = determineIfValueCanUpdate(diffOutcome);
|
||||
|
||||
const hasBaseVersion = baseVersion !== MissingVersion;
|
||||
const hasBaseVersion = baseVersion !== MissingVersion;
|
||||
|
||||
const { mergeOutcome, conflict, mergedVersion } = mergeVersions({
|
||||
baseVersion: hasBaseVersion ? baseVersion : undefined,
|
||||
currentVersion,
|
||||
targetVersion,
|
||||
diffOutcome,
|
||||
isRuleCustomized,
|
||||
});
|
||||
const { mergeOutcome, conflict, mergedVersion } = mergeVersions({
|
||||
baseVersion: hasBaseVersion ? baseVersion : undefined,
|
||||
currentVersion,
|
||||
targetVersion,
|
||||
diffOutcome,
|
||||
isRuleCustomized,
|
||||
options,
|
||||
});
|
||||
|
||||
return {
|
||||
has_base_version: hasBaseVersion,
|
||||
base_version: hasBaseVersion ? baseVersion : undefined,
|
||||
current_version: currentVersion,
|
||||
target_version: targetVersion,
|
||||
merged_version: mergedVersion,
|
||||
merge_outcome: mergeOutcome,
|
||||
return {
|
||||
has_base_version: hasBaseVersion,
|
||||
base_version: hasBaseVersion ? baseVersion : undefined,
|
||||
current_version: currentVersion,
|
||||
target_version: targetVersion,
|
||||
merged_version: mergedVersion,
|
||||
merge_outcome: mergeOutcome,
|
||||
|
||||
diff_outcome: diffOutcome,
|
||||
conflict,
|
||||
has_update: valueCanUpdate,
|
||||
diff_outcome: diffOutcome,
|
||||
conflict,
|
||||
has_update: valueCanUpdate,
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface MergeResult<TValue> {
|
||||
mergeOutcome: ThreeWayMergeOutcome;
|
||||
|
@ -75,6 +118,7 @@ interface MergeArgs<TValue> {
|
|||
targetVersion: TValue[];
|
||||
diffOutcome: ThreeWayDiffOutcome;
|
||||
isRuleCustomized: boolean;
|
||||
options: ScalarArrayDiffAlgorithmOptions;
|
||||
}
|
||||
|
||||
const mergeVersions = <TValue>({
|
||||
|
@ -83,6 +127,7 @@ const mergeVersions = <TValue>({
|
|||
targetVersion,
|
||||
diffOutcome,
|
||||
isRuleCustomized,
|
||||
options,
|
||||
}: MergeArgs<TValue>): MergeResult<TValue> => {
|
||||
const dedupedBaseVersion = uniq(baseVersion);
|
||||
const dedupedCurrentVersion = uniq(currentVersion);
|
||||
|
@ -135,17 +180,35 @@ const mergeVersions = <TValue>({
|
|||
// Otherwise we treat scenario -AB as AAB
|
||||
// https://github.com/elastic/kibana/issues/210358#issuecomment-2654492854
|
||||
case ThreeWayDiffOutcome.MissingBaseCanUpdate: {
|
||||
return isRuleCustomized
|
||||
? {
|
||||
if (!isRuleCustomized) {
|
||||
return {
|
||||
mergedVersion: targetVersion,
|
||||
mergeOutcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
};
|
||||
}
|
||||
|
||||
switch (options.missingBaseVersionStrategy) {
|
||||
case ScalarArrayDiffMissingBaseVersionStrategy.Merge: {
|
||||
return {
|
||||
mergedVersion: union(dedupedCurrentVersion, dedupedTargetVersion),
|
||||
mergeOutcome: ThreeWayMergeOutcome.Merged,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
}
|
||||
: {
|
||||
};
|
||||
}
|
||||
|
||||
case ScalarArrayDiffMissingBaseVersionStrategy.UseTarget: {
|
||||
return {
|
||||
mergedVersion: targetVersion,
|
||||
mergeOutcome: ThreeWayMergeOutcome.Target,
|
||||
conflict: ThreeWayDiffConflict.NONE,
|
||||
conflict: ThreeWayDiffConflict.SOLVABLE,
|
||||
};
|
||||
}
|
||||
|
||||
default: {
|
||||
return assertUnreachable(options.missingBaseVersionStrategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return assertUnreachable(diffOutcome);
|
||||
|
|
|
@ -41,7 +41,6 @@ import {
|
|||
dataSourceDiffAlgorithm,
|
||||
multiLineStringDiffAlgorithm,
|
||||
numberDiffAlgorithm,
|
||||
scalarArrayDiffAlgorithm,
|
||||
simpleDiffAlgorithm,
|
||||
singleLineStringDiffAlgorithm,
|
||||
kqlQueryDiffAlgorithm,
|
||||
|
@ -50,6 +49,10 @@ import {
|
|||
ruleTypeDiffAlgorithm,
|
||||
forceTargetVersionDiffAlgorithm,
|
||||
} from './algorithms';
|
||||
import {
|
||||
ScalarArrayDiffMissingBaseVersionStrategy,
|
||||
createScalarArrayDiffAlgorithm,
|
||||
} from './algorithms/scalar_array_diff_algorithm';
|
||||
|
||||
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`;
|
||||
|
@ -215,13 +218,17 @@ const commonFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor<DiffableCommonFields>
|
|||
*/
|
||||
version: forceTargetVersionDiffAlgorithm,
|
||||
name: singleLineStringDiffAlgorithm,
|
||||
tags: scalarArrayDiffAlgorithm,
|
||||
tags: createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.Merge,
|
||||
}),
|
||||
description: multiLineStringDiffAlgorithm,
|
||||
severity: singleLineStringDiffAlgorithm,
|
||||
severity_mapping: simpleDiffAlgorithm,
|
||||
risk_score: numberDiffAlgorithm,
|
||||
risk_score_mapping: simpleDiffAlgorithm,
|
||||
references: scalarArrayDiffAlgorithm,
|
||||
references: createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.Merge,
|
||||
}),
|
||||
false_positives: simpleDiffAlgorithm,
|
||||
threat: simpleDiffAlgorithm,
|
||||
note: multiLineStringDiffAlgorithm,
|
||||
|
@ -304,7 +311,9 @@ const threatMatchFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor<DiffableThreatMat
|
|||
kql_query: kqlQueryDiffAlgorithm,
|
||||
data_source: dataSourceDiffAlgorithm,
|
||||
threat_query: kqlQueryDiffAlgorithm,
|
||||
threat_index: scalarArrayDiffAlgorithm,
|
||||
threat_index: createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.UseTarget,
|
||||
}),
|
||||
threat_mapping: simpleDiffAlgorithm,
|
||||
threat_indicator_path: singleLineStringDiffAlgorithm,
|
||||
alert_suppression: simpleDiffAlgorithm,
|
||||
|
@ -355,7 +364,9 @@ const newTermsFieldsDiffAlgorithms: FieldsDiffAlgorithmsFor<DiffableNewTermsFiel
|
|||
type: ruleTypeDiffAlgorithm,
|
||||
kql_query: kqlQueryDiffAlgorithm,
|
||||
data_source: dataSourceDiffAlgorithm,
|
||||
new_terms_fields: scalarArrayDiffAlgorithm,
|
||||
new_terms_fields: createScalarArrayDiffAlgorithm({
|
||||
missingBaseVersionStrategy: ScalarArrayDiffMissingBaseVersionStrategy.UseTarget,
|
||||
}),
|
||||
history_window_start: singleLineStringDiffAlgorithm,
|
||||
alert_suppression: simpleDiffAlgorithm,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,9 @@ import { isUndefined, omitBy } from 'lodash';
|
|||
import type {
|
||||
PartialRuleDiff,
|
||||
RuleResponse,
|
||||
UpgradeConflictResolution,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { UpgradeConflictResolutionEnum } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ModeEnum } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import {
|
||||
ThreeWayDiffConflict,
|
||||
|
@ -170,6 +172,7 @@ export function testFieldUpgradeReview(
|
|||
|
||||
interface TestFieldUpgradesToMergedValueParams {
|
||||
ruleUpgradeAssets: TestFieldRuleUpgradeAssets;
|
||||
onConflict?: UpgradeConflictResolution;
|
||||
diffableRuleFieldName: string;
|
||||
expectedFieldsAfterUpgrade: Partial<RuleResponse>;
|
||||
}
|
||||
|
@ -187,6 +190,7 @@ interface TestFieldUpgradesToMergedValueParams {
|
|||
export function testFieldUpgradesToMergedValue(
|
||||
{
|
||||
ruleUpgradeAssets,
|
||||
onConflict = UpgradeConflictResolutionEnum.SKIP,
|
||||
diffableRuleFieldName,
|
||||
expectedFieldsAfterUpgrade,
|
||||
}: TestFieldUpgradesToMergedValueParams,
|
||||
|
@ -215,6 +219,7 @@ export function testFieldUpgradesToMergedValue(
|
|||
|
||||
const response = await performUpgradePrebuiltRules(es, supertest, {
|
||||
mode: ModeEnum.SPECIFIC_RULES,
|
||||
on_conflict: onConflict,
|
||||
rules: [
|
||||
{
|
||||
rule_id: ruleUpgradeAssets.upgrade.rule_id ?? DEFAULT_TEST_RULE_ID,
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ThreeWayDiffOutcome } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import {
|
||||
ThreeWayDiffOutcome,
|
||||
UpgradeConflictResolutionEnum,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { FtrProviderContext } from '../../../../../../../../ftr_provider_context';
|
||||
import type { TestFieldRuleUpgradeAssets } from '../test_helpers';
|
||||
import {
|
||||
|
@ -302,16 +305,26 @@ export function newTermsFieldsField({ getService }: FtrProviderContext): void {
|
|||
ruleUpgradeAssets,
|
||||
diffableRuleFieldName: 'new_terms_fields',
|
||||
expectedDiffOutcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
isMergableField: true,
|
||||
isMergableField: false,
|
||||
expectedFieldDiffValues: {
|
||||
current: ['fieldB'],
|
||||
target: ['fieldA', 'fieldC'],
|
||||
merged: ['fieldB', 'fieldA', 'fieldC'],
|
||||
merged: ['fieldA', 'fieldC'],
|
||||
},
|
||||
},
|
||||
getService
|
||||
);
|
||||
|
||||
testFieldUpgradesToMergedValue(
|
||||
{
|
||||
ruleUpgradeAssets,
|
||||
onConflict: UpgradeConflictResolutionEnum.UPGRADE_SOLVABLE,
|
||||
diffableRuleFieldName: 'new_terms_fields',
|
||||
expectedFieldsAfterUpgrade: { new_terms_fields: ['fieldA', 'fieldC'] },
|
||||
},
|
||||
getService
|
||||
);
|
||||
|
||||
testFieldUpgradesToResolvedValue(
|
||||
{
|
||||
ruleUpgradeAssets,
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ThreeWayDiffOutcome } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import {
|
||||
ThreeWayDiffOutcome,
|
||||
UpgradeConflictResolutionEnum,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { FtrProviderContext } from '../../../../../../../../ftr_provider_context';
|
||||
import type { TestFieldRuleUpgradeAssets } from '../test_helpers';
|
||||
import {
|
||||
|
@ -302,16 +305,26 @@ export function threatIndexField({ getService }: FtrProviderContext): void {
|
|||
ruleUpgradeAssets,
|
||||
diffableRuleFieldName: 'threat_index',
|
||||
expectedDiffOutcome: ThreeWayDiffOutcome.MissingBaseCanUpdate,
|
||||
isMergableField: true,
|
||||
isMergableField: false,
|
||||
expectedFieldDiffValues: {
|
||||
current: ['indexD'],
|
||||
target: ['indexB', 'indexC'],
|
||||
merged: ['indexD', 'indexB', 'indexC'],
|
||||
merged: ['indexB', 'indexC'],
|
||||
},
|
||||
},
|
||||
getService
|
||||
);
|
||||
|
||||
testFieldUpgradesToMergedValue(
|
||||
{
|
||||
ruleUpgradeAssets,
|
||||
onConflict: UpgradeConflictResolutionEnum.UPGRADE_SOLVABLE,
|
||||
diffableRuleFieldName: 'threat_index',
|
||||
expectedFieldsAfterUpgrade: { threat_index: ['indexB', 'indexC'] },
|
||||
},
|
||||
getService
|
||||
);
|
||||
|
||||
testFieldUpgradesToResolvedValue(
|
||||
{
|
||||
ruleUpgradeAssets,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue