[Security Solution][Detections] Adds list plugin Saved Objects to Security feature privilege (#90895) (#91075)
## Summary Add's the list plugins Saved Objects (`exception-list` and `exception-list-agnostic`) to the `Security` feature privilege. Resolves https://github.com/elastic/kibana/issues/90715 ### Test Instructions Load pre-packaged roles/users, and ensure only those with the Kibana Space privilege `Security:All` have the ability to create/edit rules and exception lists (space-aware/agnostic). Users with `Security:Read` should only be able to view rules/exception lists. Pre-packaged security roles should no longer be granted the `Saved Objects Management` feature privilege, and this feature privilege should no longer be required to use any of the Detections features. To add test users: t1_analyst (`"siem": ["read"]`): ``` bash cd x-pack/plugins/security_solution/server/lib/detection_engine/scripts/ ./roles_users/t1_analyst/post_detections_role.sh roles_users/t1_analyst/detections_role.json ./roles_users/t1_analyst/post_detections_user.sh roles_users/t1_analyst/detections_user.json ``` hunter (`"siem": ["all"]`): ``` bash cd x-pack/plugins/security_solution/server/lib/detection_engine/scripts/ ./roles_users/t1_analyst/post_detections_role.sh roles_users/hunter/detections_role.json ./roles_users/t1_analyst/post_detections_user.sh roles_users/hunter/detections_user.json ``` Note: Be sure to remove these users after testing if using a public cluster. ### Checklist Delete any items that are not applicable to this PR. - [X] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials -- `docs` label added, will work with @jmikell821 on doc changes - [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 Co-authored-by: Garrett Spong <spong@users.noreply.github.com>
This commit is contained in:
parent
17b2b27765
commit
5950d262eb
|
@ -26,8 +26,7 @@
|
|||
"siem": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"],
|
||||
"dev_tools": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
"dev_tools": ["all"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ This user can CRUD rules and signals. The main difference here is the user has
|
|||
|
||||
```json
|
||||
"builtInAlerts": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
```
|
||||
|
||||
privileges whereas the T1 and T2 have "read" privileges which prevents them from creating rules
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
"ml": ["read"],
|
||||
"siem": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
"ml": ["all"],
|
||||
"siem": ["all"],
|
||||
"actions": ["all"],
|
||||
"builtInAlerts": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
"ml": ["read"],
|
||||
"siem": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"],
|
||||
"savedObjectsManagement": ["read"]
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
"ml": ["read"],
|
||||
"siem": ["all"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
"ml": ["read"],
|
||||
"siem": ["all"],
|
||||
"actions": ["all"],
|
||||
"builtInAlerts": ["all"],
|
||||
"savedObjectsManagement": ["all"]
|
||||
"builtInAlerts": ["all"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -21,10 +21,9 @@
|
|||
{
|
||||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["all"],
|
||||
"siem": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"],
|
||||
"savedObjectsManagement": ["read"]
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -23,10 +23,9 @@
|
|||
{
|
||||
"feature": {
|
||||
"ml": ["read"],
|
||||
"siem": ["all"],
|
||||
"siem": ["read"],
|
||||
"actions": ["read"],
|
||||
"builtInAlerts": ["read"],
|
||||
"savedObjectsManagement": ["read"]
|
||||
"builtInAlerts": ["read"]
|
||||
},
|
||||
"spaces": ["*"]
|
||||
}
|
||||
|
|
|
@ -219,6 +219,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
'cases-comments',
|
||||
'cases-configure',
|
||||
'cases-user-actions',
|
||||
'exception-list',
|
||||
'exception-list-agnostic',
|
||||
...savedObjectTypes,
|
||||
],
|
||||
read: ['config'],
|
||||
|
@ -243,6 +245,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
'cases-comments',
|
||||
'cases-configure',
|
||||
'cases-user-actions',
|
||||
'exception-list',
|
||||
'exception-list-agnostic',
|
||||
...savedObjectTypes,
|
||||
],
|
||||
},
|
||||
|
|
|
@ -100,7 +100,6 @@ interface RoleInterface {
|
|||
siem: string[];
|
||||
actions: string[];
|
||||
builtInAlerts: string[];
|
||||
savedObjectsManagement: string[];
|
||||
};
|
||||
spaces: string[];
|
||||
}>;
|
||||
|
|
|
@ -14,7 +14,10 @@ import { deleteAllExceptions } from '../../../lists_api_integration/utils';
|
|||
import { RulesSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/response';
|
||||
import { getCreateExceptionListMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_schema.mock';
|
||||
import { CreateExceptionListItemSchema } from '../../../../plugins/lists/common';
|
||||
import { EXCEPTION_LIST_URL } from '../../../../plugins/lists/common/constants';
|
||||
import {
|
||||
EXCEPTION_LIST_ITEM_URL,
|
||||
EXCEPTION_LIST_URL,
|
||||
} from '../../../../plugins/lists/common/constants';
|
||||
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
@ -37,10 +40,13 @@ import {
|
|||
findImmutableRuleById,
|
||||
getPrePackagedRulesStatus,
|
||||
} from '../../utils';
|
||||
import { ROLES } from '../../../../plugins/security_solution/common/test';
|
||||
import { createUserAndRole, deleteUserAndRole } from '../roles_users_utils';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
|
||||
|
@ -58,419 +64,456 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('auditbeat/hosts');
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id and add an exception list to the rule', async () => {
|
||||
const {
|
||||
body: { id, list_id, namespace_type, type },
|
||||
} = await supertest
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
describe('elastic admin', () => {
|
||||
it('should create a single rule with a rule_id and add an exception list to the rule', async () => {
|
||||
const {
|
||||
body: { id, list_id, namespace_type, type },
|
||||
} = await supertest
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
const ruleWithException: CreateRulesSchema = {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
const ruleWithException: CreateRulesSchema = {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rule = await createRule(supertest, ruleWithException);
|
||||
const expected: Partial<RulesSchema> = {
|
||||
...getSimpleRuleOutput(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
const bodyToCompare = removeServerGeneratedProperties(rule);
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should create a single rule with an exception list and validate it ran successfully', async () => {
|
||||
const {
|
||||
body: { id, list_id, namespace_type, type },
|
||||
} = await supertest
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
const ruleWithException: CreateRulesSchema = {
|
||||
...getSimpleRule(),
|
||||
enabled: true,
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rule = await createRule(supertest, ruleWithException);
|
||||
await waitForRuleSuccessOrStatus(supertest, rule.id);
|
||||
const bodyToCompare = removeServerGeneratedProperties(rule);
|
||||
|
||||
const expected: Partial<RulesSchema> = {
|
||||
...getSimpleRuleOutput(),
|
||||
enabled: true,
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should allow removing an exception list from an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one exceptions_list
|
||||
|
||||
// remove the exceptions list as a user is allowed to remove it from an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] })
|
||||
.expect(200);
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(0);
|
||||
});
|
||||
|
||||
it('should allow adding a second exception list to an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(2);
|
||||
});
|
||||
|
||||
it('should override any updates to pre-packaged rules if the user removes the exception list through the API but the new version of a rule has an exception list again', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] })
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// We should have a length of 1 and it should be the same as our original before we tried to remove it using patch
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(1);
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql(immutableRule.exceptions_list);
|
||||
});
|
||||
|
||||
it('should merge back an exceptions_list if it was removed from the immutable rule through PATCH', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// remove the exception list and only have a single list that is not an endpoint_list
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
]);
|
||||
});
|
||||
|
||||
const rule = await createRule(supertest, ruleWithException);
|
||||
const expected: Partial<RulesSchema> = {
|
||||
...getSimpleRuleOutput(),
|
||||
exceptions_list: [
|
||||
it('should NOT add an extra exceptions_list that already exists on a rule during an upgrade', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// The installed rule should have both the original immutable exceptions list back and the
|
||||
// new list the user added.
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
...immutableRule.exceptions_list,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should NOT allow updates to pre-packaged rules to overwrite existing exception based rules when the user adds an additional exception list', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// It should be the same as what the user added originally
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
const bodyToCompare = removeServerGeneratedProperties(rule);
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
});
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create a single rule with an exception list and validate it ran successfully', async () => {
|
||||
const {
|
||||
body: { id, list_id, namespace_type, type },
|
||||
} = await supertest
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
it('should not remove any exceptions added to a pre-packaged/immutable rule during an update if that rule has no existing exception lists', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const ruleWithException: CreateRulesSchema = {
|
||||
...getSimpleRule(),
|
||||
enabled: true,
|
||||
exceptions_list: [
|
||||
// Create a new exception list
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// Rule id of "eb079c62-4481-4d6e-9643-3ca499df7aaa" is from the file:
|
||||
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json
|
||||
// since this rule does not have existing exceptions_list that we are going to use for tests
|
||||
const immutableRule = await getRule(supertest, 'eb079c62-4481-4d6e-9643-3ca499df7aaa');
|
||||
expect(immutableRule.exceptions_list.length).eql(0); // make sure we have no exceptions_list
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: 'eb079c62-4481-4d6e-9643-3ca499df7aaa',
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, 'eb079c62-4481-4d6e-9643-3ca499df7aaa');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'eb079c62-4481-4d6e-9643-3ca499df7aaa'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
]);
|
||||
});
|
||||
|
||||
const rule = await createRule(supertest, ruleWithException);
|
||||
await waitForRuleSuccessOrStatus(supertest, rule.id);
|
||||
const bodyToCompare = removeServerGeneratedProperties(rule);
|
||||
it('should not change the immutable tags when adding a second exception list to an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const expected: Partial<RulesSchema> = {
|
||||
...getSimpleRuleOutput(),
|
||||
enabled: true,
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const body = await findImmutableRuleById(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
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]);
|
||||
expect(bodyToCompare.rule_id).to.eql(immutableRule.rule_id); // Rule id should not change with a a patch
|
||||
expect(bodyToCompare.immutable).to.eql(immutableRule.immutable); // Immutable should always stay the same which is true and never flip to false.
|
||||
expect(bodyToCompare.version).to.eql(immutableRule.version); // The version should never update on a patch
|
||||
});
|
||||
|
||||
it('should not change count of prepacked rules when adding a second exception list to an immutable rule through patch. If this fails, suspect the immutable tags are not staying on the rule correctly.', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const status = await getPrePackagedRulesStatus(supertest);
|
||||
expect(status.rules_not_installed).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow removing an exception list from an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
describe('t1_analyst', () => {
|
||||
const role = ROLES.t1_analyst;
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one exceptions_list
|
||||
beforeEach(async () => {
|
||||
await createUserAndRole(getService, role);
|
||||
});
|
||||
|
||||
// remove the exceptions list as a user is allowed to remove it from an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] })
|
||||
.expect(200);
|
||||
afterEach(async () => {
|
||||
await deleteUserAndRole(getService, role);
|
||||
});
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(0);
|
||||
});
|
||||
it('should NOT be able to create an exception list', async () => {
|
||||
await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(role, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListItemMinimalSchemaMock())
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('should allow adding a second exception list to an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(2);
|
||||
});
|
||||
|
||||
it('should override any updates to pre-packaged rules if the user removes the exception list through the API but the new version of a rule has an exception list again', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] })
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// We should have a length of 1 and it should be the same as our original before we tried to remove it using patch
|
||||
expect(immutableRuleSecondTime.exceptions_list.length).to.eql(1);
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql(immutableRule.exceptions_list);
|
||||
});
|
||||
|
||||
it('should merge back an exceptions_list if it was removed from the immutable rule through PATCH', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// remove the exception list and only have a single list that is not an endpoint_list
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should NOT add an extra exceptions_list that already exists on a rule during an upgrade', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// The installed rule should have both the original immutable exceptions list back and the
|
||||
// new list the user added.
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([...immutableRule.exceptions_list]);
|
||||
});
|
||||
|
||||
it('should NOT allow updates to pre-packaged rules to overwrite existing exception based rules when the user adds an additional exception list', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'9a1a2dae-0b5f-4c3d-8305-a268d404c306'
|
||||
);
|
||||
|
||||
// It should be the same as what the user added originally
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not remove any exceptions added to a pre-packaged/immutable rule during an update if that rule has no existing exception lists', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
// Create a new exception list
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// Rule id of "eb079c62-4481-4d6e-9643-3ca499df7aaa" is from the file:
|
||||
// x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json
|
||||
// since this rule does not have existing exceptions_list that we are going to use for tests
|
||||
const immutableRule = await getRule(supertest, 'eb079c62-4481-4d6e-9643-3ca499df7aaa');
|
||||
expect(immutableRule.exceptions_list.length).eql(0); // make sure we have no exceptions_list
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: 'eb079c62-4481-4d6e-9643-3ca499df7aaa',
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await downgradeImmutableRule(es, 'eb079c62-4481-4d6e-9643-3ca499df7aaa');
|
||||
await installPrePackagedRules(supertest);
|
||||
const immutableRuleSecondTime = await getRule(
|
||||
supertest,
|
||||
'eb079c62-4481-4d6e-9643-3ca499df7aaa'
|
||||
);
|
||||
|
||||
expect(immutableRuleSecondTime.exceptions_list).to.eql([
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not change the immutable tags when adding a second exception list to an immutable rule through patch', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const body = await findImmutableRuleById(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
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]);
|
||||
expect(bodyToCompare.rule_id).to.eql(immutableRule.rule_id); // Rule id should not change with a a patch
|
||||
expect(bodyToCompare.immutable).to.eql(immutableRule.immutable); // Immutable should always stay the same which is true and never flip to false.
|
||||
expect(bodyToCompare.version).to.eql(immutableRule.version); // The version should never update on a patch
|
||||
});
|
||||
|
||||
it('should not change count of prepacked rules when adding a second exception list to an immutable rule through patch. If this fails, suspect the immutable tags are not staying on the rule correctly.', async () => {
|
||||
await installPrePackagedRules(supertest);
|
||||
|
||||
const { id, list_id, namespace_type, type } = await createExceptionList(
|
||||
supertest,
|
||||
getCreateExceptionListMinimalSchemaMock()
|
||||
);
|
||||
|
||||
// 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
|
||||
// This rule has an existing exceptions_list that we are going to use
|
||||
const immutableRule = await getRule(supertest, '9a1a2dae-0b5f-4c3d-8305-a268d404c306');
|
||||
expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one
|
||||
|
||||
// add a second exceptions list as a user is allowed to add a second list to an immutable rule
|
||||
await supertest
|
||||
.patch(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306',
|
||||
exceptions_list: [
|
||||
...immutableRule.exceptions_list,
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const status = await getPrePackagedRulesStatus(supertest);
|
||||
expect(status.rules_not_installed).to.eql(0);
|
||||
it('should NOT be able to create an exception list item', async () => {
|
||||
await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(role, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListItemMinimalSchemaMock())
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tests with auditbeat data', () => {
|
||||
|
|
|
@ -30,10 +30,13 @@ import {
|
|||
getRuleForSignalTesting,
|
||||
getRuleForSignalTestingWithTimestampOverride,
|
||||
} from '../../utils';
|
||||
import { ROLES } from '../../../../plugins/security_solution/common/test';
|
||||
import { createUserAndRole, deleteUserAndRole } from '../roles_users_utils';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('create_rules', () => {
|
||||
|
@ -65,186 +68,209 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('auditbeat/hosts');
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
describe('elastic admin', () => {
|
||||
it('should create a single rule with a rule_id', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutput());
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutput());
|
||||
});
|
||||
|
||||
/*
|
||||
This test is to ensure no future regressions introduced by the following scenario
|
||||
a call to updateApiKey was invalidating the api key used by the
|
||||
rule while the rule was executing, or even before it executed,
|
||||
on the first rule run.
|
||||
this pr https://github.com/elastic/kibana/pull/68184
|
||||
fixed this by finding the true source of a bug that required the manual
|
||||
api key update, and removed the call to that function.
|
||||
|
||||
When the api key is updated before / while the rule is executing, the alert
|
||||
executor no longer has access to a service to update the rule status
|
||||
saved object in Elasticsearch. Because of this, we cannot set the rule into
|
||||
a 'failure' state, so the user ends up seeing 'going to run' as that is the
|
||||
last status set for the rule before it erupts in an error that cannot be
|
||||
recorded inside of the executor.
|
||||
|
||||
This adds an e2e test for the backend to catch that in case
|
||||
this pops up again elsewhere.
|
||||
*/
|
||||
it('should create a single rule with a rule_id and validate it ran successfully', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['auditbeat-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id);
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('succeeded');
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id and an index pattern that does not match anything available and fail the rule', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['does-not-exist-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id, 'failed');
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('failed');
|
||||
expect(statusBody[body.id].current_status.last_failure_message).to.eql(
|
||||
'The following index patterns did not match any indices: ["does-not-exist-*"]'
|
||||
);
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id and an index pattern that does not match anything and an index pattern that does and the rule should be successful', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['does-not-exist-*', 'auditbeat-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id, 'succeeded');
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('succeeded');
|
||||
});
|
||||
|
||||
it('should create a single rule without an input index', async () => {
|
||||
const rule: CreateRulesSchema = {
|
||||
name: 'Simple Rule Query',
|
||||
description: 'Simple Rule Query',
|
||||
enabled: true,
|
||||
risk_score: 1,
|
||||
rule_id: 'rule-1',
|
||||
severity: 'high',
|
||||
type: 'query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
};
|
||||
const expected = {
|
||||
actions: [],
|
||||
author: [],
|
||||
created_by: 'elastic',
|
||||
description: 'Simple Rule Query',
|
||||
enabled: true,
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
interval: '5m',
|
||||
rule_id: 'rule-1',
|
||||
language: 'kuery',
|
||||
output_index: '.siem-signals-default',
|
||||
max_signals: 100,
|
||||
risk_score: 1,
|
||||
risk_score_mapping: [],
|
||||
name: 'Simple Rule Query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: [],
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic',
|
||||
tags: [],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
throttle: 'no_actions',
|
||||
exceptions_list: [],
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(rule)
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should create a single rule without a rule_id', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRuleWithoutRuleId())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId());
|
||||
});
|
||||
|
||||
it('should create a single Machine Learning rule', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleMlRule())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleMlRuleOutput());
|
||||
});
|
||||
|
||||
it('should cause a 409 conflict if we attempt to create the same rule_id twice', async () => {
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(409);
|
||||
|
||||
expect(body).to.eql({
|
||||
message: 'rule_id: "rule-1" already exists',
|
||||
status_code: 409,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
This test is to ensure no future regressions introduced by the following scenario
|
||||
a call to updateApiKey was invalidating the api key used by the
|
||||
rule while the rule was executing, or even before it executed,
|
||||
on the first rule run.
|
||||
this pr https://github.com/elastic/kibana/pull/68184
|
||||
fixed this by finding the true source of a bug that required the manual
|
||||
api key update, and removed the call to that function.
|
||||
describe('t1_analyst', () => {
|
||||
const role = ROLES.t1_analyst;
|
||||
|
||||
When the api key is updated before / while the rule is executing, the alert
|
||||
executor no longer has access to a service to update the rule status
|
||||
saved object in Elasticsearch. Because of this, we cannot set the rule into
|
||||
a 'failure' state, so the user ends up seeing 'going to run' as that is the
|
||||
last status set for the rule before it erupts in an error that cannot be
|
||||
recorded inside of the executor.
|
||||
beforeEach(async () => {
|
||||
await createUserAndRole(getService, role);
|
||||
});
|
||||
|
||||
This adds an e2e test for the backend to catch that in case
|
||||
this pops up again elsewhere.
|
||||
*/
|
||||
it('should create a single rule with a rule_id and validate it ran successfully', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['auditbeat-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
afterEach(async () => {
|
||||
await deleteUserAndRole(getService, role);
|
||||
});
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id);
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('succeeded');
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id and an index pattern that does not match anything available and fail the rule', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['does-not-exist-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id, 'failed');
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('failed');
|
||||
expect(statusBody[body.id].current_status.last_failure_message).to.eql(
|
||||
'The following index patterns did not match any indices: ["does-not-exist-*"]'
|
||||
);
|
||||
});
|
||||
|
||||
it('should create a single rule with a rule_id and an index pattern that does not match anything and an index pattern that does and the rule should be successful', async () => {
|
||||
const simpleRule = getRuleForSignalTesting(['does-not-exist-*', 'auditbeat-*']);
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(simpleRule)
|
||||
.expect(200);
|
||||
|
||||
await waitForRuleSuccessOrStatus(supertest, body.id, 'succeeded');
|
||||
|
||||
const { body: statusBody } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ ids: [body.id] })
|
||||
.expect(200);
|
||||
|
||||
expect(statusBody[body.id].current_status.status).to.eql('succeeded');
|
||||
});
|
||||
|
||||
it('should create a single rule without an input index', async () => {
|
||||
const rule: CreateRulesSchema = {
|
||||
name: 'Simple Rule Query',
|
||||
description: 'Simple Rule Query',
|
||||
enabled: true,
|
||||
risk_score: 1,
|
||||
rule_id: 'rule-1',
|
||||
severity: 'high',
|
||||
type: 'query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
};
|
||||
const expected = {
|
||||
actions: [],
|
||||
author: [],
|
||||
created_by: 'elastic',
|
||||
description: 'Simple Rule Query',
|
||||
enabled: true,
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
interval: '5m',
|
||||
rule_id: 'rule-1',
|
||||
language: 'kuery',
|
||||
output_index: '.siem-signals-default',
|
||||
max_signals: 100,
|
||||
risk_score: 1,
|
||||
risk_score_mapping: [],
|
||||
name: 'Simple Rule Query',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: [],
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic',
|
||||
tags: [],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
throttle: 'no_actions',
|
||||
exceptions_list: [],
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(rule)
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should create a single rule without a rule_id', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRuleWithoutRuleId())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId());
|
||||
});
|
||||
|
||||
it('should create a single Machine Learning rule', async () => {
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleMlRule())
|
||||
.expect(200);
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(getSimpleMlRuleOutput());
|
||||
});
|
||||
|
||||
it('should cause a 409 conflict if we attempt to create the same rule_id twice', async () => {
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(200);
|
||||
|
||||
const { body } = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(409);
|
||||
|
||||
expect(body).to.eql({
|
||||
message: 'rule_id: "rule-1" already exists',
|
||||
status_code: 409,
|
||||
it('should NOT be able to create a rule', async () => {
|
||||
await supertestWithoutAuth
|
||||
.post(DETECTION_ENGINE_RULES_URL)
|
||||
.auth(role, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getSimpleRule())
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue