diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index ccefbbd287dae..721e53732c093 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -309,10 +309,11 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const alertIdToClose = shouldCloseAlert && alertData ? alertData.ecsData._id : undefined; const bulkCloseIndex = shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined; - addOrUpdateExceptionItems(enrichExceptionItems(), alertIdToClose, bulkCloseIndex); + addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), alertIdToClose, bulkCloseIndex); } }, [ addOrUpdateExceptionItems, + ruleId, enrichExceptionItems, shouldCloseAlert, shouldBulkCloseAlert, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/translations.ts index 2e9bced21fe71..2fe73982e0faf 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/translations.ts @@ -63,8 +63,7 @@ export const ENDPOINT_QUARANTINE_TEXT = i18n.translate( export const BULK_CLOSE_LABEL = i18n.translate( 'xpack.securitySolution.exceptions.addException.bulkCloseLabel', { - defaultMessage: - 'Close all alerts that match this exception, including alerts generated by other rules', + defaultMessage: 'Close all alerts that match this exception and were generated by this rule', } ); @@ -72,7 +71,7 @@ export const BULK_CLOSE_LABEL_DISABLED = i18n.translate( 'xpack.securitySolution.exceptions.addException.bulkCloseLabel.disabled', { defaultMessage: - 'Close all alerts that match attributes in this exception (Lists and non-ECS fields are not supported)', + 'Close all alerts that match this exception and were generated by this rule (Lists and non-ECS fields are not supported)', } ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 41fc6ba57d715..5dbf319c3299d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -240,9 +240,15 @@ export const EditExceptionModal = memo(function EditExceptionModal({ if (addOrUpdateExceptionItems !== null) { const bulkCloseIndex = shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined; - addOrUpdateExceptionItems(enrichExceptionItems(), undefined, bulkCloseIndex); + addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), undefined, bulkCloseIndex); } - }, [addOrUpdateExceptionItems, enrichExceptionItems, shouldBulkCloseAlert, signalIndexName]); + }, [ + addOrUpdateExceptionItems, + ruleId, + enrichExceptionItems, + shouldBulkCloseAlert, + signalIndexName, + ]); return ( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts index 1452003d8f8b8..7c6750f81f84b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts @@ -48,8 +48,7 @@ export const EDIT_EXCEPTION_SUCCESS = i18n.translate( export const BULK_CLOSE_LABEL = i18n.translate( 'xpack.securitySolution.exceptions.editException.bulkCloseLabel', { - defaultMessage: - 'Close all alerts that match this exception, including alerts generated by other rules', + defaultMessage: 'Close all alerts that match this exception and were generated by this rule', } ); @@ -57,7 +56,7 @@ export const BULK_CLOSE_LABEL_DISABLED = i18n.translate( 'xpack.securitySolution.exceptions.editException.bulkCloseLabel.disabled', { defaultMessage: - 'Close all alerts that match attributes in this exception (Lists and non-ECS fields are not supported)', + 'Close all alerts that match this exception and were generated by this rule (Lists and non-ECS fields are not supported)', } ); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx index 2398f8d799c2c..3de949a05c881 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.test.tsx @@ -11,7 +11,7 @@ import { KibanaServices } from '../../../common/lib/kibana'; import * as alertsApi from '../../../detections/containers/detection_engine/alerts/api'; import * as listsApi from '../../../../../lists/public/exceptions/api'; import * as getQueryFilterHelper from '../../../../common/detection_engine/get_query_filter'; -import * as buildAlertStatusFilterHelper from '../../../detections/components/alerts_table/default_config'; +import * as buildFilterHelpers from '../../../detections/components/alerts_table/default_config'; import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; import { getCreateExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/request/create_exception_list_item_schema.mock'; import { getUpdateExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/request/update_exception_list_item_schema.mock'; @@ -42,12 +42,16 @@ describe('useAddOrUpdateException', () => { >>; let getQueryFilter: jest.SpyInstance>; let buildAlertStatusFilter: jest.SpyInstance>; + let buildAlertsRuleIdFilter: jest.SpyInstance>; let addOrUpdateItemsArgs: Parameters; let render: () => RenderHookResult; const onError = jest.fn(); const onSuccess = jest.fn(); + const ruleId = 'rule-id'; const alertIdToClose = 'idToClose'; const bulkCloseIndex = ['.custom']; const itemsToAdd: CreateExceptionListItemSchema[] = [ @@ -122,9 +126,11 @@ describe('useAddOrUpdateException', () => { getQueryFilter = jest.spyOn(getQueryFilterHelper, 'getQueryFilter'); - buildAlertStatusFilter = jest.spyOn(buildAlertStatusFilterHelper, 'buildAlertStatusFilter'); + buildAlertStatusFilter = jest.spyOn(buildFilterHelpers, 'buildAlertStatusFilter'); + + buildAlertsRuleIdFilter = jest.spyOn(buildFilterHelpers, 'buildAlertsRuleIdFilter'); - addOrUpdateItemsArgs = [itemsToAddOrUpdate]; + addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate]; render = () => renderHook(() => useAddOrUpdateException({ @@ -247,7 +253,7 @@ describe('useAddOrUpdateException', () => { describe('when alertIdToClose is passed in', () => { beforeEach(() => { - addOrUpdateItemsArgs = [itemsToAddOrUpdate, alertIdToClose]; + addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate, alertIdToClose]; }); it('should update the alert status', async () => { await act(async () => { @@ -302,7 +308,7 @@ describe('useAddOrUpdateException', () => { describe('when bulkCloseIndex is passed in', () => { beforeEach(() => { - addOrUpdateItemsArgs = [itemsToAddOrUpdate, undefined, bulkCloseIndex]; + addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate, undefined, bulkCloseIndex]; }); it('should update the status of only alerts that are open', async () => { await act(async () => { @@ -320,6 +326,22 @@ describe('useAddOrUpdateException', () => { expect(buildAlertStatusFilter.mock.calls[0][0]).toEqual('open'); }); }); + it('should update the status of only alerts generated by the provided rule', async () => { + await act(async () => { + const { rerender, result, waitForNextUpdate } = render(); + const addOrUpdateItems = await waitForAddOrUpdateFunc({ + rerender, + result, + waitForNextUpdate, + }); + if (addOrUpdateItems) { + addOrUpdateItems(...addOrUpdateItemsArgs); + } + await waitForNextUpdate(); + expect(buildAlertsRuleIdFilter).toHaveBeenCalledTimes(1); + expect(buildAlertsRuleIdFilter.mock.calls[0][0]).toEqual(ruleId); + }); + }); it('should generate the query filter using exceptions', async () => { await act(async () => { const { rerender, result, waitForNextUpdate } = render(); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx index dbd634e97a328..71cdebf21d801 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_add_exception.tsx @@ -17,7 +17,10 @@ import { } from '../../../lists_plugin_deps'; import { updateAlertStatus } from '../../../detections/containers/detection_engine/alerts/api'; import { getUpdateAlertsQuery } from '../../../detections/components/alerts_table/actions'; -import { buildAlertStatusFilter } from '../../../detections/components/alerts_table/default_config'; +import { + buildAlertStatusFilter, + buildAlertsRuleIdFilter, +} from '../../../detections/components/alerts_table/default_config'; import { getQueryFilter } from '../../../../common/detection_engine/get_query_filter'; import { Index } from '../../../../common/detection_engine/schemas/common/schemas'; import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from './helpers'; @@ -25,12 +28,14 @@ import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from /** * Adds exception items to the list. Also optionally closes alerts. * + * @param ruleId id of the rule where the exception updates will be applied * @param exceptionItemsToAddOrUpdate array of ExceptionListItemSchema to add or update * @param alertIdToClose - optional string representing alert to close * @param bulkCloseIndex - optional index used to create bulk close query * */ export type AddOrUpdateExceptionItemsFunc = ( + ruleId: string, exceptionItemsToAddOrUpdate: Array, alertIdToClose?: string, bulkCloseIndex?: Index @@ -63,9 +68,10 @@ export const useAddOrUpdateException = ({ const [isLoading, setIsLoading] = useState(false); const addOrUpdateExceptionRef = useRef(null); const addOrUpdateException = useCallback( - async (exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => { + async (ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => { if (addOrUpdateExceptionRef.current !== null) { addOrUpdateExceptionRef.current( + ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex @@ -117,6 +123,7 @@ export const useAddOrUpdateException = ({ }; const addOrUpdateExceptionItems: AddOrUpdateExceptionItemsFunc = async ( + ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex @@ -137,7 +144,7 @@ export const useAddOrUpdateException = ({ const filter = getQueryFilter( '', 'kuery', - buildAlertStatusFilter('open'), + [...buildAlertsRuleIdFilter(ruleId), ...buildAlertStatusFilter('open')], bulkCloseIndex, prepareExceptionItemsForBulkClose(exceptionItemsToAddOrUpdate), false