Skip to content
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