Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into task/EMT-685-trus…
Browse files Browse the repository at this point in the history
…ted-apps-list-api
  • Loading branch information
paul-tavares committed Aug 26, 2020
2 parents b97524b + 1ca7651 commit 1394a76
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,84 @@ describe('rules_notification_alert_type', () => {
);
});

it('should resolve results_link when meta is undefined to use "/app/security"', async () => {
const ruleAlert = getResult();
delete ruleAlert.params.meta;
alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
alertServices.callCluster.mockResolvedValue({
count: 10,
});

await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled();

const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default',
expect.objectContaining({
results_link:
'/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:1576255233400,kind:absolute,to:1576341633400)),timeline:(linkTo:!(global),timerange:(from:1576255233400,kind:absolute,to:1576341633400)))',
})
);
});

it('should resolve results_link when meta is an empty object to use "/app/security"', async () => {
const ruleAlert = getResult();
ruleAlert.params.meta = {};
alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
alertServices.callCluster.mockResolvedValue({
count: 10,
});
await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled();

const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default',
expect.objectContaining({
results_link:
'/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:1576255233400,kind:absolute,to:1576341633400)),timeline:(linkTo:!(global),timerange:(from:1576255233400,kind:absolute,to:1576341633400)))',
})
);
});

it('should resolve results_link to custom kibana link when given one', async () => {
const ruleAlert = getResult();
ruleAlert.params.meta = {
kibana_siem_app_url: 'http://localhost',
};
alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
alertServices.callCluster.mockResolvedValue({
count: 10,
});
await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled();

const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default',
expect.objectContaining({
results_link:
'http://localhost/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:1576255233400,kind:absolute,to:1576341633400)),timeline:(linkTo:!(global),timerange:(from:1576255233400,kind:absolute,to:1576341633400)))',
})
);
});

it('should not call alertInstanceFactory if signalsCount was 0', async () => {
const ruleAlert = getResult();
alertServices.savedObjectsClient.get.mockResolvedValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export const rulesNotificationAlertType = ({
from: fromInMs,
to: toInMs,
id: ruleAlertSavedObject.id,
kibanaSiemAppUrl: (ruleAlertParams.meta as { kibana_siem_app_url?: string })
.kibana_siem_app_url,
kibanaSiemAppUrl: (ruleAlertParams.meta as { kibana_siem_app_url?: string } | undefined)
?.kibana_siem_app_url,
});

logger.info(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"type": "query",
"index": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"filters": [],
"language": "kuery",
"query": "host.name: *",
"author": [],
"false_positives": [],
"references": [],
"risk_score": 50,
"risk_score_mapping": [],
"severity": "low",
"severity_mapping": [],
"threat": [],
"name": "Host Name Test",
"description": "Host Name Test",
"tags": [],
"license": "",
"interval": "5m",
"from": "now-30s",
"to": "now",
"actions": [
{
"group": "default",
"id": "4c42ecf2-5e9b-4ce6-8a7a-ab620fd8b169",
"params": {
"body": "{}"
},
"action_type_id": ".webhook"
}
],
"enabled": true,
"throttle": "rule"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getListsClient,
getExceptions,
sortExceptionItems,
parseScheduleDates,
} from './utils';
import { RuleExecutorOptions } from './types';
import { searchAfterAndBulkCreate } from './search_after_bulk_create';
Expand Down Expand Up @@ -227,6 +228,105 @@ describe('rules_notification_alert_type', () => {
);
});

it('should resolve results_link when meta is an empty object to use "/app/security"', async () => {
const ruleAlert = getResult();
ruleAlert.params.meta = {};
ruleAlert.actions = [
{
actionTypeId: '.slack',
params: {
message:
'Rule generated {{state.signals_count}} signals\n\n{{context.rule.name}}\n{{{context.results_link}}}',
},
group: 'default',
id: '99403909-ca9b-49ba-9d7a-7e5320e68d05',
},
];

alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
(parseScheduleDates as jest.Mock).mockReturnValue(moment(100));
payload.params.meta = {};
await alert.executor(payload);

expect(scheduleNotificationActions).toHaveBeenCalledWith(
expect.objectContaining({
resultsLink:
'/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))',
})
);
});

it('should resolve results_link when meta is undefined use "/app/security"', async () => {
const ruleAlert = getResult();
delete ruleAlert.params.meta;
ruleAlert.actions = [
{
actionTypeId: '.slack',
params: {
message:
'Rule generated {{state.signals_count}} signals\n\n{{context.rule.name}}\n{{{context.results_link}}}',
},
group: 'default',
id: '99403909-ca9b-49ba-9d7a-7e5320e68d05',
},
];

alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
(parseScheduleDates as jest.Mock).mockReturnValue(moment(100));
delete payload.params.meta;
await alert.executor(payload);

expect(scheduleNotificationActions).toHaveBeenCalledWith(
expect.objectContaining({
resultsLink:
'/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))',
})
);
});

it('should resolve results_link with a custom link', async () => {
const ruleAlert = getResult();
ruleAlert.params.meta = { kibana_siem_app_url: 'http://localhost' };
ruleAlert.actions = [
{
actionTypeId: '.slack',
params: {
message:
'Rule generated {{state.signals_count}} signals\n\n{{context.rule.name}}\n{{{context.results_link}}}',
},
group: 'default',
id: '99403909-ca9b-49ba-9d7a-7e5320e68d05',
},
];

alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'rule-id',
type: 'type',
references: [],
attributes: ruleAlert,
});
(parseScheduleDates as jest.Mock).mockReturnValue(moment(100));
payload.params.meta = { kibana_siem_app_url: 'http://localhost' };
await alert.executor(payload);

expect(scheduleNotificationActions).toHaveBeenCalledWith(
expect.objectContaining({
resultsLink:
'http://localhost/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))',
})
);
});

describe('ML rule', () => {
it('should throw an error if ML plugin was not available', async () => {
const ruleAlert = getMlResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,8 @@ export const signalRulesAlertType = ({
from: fromInMs,
to: toInMs,
id: savedObject.id,
kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string }).kibana_siem_app_url,
kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined)
?.kibana_siem_app_url,
});

logger.info(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import expect from '@kbn/expect';

import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import {
createSignalsIndex,
deleteAllAlerts,
deleteSignalsIndex,
removeServerGeneratedProperties,
waitFor,
getWebHookAction,
getRuleWithWebHookAction,
getSimpleRuleOutputWithWebHookAction,
} from '../../utils';
import { CreateRulesSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema';

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

describe('add_actions', () => {
describe('adding actions', () => {
beforeEach(async () => {
await createSignalsIndex(supertest);
});

afterEach(async () => {
await deleteSignalsIndex(supertest);
await deleteAllAlerts(es);
});

it('should be able to create a new webhook action and attach it to a rule', async () => {
// create a new action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);

// create a rule with the action attached
const { body } = await supertest
.post(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
.send(getRuleWithWebHookAction(hookAction.id))
.expect(200);

const bodyToCompare = removeServerGeneratedProperties(body);
expect(bodyToCompare).to.eql(
getSimpleRuleOutputWithWebHookAction(`${bodyToCompare?.actions?.[0].id}`)
);
});

it('should be able to create a new webhook action and attach it to a rule without a meta field and run it correctly', async () => {
// create a new action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);

// create a rule with the action attached
const { body: rule } = await supertest
.post(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
.send(getRuleWithWebHookAction(hookAction.id))
.expect(200);

// wait for Task Manager to execute the rule and its update status
await waitFor(async () => {
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`)
.set('kbn-xsrf', 'true')
.send({ ids: [rule.id] })
.expect(200);
return body[rule.id].current_status?.status === 'succeeded';
});

// expected result for status should be 'succeeded'
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`)
.set('kbn-xsrf', 'true')
.send({ ids: [rule.id] })
.expect(200);
expect(body[rule.id].current_status.status).to.eql('succeeded');
});

it('should be able to create a new webhook action and attach it to a rule with a meta field and run it correctly', async () => {
// create a new action
const { body: hookAction } = await supertest
.post('/api/actions/action')
.set('kbn-xsrf', 'true')
.send(getWebHookAction())
.expect(200);

// create a rule with the action attached and a meta field
const ruleWithAction: CreateRulesSchema = {
...getRuleWithWebHookAction(hookAction.id),
meta: {},
};

const { body: rule } = await supertest
.post(DETECTION_ENGINE_RULES_URL)
.set('kbn-xsrf', 'true')
.send(ruleWithAction)
.expect(200);

// wait for Task Manager to execute the rule and update status
await waitFor(async () => {
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`)
.set('kbn-xsrf', 'true')
.send({ ids: [rule.id] })
.expect(200);
return body[rule.id].current_status?.status === 'succeeded';
});

// expected result for status should be 'succeeded'
const { body } = await supertest
.post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`)
.set('kbn-xsrf', 'true')
.send({ ids: [rule.id] })
.expect(200);
expect(body[rule.id].current_status.status).to.eql('succeeded');
});
});
});
};
Loading

0 comments on commit 1394a76

Please sign in to comment.