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 @@ -47,10 +47,11 @@ export const getPrebuiltRulesAndTimelinesStatusRoute = (
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
const ctx = await context.resolve(['core', 'alerting']);
const ctx = await context.resolve(['core', 'alerting', 'securitySolution']);
const savedObjectsClient = ctx.core.savedObjects.client;
const rulesClient = await ctx.alerting.getRulesClient();
const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient);
const mlAuthz = ctx.securitySolution.getMlAuthz();

try {
const latestPrebuiltRules = await ruleAssetsClient.fetchLatestAssets();
Expand All @@ -69,8 +70,16 @@ export const getPrebuiltRulesAndTimelinesStatusRoute = (
await getExistingPrepackagedRules({ rulesClient, logger })
);

const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules);
const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules);
const rulesToInstall = await getRulesToInstall(
latestPrebuiltRules,
installedPrebuiltRules,
mlAuthz
);
const rulesToUpdate = await getRulesToUpdate(
latestPrebuiltRules,
installedPrebuiltRules,
mlAuthz
);

const frameworkRequest = await buildFrameworkRequest(context, request);
const prebuiltTimelineStatus = await checkTimelinesStatus(frameworkRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const legacyCreatePrepackagedRules = async (
const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient;
const detectionRulesClient = context.getDetectionRulesClient();
const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient);
const mlAuthz = context.getMlAuthz();

if (!siemClient || !rulesClient) {
throw new PrepackagedRulesError('', 404);
Expand All @@ -63,8 +64,16 @@ export const legacyCreatePrepackagedRules = async (
const installedPrebuiltRules = rulesToMap(
await getExistingPrepackagedRules({ rulesClient, logger })
);
const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules);
const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules);
const rulesToInstall = await getRulesToInstall(
latestPrebuiltRules,
installedPrebuiltRules,
mlAuthz
);
const rulesToUpdate = await getRulesToUpdate(
latestPrebuiltRules,
installedPrebuiltRules,
mlAuthz
);

const ruleCreationResult = await createPrebuiltRules(
detectionRulesClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,40 @@

import { getRulesToInstall } from './get_rules_to_install';
import { getRuleMock } from '../../routes/__mocks__/request_responses';
import { getPrebuiltRuleMock } from '../mocks';
import { getPrebuiltRuleMock, getPrebuiltRuleMockOfType } from '../mocks';
import { getQueryRuleParams } from '../../rule_schema/mocks';
import { rulesToMap } from './utils';
import { buildMlAuthz, buildRestrictedMlAuthz } from '../../../machine_learning/__mocks__/authz';

describe('get_rules_to_install', () => {
test('should return empty array if both rule sets are empty', () => {
const update = getRulesToInstall([], rulesToMap([]));
const mockMlAuthz = buildMlAuthz();

test('should return empty array if both rule sets are empty', async () => {
const update = await getRulesToInstall([], rulesToMap([]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return empty array if the two rule ids match', () => {
test('should return empty array if the two rule ids match', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-1';
const update = getRulesToInstall([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToInstall([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return the rule to install if the id of the two rules do not match', () => {
test('should return the rule to install if the id of the two rules do not match', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-2';
const update = getRulesToInstall([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToInstall([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([ruleAsset]);
});

test('should return two rules to install if both the ids of the two rules do not match', () => {
test('should return two rules to install if both the ids of the two rules do not match', async () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.rule_id = 'rule-1';

Expand All @@ -46,11 +49,15 @@ describe('get_rules_to_install', () => {

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-3';
const update = getRulesToInstall([ruleAsset1, ruleAsset2], rulesToMap([installedRule]));
const update = await getRulesToInstall(
[ruleAsset1, ruleAsset2],
rulesToMap([installedRule]),
mockMlAuthz
);
expect(update).toEqual([ruleAsset1, ruleAsset2]);
});

test('should return two rules of three to install if both the ids of the two rules do not match but the third does', () => {
test('should return two rules of three to install if both the ids of the two rules do not match but the third does', async () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.rule_id = 'rule-1';

Expand All @@ -62,10 +69,27 @@ describe('get_rules_to_install', () => {

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-3';
const update = getRulesToInstall(
const update = await getRulesToInstall(
[ruleAsset1, ruleAsset2, ruleAsset3],
rulesToMap([installedRule])
rulesToMap([installedRule]),
mockMlAuthz
);
expect(update).toEqual([ruleAsset1, ruleAsset2]);
});

test('should exclude license-restricted rules from rules to install', async () => {
const queryRuleAsset = getPrebuiltRuleMock();
queryRuleAsset.rule_id = 'rule-query';

const mlRuleAsset = getPrebuiltRuleMockOfType('machine_learning');

const mlAuthzRestrictingMlRules = buildRestrictedMlAuthz();

const update = await getRulesToInstall(
[queryRuleAsset, mlRuleAsset],
rulesToMap([]),
mlAuthzRestrictingMlRules
);
expect(update).toEqual([queryRuleAsset]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
* 2.0.
*/

import type { MlAuthz } from '../../../machine_learning/authz';
import type { RuleAlertType } from '../../rule_schema';
import type { PrebuiltRuleAsset } from '../model/rule_assets/prebuilt_rule_asset';
import { excludeLicenseRestrictedRules } from './utils';

export const getRulesToInstall = (
export const getRulesToInstall = async (
latestPrebuiltRules: PrebuiltRuleAsset[],
installedRules: Map<string, RuleAlertType>
installedRules: Map<string, RuleAlertType>,
mlAuthz: MlAuthz
) => {
return latestPrebuiltRules.filter((rule) => !installedRules.has(rule.rule_id));
const uninstalledRules = latestPrebuiltRules.filter((rule) => !installedRules.has(rule.rule_id));
return excludeLicenseRestrictedRules(uninstalledRules, mlAuthz);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,56 @@

import { filterInstalledRules, getRulesToUpdate } from './get_rules_to_update';
import { getRuleMock } from '../../routes/__mocks__/request_responses';
import { getPrebuiltRuleMock } from '../mocks';
import { getPrebuiltRuleMock, getPrebuiltRuleMockOfType } from '../mocks';
import { getQueryRuleParams } from '../../rule_schema/mocks';
import { rulesToMap } from './utils';
import { buildRestrictedMlAuthz, buildMlAuthz } from '../../../machine_learning/__mocks__/authz';

describe('get_rules_to_update', () => {
test('should return empty array if both rule sets are empty', () => {
const update = getRulesToUpdate([], rulesToMap([]));
const mockMlAuthz = buildMlAuthz();

test('should return empty array if both rule sets are empty', async () => {
const update = await getRulesToUpdate([], rulesToMap([]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return empty array if the rule_id of the two rules do not match', () => {
test('should return empty array if the rule_id of the two rules do not match', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';
ruleAsset.version = 2;

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-2';
installedRule.params.version = 1;
const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToUpdate([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return empty array if the version of file system rule is less than the installed version', () => {
test('should return empty array if the version of file system rule is less than the installed version', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';
ruleAsset.version = 1;

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-1';
installedRule.params.version = 2;
const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToUpdate([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return empty array if the version of file system rule is the same as the installed version', () => {
test('should return empty array if the version of file system rule is the same as the installed version', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';
ruleAsset.version = 1;

const installedRule = getRuleMock(getQueryRuleParams());
installedRule.params.ruleId = 'rule-1';
installedRule.params.version = 1;
const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToUpdate([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([]);
});

test('should return the rule to update if the version of file system rule is greater than the installed version', () => {
test('should return the rule to update if the version of file system rule is greater than the installed version', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';
ruleAsset.version = 2;
Expand All @@ -63,11 +66,11 @@ describe('get_rules_to_update', () => {
installedRule.params.version = 1;
installedRule.params.exceptionsList = [];

const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule]));
const update = await getRulesToUpdate([ruleAsset], rulesToMap([installedRule]), mockMlAuthz);
expect(update).toEqual([ruleAsset]);
});

test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', () => {
test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', async () => {
const ruleAsset = getPrebuiltRuleMock();
ruleAsset.rule_id = 'rule-1';
ruleAsset.version = 2;
Expand All @@ -82,11 +85,15 @@ describe('get_rules_to_update', () => {
installedRule2.params.version = 1;
installedRule2.params.exceptionsList = [];

const update = getRulesToUpdate([ruleAsset], rulesToMap([installedRule1, installedRule2]));
const update = await getRulesToUpdate(
[ruleAsset],
rulesToMap([installedRule1, installedRule2]),
mockMlAuthz
);
expect(update).toEqual([ruleAsset]);
});

test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', () => {
test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', async () => {
const ruleAsset1 = getPrebuiltRuleMock();
ruleAsset1.rule_id = 'rule-1';
ruleAsset1.version = 2;
Expand All @@ -105,12 +112,41 @@ describe('get_rules_to_update', () => {
installedRule2.params.version = 1;
installedRule2.params.exceptionsList = [];

const update = getRulesToUpdate(
const update = await getRulesToUpdate(
[ruleAsset1, ruleAsset2],
rulesToMap([installedRule1, installedRule2])
rulesToMap([installedRule1, installedRule2]),
mockMlAuthz
);
expect(update).toEqual([ruleAsset1, ruleAsset2]);
});

test('should exclude license-restricted rules from rules to update', async () => {
const queryRuleAsset = getPrebuiltRuleMock();
queryRuleAsset.rule_id = 'rule-query';
queryRuleAsset.version = 2;

const mlRuleAsset = getPrebuiltRuleMockOfType('machine_learning');
mlRuleAsset.version = 2;

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

const installedMlRule = getRuleMock(getQueryRuleParams());
installedMlRule.params.ruleId = mlRuleAsset.rule_id;
installedMlRule.params.version = 1;
installedMlRule.params.exceptionsList = [];

const mlAuthzRestrictingMlRules = buildRestrictedMlAuthz();

const update = await getRulesToUpdate(
[queryRuleAsset, mlRuleAsset],
rulesToMap([installedQueryRule, installedMlRule]),
mlAuthzRestrictingMlRules
);
expect(update).toEqual([queryRuleAsset]);
});
});

describe('filterInstalledRules', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
* 2.0.
*/

import type { MlAuthz } from '../../../machine_learning/authz';
import type { RuleAlertType } from '../../rule_schema';
import type { PrebuiltRuleAsset } from '../model/rule_assets/prebuilt_rule_asset';
import { excludeLicenseRestrictedRules } from './utils';

/**
* Returns the rules to update by doing a compare to the rules from the file system against
Expand All @@ -15,13 +17,15 @@ import type { PrebuiltRuleAsset } from '../model/rule_assets/prebuilt_rule_asset
* @param latestPrebuiltRules The latest rules to check against installed
* @param installedRules The installed rules
*/
export const getRulesToUpdate = (
export const getRulesToUpdate = async (
latestPrebuiltRules: PrebuiltRuleAsset[],
installedRules: Map<string, RuleAlertType>
installedRules: Map<string, RuleAlertType>,
mlAuthz: MlAuthz
) => {
return latestPrebuiltRules.filter((latestRule) =>
const rulesToUpdate = latestPrebuiltRules.filter((latestRule) =>
filterInstalledRules(latestRule, installedRules)
);
return excludeLicenseRestrictedRules(rulesToUpdate, mlAuthz);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,13 @@ export const mlServicesMock = mlPluginServerMock;

const mockValidateRuleType = jest.fn().mockResolvedValue({ valid: true, message: undefined });

const mockValidateInvalidRuleType = jest.fn().mockImplementation(async (type: string) => ({
valid: type !== 'machine_learning',
message: type === 'machine_learning' ? 'ML rules require a platinum license' : undefined,
}));

export const buildMlAuthz = jest.fn().mockReturnValue({ validateRuleType: mockValidateRuleType });

export const buildRestrictedMlAuthz = jest
.fn()
.mockReturnValue({ validateRuleType: mockValidateInvalidRuleType });
Loading