[Security Solutions] Adds e2e tests for the legacy notification system (#116531)

## Summary

Adds e2e tests for the legacy notification system for:
* Exporting rules
* Reading rules
* Finding rules

Also adds missing e2e tests for the non-legacy actions where they previously did not have e2e tests.

These tests ensure that the legacy notifications system will run for a while.

- [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
This commit is contained in:
Frank Hassanabad 2021-10-29 14:02:52 -06:00 committed by GitHub
parent 19f4b6801f
commit 023d668e13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 599 additions and 0 deletions

View file

@ -217,6 +217,304 @@ export default ({ getService }: FtrProviderContext): void => {
expect(firstRule).to.eql(outputRule1);
expect(secondRule).to.eql(outputRule2);
});
/**
* Tests the legacy actions to ensure we can export legacy notifications
* @deprecated Once the legacy notification system is removed, remove this test too.
*/
describe('legacy_notification_system', () => {
it('should be able to export 1 legacy action on 1 rule', async () => {
// create an action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create a rule without actions
const rule = await createRule(supertest, getSimpleRule('rule-1'));
// attach the legacy notification
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${rule.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction.actionTypeId,
},
],
})
.expect(200);
// export the rule
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
.set('kbn-xsrf', 'true')
.send()
.expect(200)
.parse(binaryToString);
const outputRule1: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput('rule-1'),
actions: [
{
group: 'default',
id: hookAction.id,
action_type_id: hookAction.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
],
throttle: '1h',
};
const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]);
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
expect(firstRule).to.eql(outputRule1);
});
it('should be able to export 2 legacy actions on 1 rule', async () => {
// create 1st action/connector
const { body: hookAction1 } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create 2nd action/connector
const { body: hookAction2 } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create a rule without actions
const rule = await createRule(supertest, getSimpleRule('rule-1'));
// attach the legacy notification with actions
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${rule.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction1.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction1.actionTypeId,
},
{
id: hookAction2.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction2.actionTypeId,
},
],
})
.expect(200);
// export the rule
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
.set('kbn-xsrf', 'true')
.send()
.expect(200)
.parse(binaryToString);
const outputRule1: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput('rule-1'),
actions: [
{
group: 'default',
id: hookAction1.id,
action_type_id: hookAction1.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
{
group: 'default',
id: hookAction2.id,
action_type_id: hookAction2.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
],
throttle: '1h',
};
const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]);
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
expect(firstRule).to.eql(outputRule1);
});
it('should be able to export 2 legacy actions on 2 rules', async () => {
// create 1st action/connector
const { body: hookAction1 } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create 2nd action/connector
const { body: hookAction2 } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create 2 rules without actions
const rule1 = await createRule(supertest, getSimpleRule('rule-1'));
const rule2 = await createRule(supertest, getSimpleRule('rule-2'));
// attach the legacy notification with actions to the first rule
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${rule1.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction1.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction1.actionTypeId,
},
{
id: hookAction2.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction2.actionTypeId,
},
],
})
.expect(200);
// attach the legacy notification with actions to the 2nd rule
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${rule2.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction1.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction1.actionTypeId,
},
{
id: hookAction2.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction2.actionTypeId,
},
],
})
.expect(200);
// export the rule
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
.set('kbn-xsrf', 'true')
.send()
.expect(200)
.parse(binaryToString);
const outputRule1: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput('rule-1'),
actions: [
{
group: 'default',
id: hookAction1.id,
action_type_id: hookAction1.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
{
group: 'default',
id: hookAction2.id,
action_type_id: hookAction2.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
],
throttle: '1h',
};
const outputRule2: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput('rule-2'),
actions: [
{
group: 'default',
id: hookAction1.id,
action_type_id: hookAction1.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
{
group: 'default',
id: hookAction2.id,
action_type_id: hookAction2.actionTypeId,
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
},
],
throttle: '1h',
};
const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]);
const secondRuleParsed = JSON.parse(body.toString().split(/\n/)[1]);
const firstRule = removeServerGeneratedProperties(firstRuleParsed);
const secondRule = removeServerGeneratedProperties(secondRuleParsed);
expect(firstRule).to.eql(outputRule2);
expect(secondRule).to.eql(outputRule1);
});
});
});
});
};

View file

@ -18,6 +18,7 @@ import {
getComplexRuleOutput,
getSimpleRule,
getSimpleRuleOutput,
getWebHookAction,
removeServerGeneratedProperties,
} from '../../utils';
@ -92,5 +93,164 @@ export default ({ getService }: FtrProviderContext): void => {
total: 1,
});
});
it('should find a single rule with a execute immediately action correctly', async () => {
// create connector/action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
const action = {
group: 'default',
id: hookAction.id,
action_type_id: hookAction.actionTypeId,
params: {},
};
// create rule with connector/action
const rule: ReturnType<typeof getSimpleRule> = {
...getSimpleRule('rule-1'),
actions: [action],
};
await createRule(supertest, rule);
// query the single rule from _find
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}/_find`)
.set('kbn-xsrf', 'true')
.send()
.expect(200);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [action],
throttle: 'rule',
};
body.data = [removeServerGeneratedProperties(body.data[0])];
expect(body).to.eql({
data: [ruleWithActions],
page: 1,
perPage: 20,
total: 1,
});
});
it('should be able to find a scheduled action correctly', async () => {
// create connector/action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
const action = {
group: 'default',
id: hookAction.id,
action_type_id: hookAction.actionTypeId,
params: {},
};
// create rule with connector/action
const rule: ReturnType<typeof getSimpleRule> = {
...getSimpleRule('rule-1'),
throttle: '1h', // <-- throttle makes this a scheduled action
actions: [action],
};
await createRule(supertest, rule);
// query the single rule from _find
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}/_find`)
.set('kbn-xsrf', 'true')
.send()
.expect(200);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [action],
throttle: '1h', // <-- throttle makes this a scheduled action
};
body.data = [removeServerGeneratedProperties(body.data[0])];
expect(body).to.eql({
data: [ruleWithActions],
page: 1,
perPage: 20,
total: 1,
});
});
/**
* Tests the legacy actions to ensure we can export legacy notifications
* @deprecated Once the legacy notification system is removed, remove this test too.
*/
describe('legacy_notification_system', async () => {
it('should be able to a read a scheduled action correctly', async () => {
// create an connector/action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create a rule without actions
const createRuleBody = await createRule(supertest, getSimpleRule('rule-1'));
// attach the legacy notification
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${createRuleBody.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction.actionTypeId,
},
],
})
.expect(200);
// query the single rule from _find
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}/_find`)
.set('kbn-xsrf', 'true')
.send()
.expect(200);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [
{
id: hookAction.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
action_type_id: hookAction.actionTypeId,
},
],
throttle: '1h',
};
body.data = [removeServerGeneratedProperties(body.data[0])];
expect(body).to.eql({
data: [ruleWithActions],
page: 1,
perPage: 20,
total: 1,
});
});
});
});
};

View file

@ -18,6 +18,7 @@ import {
getSimpleRuleOutput,
getSimpleRuleOutputWithoutRuleId,
getSimpleRuleWithoutRuleId,
getWebHookAction,
removeServerGeneratedProperties,
removeServerGeneratedPropertiesIncludingRuleId,
} from '../../utils';
@ -101,6 +102,146 @@ export default ({ getService }: FtrProviderContext) => {
message: 'rule_id: "fake_id" not found',
});
});
it('should be able to a read a execute immediately action correctly', async () => {
// create connector/action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
const action = {
group: 'default',
id: hookAction.id,
action_type_id: hookAction.actionTypeId,
params: {},
};
// create rule with connector/action
const rule: ReturnType<typeof getSimpleRule> = {
...getSimpleRule('rule-1'),
actions: [action],
};
const createRuleBody = await createRule(supertest, rule);
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}?id=${createRuleBody.id}`)
.set('kbn-xsrf', 'true')
.send(getSimpleRule())
.expect(200);
const bodyToCompare = removeServerGeneratedProperties(body);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [action],
throttle: 'rule',
};
expect(bodyToCompare).to.eql(ruleWithActions);
});
it('should be able to a read a scheduled action correctly', async () => {
// create connector/action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
const action = {
group: 'default',
id: hookAction.id,
action_type_id: hookAction.actionTypeId,
params: {},
};
// create rule with connector/action
const rule: ReturnType<typeof getSimpleRule> = {
...getSimpleRule('rule-1'),
throttle: '1h', // <-- throttle makes this a scheduled action
actions: [action],
};
const createRuleBody = await createRule(supertest, rule);
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}?id=${createRuleBody.id}`)
.set('kbn-xsrf', 'true')
.send(getSimpleRule())
.expect(200);
const bodyToCompare = removeServerGeneratedProperties(body);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [action],
throttle: '1h', // <-- throttle makes this a scheduled action
};
expect(bodyToCompare).to.eql(ruleWithActions);
});
/**
* Tests the legacy actions to ensure we can export legacy notifications
* @deprecated Once the legacy notification system is removed, remove this test too.
*/
describe('legacy_notification_system', () => {
it('should be able to a read a scheduled action correctly', async () => {
// create an action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);
// create a rule without actions
const createRuleBody = await createRule(supertest, getSimpleRule('rule-1'));
// attach the legacy notification
await supertest
.post(`/internal/api/detection/legacy/notifications?alert_id=${createRuleBody.id}`)
.set('kbn-xsrf', 'true')
.send({
name: 'Legacy notification with one action',
interval: '1h',
actions: [
{
id: hookAction.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
actionTypeId: hookAction.actionTypeId,
},
],
})
.expect(200);
// read the rule which should have the legacy actions attached
const { body } = await supertest
.get(`${DETECTION_ENGINE_RULES_URL}?id=${createRuleBody.id}`)
.set('kbn-xsrf', 'true')
.send(getSimpleRule())
.expect(200);
const bodyToCompare = removeServerGeneratedProperties(body);
const ruleWithActions: ReturnType<typeof getSimpleRuleOutput> = {
...getSimpleRuleOutput(),
actions: [
{
id: hookAction.id,
group: 'default',
params: {
message:
'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts',
},
action_type_id: hookAction.actionTypeId,
},
],
throttle: '1h',
};
expect(bodyToCompare).to.eql(ruleWithActions);
});
});
});
});
};