Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Security Solution][Exceptions] Exception modal bulk close option only closes alerts generated by same rule (#77402) #77839

Merged
merged 1 commit into from
Sep 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,15 @@ 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',
}
);

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)',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<EuiOverlayMask onClick={onCancel}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,15 @@ 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',
}
);

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)',
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -42,12 +42,16 @@ describe('useAddOrUpdateException', () => {
>>;
let getQueryFilter: jest.SpyInstance<ReturnType<typeof getQueryFilterHelper.getQueryFilter>>;
let buildAlertStatusFilter: jest.SpyInstance<ReturnType<
typeof buildAlertStatusFilterHelper.buildAlertStatusFilter
typeof buildFilterHelpers.buildAlertStatusFilter
>>;
let buildAlertsRuleIdFilter: jest.SpyInstance<ReturnType<
typeof buildFilterHelpers.buildAlertsRuleIdFilter
>>;
let addOrUpdateItemsArgs: Parameters<AddOrUpdateExceptionItemsFunc>;
let render: () => RenderHookResult<UseAddOrUpdateExceptionProps, ReturnUseAddOrUpdateException>;
const onError = jest.fn();
const onSuccess = jest.fn();
const ruleId = 'rule-id';
const alertIdToClose = 'idToClose';
const bulkCloseIndex = ['.custom'];
const itemsToAdd: CreateExceptionListItemSchema[] = [
Expand Down Expand Up @@ -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<UseAddOrUpdateExceptionProps, ReturnUseAddOrUpdateException>(() =>
useAddOrUpdateException({
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,25 @@ 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';

/**
* 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<ExceptionListItemSchema | CreateExceptionListItemSchema>,
alertIdToClose?: string,
bulkCloseIndex?: Index
Expand Down Expand Up @@ -63,9 +68,10 @@ export const useAddOrUpdateException = ({
const [isLoading, setIsLoading] = useState(false);
const addOrUpdateExceptionRef = useRef<AddOrUpdateExceptionItemsFunc | null>(null);
const addOrUpdateException = useCallback<AddOrUpdateExceptionItemsFunc>(
async (exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => {
async (ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => {
if (addOrUpdateExceptionRef.current !== null) {
addOrUpdateExceptionRef.current(
ruleId,
exceptionItemsToAddOrUpdate,
alertIdToClose,
bulkCloseIndex
Expand Down Expand Up @@ -117,6 +123,7 @@ export const useAddOrUpdateException = ({
};

const addOrUpdateExceptionItems: AddOrUpdateExceptionItemsFunc = async (
ruleId,
exceptionItemsToAddOrUpdate,
alertIdToClose,
bulkCloseIndex
Expand All @@ -137,7 +144,7 @@ export const useAddOrUpdateException = ({
const filter = getQueryFilter(
'',
'kuery',
buildAlertStatusFilter('open'),
[...buildAlertsRuleIdFilter(ruleId), ...buildAlertStatusFilter('open')],
bulkCloseIndex,
prepareExceptionItemsForBulkClose(exceptionItemsToAddOrUpdate),
false
Expand Down