Skip to content

Commit

Permalink
[Security Solution][Detections] refactors update rule actions tests (e…
Browse files Browse the repository at this point in the history
…lastic#142464)

## Summary

- addresses elastic#138757

according to proposal in above task:
- removes step of updating immutable rule with mock data
- makes assertions whether rule properties were not modified against fetched earlier immutable rule


### Checklist

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
  • Loading branch information
vitaliidm authored and WafaaNasr committed Oct 14, 2022
1 parent 8954b2e commit becb969
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import expect from '@kbn/expect';
import { omit } from 'lodash';

import { CreateRulesSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/request';
import { FtrProviderContext } from '../../common/ftr_provider_context';
Expand All @@ -26,14 +27,24 @@ import {
findImmutableRuleById,
getPrePackagedRulesStatus,
getSimpleRuleOutput,
ruleToUpdateSchema,
} from '../../utils';

// Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file:
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security.json
const RULE_ID = '9a1a2dae-0b5f-4c3d-8305-a268d404c306';

// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const log = getService('log');

const getImmutableRule = async () => {
await installPrePackagedRules(supertest, log);
return getRule(supertest, log, RULE_ID);
};

describe('update_actions', () => {
describe('updating actions', () => {
before(async () => {
Expand Down Expand Up @@ -105,70 +116,69 @@ export default ({ getService }: FtrProviderContext) => {
});

it('should not change properties of immutable rule when applying actions to it', async () => {
await installPrePackagedRules(supertest, log);
// Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file:
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
const immutableRule = await getRule(supertest, log, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
// actions and throttle to be removed from assertion (it asserted in a separate test case)
const actionsProps = ['actions', 'throttle'];

const immutableRule = await getImmutableRule();
const hookAction = await createNewAction(supertest, log);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, newRuleToUpdate);
const ruleToUpdate = getRuleWithWebHookAction(
hookAction.id,
immutableRule.enabled,
ruleToUpdateSchema(immutableRule)
);
const updatedRule = await updateRule(supertest, log, ruleToUpdate);
const bodyToCompare = removeServerGeneratedProperties(updatedRule);
const expected = omit(removeServerGeneratedProperties(updatedRule), actionsProps);

const expected = {
...getSimpleRuleOutputWithWebHookAction(`${bodyToCompare.actions?.[0].id}`),
rule_id: immutableRule.rule_id, // Rule id should match the same as the immutable rule
version: immutableRule.version, // This version number should not change when an immutable rule is updated
immutable: true, // It should stay immutable true when returning
required_fields: immutableRule.required_fields, // required_fields cannot be modified, so newRuleToUpdate will have required_fields from immutable rule
};
expect(bodyToCompare).to.eql(expected);
const immutableRuleToAssert = omit(
removeServerGeneratedProperties(immutableRule),
actionsProps
);

expect(immutableRuleToAssert).to.eql(expected);
expect(expected.immutable).to.be(true); // It should stay immutable true when returning
});

it('should be able to create a new webhook action and attach it to an immutable rule', async () => {
await installPrePackagedRules(supertest, log);
// Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file:
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
const immutableRule = await getRule(supertest, log, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
const immutableRule = await getImmutableRule();
const hookAction = await createNewAction(supertest, log);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, newRuleToUpdate);
const ruleToUpdate = getRuleWithWebHookAction(
hookAction.id,
immutableRule.enabled,
ruleToUpdateSchema(immutableRule)
);
const updatedRule = await updateRule(supertest, log, ruleToUpdate);
const bodyToCompare = removeServerGeneratedProperties(updatedRule);

const expected = getSimpleRuleOutputWithWebHookAction(`${bodyToCompare.actions?.[0].id}`);

expect(bodyToCompare.actions).to.eql(expected.actions);
expect(bodyToCompare.throttle).to.eql(expected.throttle);
});

it('should be able to create a new webhook action, attach it to an immutable rule and the count of prepackaged rules should not increase. If this fails, suspect the immutable tags are not staying on the rule correctly.', async () => {
await installPrePackagedRules(supertest, log);
// Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file:
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
const immutableRule = await getRule(supertest, log, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
const immutableRule = await getImmutableRule();
const hookAction = await createNewAction(supertest, log);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, newRuleToUpdate);
const ruleToUpdate = getRuleWithWebHookAction(
hookAction.id,
immutableRule.enabled,
ruleToUpdateSchema(immutableRule)
);
await updateRule(supertest, log, ruleToUpdate);

const status = await getPrePackagedRulesStatus(supertest, log);
expect(status.rules_not_installed).to.eql(0);
});

it('should be able to create a new webhook action, attach it to an immutable rule and the rule should stay immutable when searching against immutable tags', async () => {
await installPrePackagedRules(supertest, log);
// Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file:
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
const immutableRule = await getRule(supertest, log, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
const immutableRule = await getImmutableRule();
const hookAction = await createNewAction(supertest, log);
const newRuleToUpdate = getSimpleRule(immutableRule.rule_id);
const ruleToUpdate = getRuleWithWebHookAction(hookAction.id, false, newRuleToUpdate);
await updateRule(supertest, log, ruleToUpdate);
const body = await findImmutableRuleById(
supertest,
log,
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
const ruleToUpdate = getRuleWithWebHookAction(
hookAction.id,
immutableRule.enabled,
ruleToUpdateSchema(immutableRule)
);
await updateRule(supertest, log, ruleToUpdate);
const body = await findImmutableRuleById(supertest, log, RULE_ID);

expect(body.data.length).to.eql(1); // should have only one length to the data set, otherwise we have duplicates or the tags were removed and that is incredibly bad.
const bodyToCompare = removeServerGeneratedProperties(body.data[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export * from './remove_server_generated_properties';
export * from './remove_server_generated_properties_including_rule_id';
export * from './resolve_simple_rule_output';
export * from './rule_to_ndjson';
export * from './rule_to_update_schema';
export * from './set_signal_status';
export * from './start_signals_migration';
export * from './update_rule';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.
*/

import type {
FullResponseSchema,
UpdateRulesSchema,
} from '@kbn/security-solution-plugin/common/detection_engine/schemas/request';
import { omit, pickBy } from 'lodash';

const propertiesToRemove = [
'id',
'immutable',
'updated_at',
'updated_by',
'created_at',
'created_by',
'related_integrations',
'required_fields',
'setup',
'execution_summary',
];

/**
* transforms FullResponseSchema rule to UpdateRulesSchema
* returned result can be used in rule update API calls
*/
export const ruleToUpdateSchema = (rule: FullResponseSchema): UpdateRulesSchema => {
const removedProperties = omit(rule, propertiesToRemove);

// We're only removing undefined values, so this cast correctly narrows the type
return pickBy(removedProperties, (value) => value !== undefined) as UpdateRulesSchema;
};

0 comments on commit becb969

Please sign in to comment.