Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { filterInstalledRules, getRulesToUpdate, mergeExceptionLists } from './get_rules_to_update';
import { filterInstalledRules, getRulesToUpdate } from './get_rules_to_update';
import { getRuleMock } from '../../routes/__mocks__/request_responses';
import { getPrebuiltRuleMock } from '../mocks';
import { getQueryRuleParams } from '../../rule_schema/mocks';
Expand Down Expand Up @@ -111,207 +111,6 @@ describe('get_rules_to_update', () => {
);
expect(update).toEqual([ruleAsset1, ruleAsset2]);
});

test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [];

const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list);
});

test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'second_exception_list',
list_id: 'some-other-id',
namespace_type: 'single',
type: 'detection',
},
];

const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual([
...ruleAsset1.exceptions_list,
...installedRule1.params.exceptionsList,
]);
});

test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list);
});

test('should not remove an existing exception_list if the rule has an empty exceptions list', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const [update] = getRulesToUpdate([ruleAsset1], rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList);
});

test('should not remove an existing exception_list if the rule has an empty exceptions list for multiple rules', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const ruleAsset2 = getPrebuiltRuleMock();
ruleAsset2.exceptions_list = [];
ruleAsset2.rule_id = 'rule-2';
ruleAsset2.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
const installedRule2 = getRuleMock(getQueryRuleParams());
installedRule2.params.ruleId = 'rule-2';
installedRule2.params.version = 1;
installedRule2.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const [update1, update2] = getRulesToUpdate(
[ruleAsset1, ruleAsset2],
rulesToMap([installedRule1, installedRule2])
);
expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList);
expect(update2.exceptions_list).toEqual(installedRule2.params.exceptionsList);
});

test('should not remove an existing exception_list if the rule has an empty exceptions list for mixed rules', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const ruleAsset2 = getPrebuiltRuleMock();
ruleAsset2.exceptions_list = [];
ruleAsset2.rule_id = 'rule-2';
ruleAsset2.version = 2;
ruleAsset2.exceptions_list = [
{
id: 'second_list',
list_id: 'second_list',
namespace_type: 'single',
type: 'detection',
},
];

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const installedRule2 = getRuleMock(getQueryRuleParams());
installedRule2.params.ruleId = 'rule-2';
installedRule2.params.version = 1;
installedRule2.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const [update1, update2] = getRulesToUpdate(
[ruleAsset1, ruleAsset2],
rulesToMap([installedRule1, installedRule2])
);
expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList);
expect(update2.exceptions_list).toEqual([
...ruleAsset2.exceptions_list,
...installedRule2.params.exceptionsList,
]);
});
});

describe('filterInstalledRules', () => {
Expand Down Expand Up @@ -365,110 +164,3 @@ describe('filterInstalledRules', () => {
expect(shouldUpdate).toEqual(true);
});
});

describe('mergeExceptionLists', () => {
test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [];

const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list);
});

test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'second_exception_list',
list_id: 'some-other-id',
namespace_type: 'single',
type: 'detection',
},
];

const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual([
...ruleAsset1.exceptions_list,
...installedRule1.params.exceptionsList,
]);
});

test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(ruleAsset1.exceptions_list);
});

test('should not remove an existing exception_list if the rule has an empty exceptions list', () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.exceptions_list = [];
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;

const installedRule1 = getRuleMock(getQueryRuleParams());
installedRule1.params.ruleId = 'rule-1';
installedRule1.params.version = 1;
installedRule1.params.exceptionsList = [
{
id: 'endpoint_list',
list_id: 'endpoint_list',
namespace_type: 'agnostic',
type: 'endpoint',
},
];

const update = mergeExceptionLists(ruleAsset1, rulesToMap([installedRule1]));
expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export const getRulesToUpdate = (
latestPrebuiltRules: PrebuiltRuleAsset[],
installedRules: Map<string, RuleAlertType>
) => {
return latestPrebuiltRules
.filter((latestRule) => filterInstalledRules(latestRule, installedRules))
.map((latestRule) => mergeExceptionLists(latestRule, installedRules));
return latestPrebuiltRules.filter((latestRule) =>
filterInstalledRules(latestRule, installedRules)
);
};

/**
Expand All @@ -38,33 +38,3 @@ export const filterInstalledRules = (

return !!installedRule && installedRule.params.version < latestPrebuiltRule.version;
};

/**
* Given a rule from the file system and the set of installed rules this will merge the exception lists
* from the installed rules onto the rules from the file system.
* @param latestPrebuiltRule The latest prepackaged rule version that might have exceptions_lists
* @param installedRules The installed rules which might have user driven exceptions_lists
*/
export const mergeExceptionLists = (
latestPrebuiltRule: PrebuiltRuleAsset,
installedRules: Map<string, RuleAlertType>
): PrebuiltRuleAsset => {
if (latestPrebuiltRule.exceptions_list != null) {
const installedRule = installedRules.get(latestPrebuiltRule.rule_id);

if (installedRule != null && installedRule.params.exceptionsList != null) {
const installedExceptionList = installedRule.params.exceptionsList;
const fileSystemExceptions = latestPrebuiltRule.exceptions_list.filter((potentialDuplicate) =>
installedExceptionList.every((item) => item.list_id !== potentialDuplicate.list_id)
);
return {
...latestPrebuiltRule,
exceptions_list: [...fileSystemExceptions, ...installedRule.params.exceptionsList],
};
} else {
return latestPrebuiltRule;
}
} else {
return latestPrebuiltRule;
}
};
Loading