[Security Solution][Bulk actions]- Fix bulk actions data views behavior (#138448) (#138466)

## Summary

Addresses [bug](https://github.com/elastic/kibana/issues/138383) found where even when `overwrite_data_views` was false, the rule's `index` property was being modified.

Please see added integration tests to understand desired behavior of changes. There is one edge case which is a bit weird, but I think too late to address in 8.4. If a user uses bulk delete on a rule with a data view and _no_ index patterns defined and `overwrite_data_views = true`, both data view and index will be set to `undefined`. Per our current behavior, a rule with no data source defaults to using the default index patterns. Not sure this is ideal, but it is in line with the behavior that already exists for a rule.

(cherry picked from commit 9e8b5b9784)

Co-authored-by: Yara Tercero <yctercero@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2022-08-09 19:46:00 -04:00 committed by GitHub
parent a889f564c5
commit 8f667bab70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 305 additions and 72 deletions

View file

@ -126,6 +126,36 @@ describe('ruleParamsModifier', () => {
expect(editedRuleParams).toHaveProperty('dataViewId', undefined);
});
test('should set dataViewId to undefined if overwrite_data_views=true on delete_index_patterns action', () => {
const editedRuleParams = ruleParamsModifier(
{ dataViewId: 'test-data-view', index: ['test-*', 'index'] } as RuleAlertType['params'],
[
{
type: BulkActionEditType.delete_index_patterns,
value: ['index'],
overwrite_data_views: true,
},
]
);
expect(editedRuleParams).toHaveProperty('dataViewId', undefined);
expect(editedRuleParams).toHaveProperty('index', ['test-*']);
});
test('should set dataViewId to undefined and index to undefined if overwrite_data_views=true on delete_index_patterns action and rule had no index patterns to begin with', () => {
const editedRuleParams = ruleParamsModifier(
{ dataViewId: 'test-data-view', index: undefined } as RuleAlertType['params'],
[
{
type: BulkActionEditType.delete_index_patterns,
value: ['index'],
overwrite_data_views: true,
},
]
);
expect(editedRuleParams).toHaveProperty('dataViewId', undefined);
expect(editedRuleParams).toHaveProperty('index', undefined);
});
test('should throw error on adding index pattern if rule is of machine learning type', () => {
expect(() =>
ruleParamsModifier({ type: 'machine_learning' } as RuleAlertType['params'], [

View file

@ -35,6 +35,10 @@ const applyBulkActionEditToRuleParams = (
"Index patterns can't be added. Machine learning rule doesn't have index patterns property"
);
if (ruleParams.dataViewId != null && !action.overwrite_data_views) {
break;
}
if (action.overwrite_data_views) {
ruleParams.dataViewId = undefined;
}
@ -48,6 +52,14 @@ const applyBulkActionEditToRuleParams = (
"Index patterns can't be deleted. Machine learning rule doesn't have index patterns property"
);
if (ruleParams.dataViewId != null && !action.overwrite_data_views) {
break;
}
if (action.overwrite_data_views) {
ruleParams.dataViewId = undefined;
}
if (ruleParams.index) {
ruleParams.index = deleteItemsFromArray(ruleParams.index, action.value);
}
@ -59,6 +71,10 @@ const applyBulkActionEditToRuleParams = (
"Index patterns can't be overwritten. Machine learning rule doesn't have index patterns property"
);
if (ruleParams.dataViewId != null && !action.overwrite_data_views) {
break;
}
if (action.overwrite_data_views) {
ruleParams.dataViewId = undefined;
}

View file

@ -562,78 +562,6 @@ export default ({ getService }: FtrProviderContext): void => {
expect(deleteIndexRule.index).to.eql(['initial-index-*', 'index2-*']);
});
it('should add an index pattern to a rule and overwrite the data view', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: setIndexBody } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.add_index_patterns,
value: ['initial-index-*'],
overwrite_data_views: true,
},
],
})
.expect(200);
expect(setIndexBody.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(setIndexBody.attributes.results.updated[0].index).to.eql(['initial-index-*']);
expect(setIndexBody.attributes.results.updated[0].data_view_id).to.eql(undefined);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(['initial-index-*']);
});
it('should not delete data view in a rule when delete index pattern action applied', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: bulkActionResponse } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.delete_index_patterns,
value: ['initial-index-*'],
},
],
})
.expect(200);
expect(bulkActionResponse.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(bulkActionResponse.attributes.results.updated[0].data_view_id).to.be(dataViewId);
expect(bulkActionResponse.attributes.results.updated[0].index).to.be(undefined);
// Check that the updates have been persisted
const { body: updatedRule } = await fetchRule(ruleId).expect(200);
expect(updatedRule.data_view_id).to.be(dataViewId);
expect(updatedRule.index).to.be(undefined);
});
it('should set timeline values in rule', async () => {
const ruleId = 'ruleId';
const timelineId = '91832785-286d-4ebe-b884-1a208d111a70';
@ -835,6 +763,265 @@ export default ({ getService }: FtrProviderContext): void => {
});
});
describe('overwrite_data_views', () => {
it('should add an index pattern to a rule and overwrite the data view when overwrite_data_views is true', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: setIndexBody } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.add_index_patterns,
value: ['initial-index-*'],
overwrite_data_views: true,
},
],
})
.expect(200);
expect(setIndexBody.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(setIndexBody.attributes.results.updated[0].index).to.eql(['initial-index-*']);
expect(setIndexBody.attributes.results.updated[0].data_view_id).to.eql(undefined);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(['initial-index-*']);
});
it('should NOT add an index pattern to a rule and overwrite the data view when overwrite_data_views is false', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: setIndexBody } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.add_index_patterns,
value: ['initial-index-*'],
overwrite_data_views: false,
},
],
})
.expect(200);
expect(setIndexBody.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(setIndexBody.attributes.results.updated[0].index).to.eql(undefined);
expect(setIndexBody.attributes.results.updated[0].data_view_id).to.eql(dataViewId);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(undefined);
expect(setIndexRule.data_view_id).to.eql(dataViewId);
});
it('should set an index pattern to a rule and overwrite the data view when overwrite_data_views is true', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: setIndexBody } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.set_index_patterns,
value: ['initial-index-*'],
overwrite_data_views: true,
},
],
})
.expect(200);
expect(setIndexBody.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(setIndexBody.attributes.results.updated[0].index).to.eql(['initial-index-*']);
expect(setIndexBody.attributes.results.updated[0].data_view_id).to.eql(undefined);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(['initial-index-*']);
expect(setIndexRule.data_view_id).to.eql(undefined);
});
it('should NOT set an index pattern to a rule and overwrite the data view when overwrite_data_views is false', async () => {
const ruleId = 'ruleId';
const dataViewId = 'index1-*';
const simpleRule = {
...getSimpleRule(ruleId),
index: undefined,
data_view_id: dataViewId,
};
await createRule(supertest, log, simpleRule);
const { body: setIndexBody } = await postBulkAction()
.send({
query: '',
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.set_index_patterns,
value: ['initial-index-*'],
overwrite_data_views: false,
},
],
})
.expect(200);
expect(setIndexBody.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(setIndexBody.attributes.results.updated[0].index).to.eql(undefined);
expect(setIndexBody.attributes.results.updated[0].data_view_id).to.eql(dataViewId);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(undefined);
expect(setIndexRule.data_view_id).to.eql(dataViewId);
});
// This rule will now not have a source defined - as has been the behavior of rules since the beginning
// this rule will use the default index patterns on rule run
it('should NOT error if all index patterns removed from a rule with data views when no index patterns exist on the rule and overwrite_data_views is true', async () => {
const dataViewId = 'index1-*';
const ruleId = 'ruleId';
const rule = await createRule(supertest, log, {
...getSimpleRule(ruleId),
data_view_id: dataViewId,
index: undefined,
});
const { body } = await postBulkAction()
.send({
ids: [rule.id],
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.delete_index_patterns,
value: ['simple-index-*'],
overwrite_data_views: true,
},
],
})
.expect(200);
expect(body.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(body.attributes.results.updated[0].index).to.eql(undefined);
expect(body.attributes.results.updated[0].data_view_id).to.eql(undefined);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(undefined);
expect(setIndexRule.data_view_id).to.eql(undefined);
});
it('should return error if all index patterns removed from a rule with data views and overwrite_data_views is true', async () => {
const dataViewId = 'index1-*';
const ruleId = 'ruleId';
const rule = await createRule(supertest, log, {
...getSimpleRule(ruleId),
data_view_id: dataViewId,
index: ['simple-index-*'],
});
const { body } = await postBulkAction()
.send({
ids: [rule.id],
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.delete_index_patterns,
value: ['simple-index-*'],
overwrite_data_views: true,
},
],
})
.expect(500);
expect(body.attributes.summary).to.eql({ failed: 1, succeeded: 0, total: 1 });
expect(body.attributes.errors[0]).to.eql({
message: "Mutated params invalid: Index patterns can't be empty",
status_code: 500,
rules: [
{
id: rule.id,
name: rule.name,
},
],
});
});
it('should NOT return error if all index patterns removed from a rule with data views and overwrite_data_views is false', async () => {
const dataViewId = 'index1-*';
const ruleId = 'ruleId';
const rule = await createRule(supertest, log, {
...getSimpleRule(ruleId),
data_view_id: dataViewId,
index: ['simple-index-*'],
});
const { body } = await postBulkAction()
.send({
ids: [rule.id],
action: BulkAction.edit,
[BulkAction.edit]: [
{
type: BulkActionEditType.delete_index_patterns,
value: ['simple-index-*'],
overwrite_data_views: false,
},
],
})
.expect(200);
expect(body.attributes.summary).to.eql({ failed: 0, succeeded: 1, total: 1 });
// Check that the updated rule is returned with the response
expect(body.attributes.results.updated[0].index).to.eql(['simple-index-*']);
expect(body.attributes.results.updated[0].data_view_id).to.eql(dataViewId);
// Check that the updates have been persisted
const { body: setIndexRule } = await fetchRule(ruleId).expect(200);
expect(setIndexRule.index).to.eql(['simple-index-*']);
expect(setIndexRule.data_view_id).to.eql(dataViewId);
});
});
it('should limit concurrent requests to 5', async () => {
const ruleId = 'ruleId';
const timelineId = '91832785-286d-4ebe-b884-1a208d111a70';