diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts index 20bf28d5902a4..c3b4fe0f20134 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.test.ts @@ -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'], [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts index 05dd63def9069..2b3fb5de9ef79 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/bulk_actions/rule_params_modifier.ts @@ -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; } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts index 1a59b28274c15..21e89b2d214b7 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/perform_bulk_action.ts @@ -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';