Skip to content

Commit

Permalink
[SIEM][Detections] In progress alert state (#68569)
Browse files Browse the repository at this point in the history
  • Loading branch information
dplumlee authored Jun 15, 2020
1 parent 0ce2970 commit e3ba5e5
Show file tree
Hide file tree
Showing 19 changed files with 446 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export type Severity = t.TypeOf<typeof severity>;
export const severityOrUndefined = t.union([severity, t.undefined]);
export type SeverityOrUndefined = t.TypeOf<typeof severityOrUndefined>;

export const status = t.keyof({ open: null, closed: null });
export const status = t.keyof({ open: null, closed: null, 'in-progress': null });
export type Status = t.TypeOf<typeof status>;

export const job_status = t.keyof({ succeeded: null, failed: null, 'going to run': null });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
*/
import {
NUMBER_OF_ALERTS,
OPEN_CLOSE_ALERTS_BTN,
SELECTED_ALERTS,
SHOWING_ALERTS,
ALERTS,
TAKE_ACTION_POPOVER_BTN,
} from '../screens/detections';

import {
Expand All @@ -22,6 +22,8 @@ import {
waitForAlertsPanelToBeLoaded,
waitForAlerts,
waitForAlertsToBeLoaded,
markInProgressFirstAlert,
goToInProgressAlerts,
} from '../tasks/detections';
import { esArchiverLoad } from '../tasks/es_archiver';
import { loginAndWaitForPage } from '../tasks/login';
Expand Down Expand Up @@ -128,9 +130,9 @@ describe('Detections', () => {
const numberOfAlertsToBeClosed = 1;
const numberOfAlertsToBeSelected = 3;

cy.get(OPEN_CLOSE_ALERTS_BTN).should('have.attr', 'disabled');
cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled');
selectNumberOfAlerts(numberOfAlertsToBeSelected);
cy.get(OPEN_CLOSE_ALERTS_BTN).should('not.have.attr', 'disabled');
cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled');

closeFirstAlert();
cy.reload();
Expand Down Expand Up @@ -173,9 +175,9 @@ describe('Detections', () => {
const numberOfAlertsToBeOpened = 1;
const numberOfAlertsToBeSelected = 3;

cy.get(OPEN_CLOSE_ALERTS_BTN).should('have.attr', 'disabled');
cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled');
selectNumberOfAlerts(numberOfAlertsToBeSelected);
cy.get(OPEN_CLOSE_ALERTS_BTN).should('not.have.attr', 'disabled');
cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled');

openFirstAlert();
cy.reload();
Expand All @@ -202,4 +204,49 @@ describe('Detections', () => {
});
});
});
context('Marking alerts as in-progress', () => {
beforeEach(() => {
esArchiverLoad('alerts');
loginAndWaitForPage(DETECTIONS);
});

it('Mark one alert in progress when more than one open alerts are selected', () => {
waitForAlerts();
waitForAlertsToBeLoaded();

cy.get(NUMBER_OF_ALERTS)
.invoke('text')
.then((numberOfAlerts) => {
const numberOfAlertsToBeMarkedInProgress = 1;
const numberOfAlertsToBeSelected = 3;

cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled');
selectNumberOfAlerts(numberOfAlertsToBeSelected);
cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled');

markInProgressFirstAlert();
cy.reload();
goToOpenedAlerts();
waitForAlertsToBeLoaded();
waitForAlerts();

const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress;
cy.get(NUMBER_OF_ALERTS).invoke('text').should('eq', expectedNumberOfAlerts.toString());
cy.get(SHOWING_ALERTS)
.invoke('text')
.should('eql', `Showing ${expectedNumberOfAlerts.toString()} alerts`);

goToInProgressAlerts();
waitForAlerts();

cy.get(NUMBER_OF_ALERTS)
.invoke('text')
.should('eql', numberOfAlertsToBeMarkedInProgress.toString());
cy.get(SHOWING_ALERTS)
.invoke('text')
.should('eql', `Showing ${numberOfAlertsToBeMarkedInProgress.toString()} alert`);
cy.get(ALERTS).should('have.length', numberOfAlertsToBeMarkedInProgress);
});
});
});
});
25 changes: 20 additions & 5 deletions x-pack/plugins/security_solution/cypress/screens/detections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const CLOSED_ALERTS_BTN = '[data-test-subj="closedAlerts"]';

export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]';

export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]';
Expand All @@ -14,11 +12,11 @@ export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-d

export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text';

export const OPEN_CLOSE_ALERT_BTN = '[data-test-subj="update-alert-status-button"]';
export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]';

export const OPEN_CLOSE_ALERTS_BTN = '[data-test-subj="openCloseAlert"] button';
export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]';

export const OPENED_ALERTS_BTN = '[data-test-subj="openAlerts"]';
export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]';

export const SELECTED_ALERTS = '[data-test-subj="selectedAlerts"]';

Expand All @@ -31,3 +29,20 @@ export const ALERTS = '[data-test-subj="event"]';
export const ALERT_ID = '[data-test-subj="draggable-content-_id"]';

export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input';

export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="alertActionPopover"] button';

export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]';

export const OPEN_SELECTED_ALERTS_BTN = '[data-test-subj="openSelectedAlertsButton"]';

export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closeSelectedAlertsButton"]';

export const MARK_SELECTED_ALERTS_IN_PROGRESS_BTN =
'[data-test-subj="markSelectedAlertsInProgressButton"]';

export const OPEN_ALERT_BTN = '[data-test-subj="open-alert-status"]';

export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]';

export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]';
45 changes: 35 additions & 10 deletions x-pack/plugins/security_solution/cypress/tasks/detections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,74 @@
*/

import {
CLOSED_ALERTS_BTN,
CLOSED_ALERTS_FILTER_BTN,
EXPAND_ALERT_BTN,
LOADING_ALERTS_PANEL,
MANAGE_ALERT_DETECTION_RULES_BTN,
OPEN_CLOSE_ALERT_BTN,
OPEN_CLOSE_ALERTS_BTN,
OPENED_ALERTS_BTN,
OPENED_ALERTS_FILTER_BTN,
SEND_ALERT_TO_TIMELINE_BTN,
ALERTS,
ALERT_CHECKBOX,
TIMELINE_CONTEXT_MENU_BTN,
CLOSE_ALERT_BTN,
TAKE_ACTION_POPOVER_BTN,
CLOSE_SELECTED_ALERTS_BTN,
IN_PROGRESS_ALERTS_FILTER_BTN,
OPEN_ALERT_BTN,
OPEN_SELECTED_ALERTS_BTN,
MARK_ALERT_IN_PROGRESS_BTN,
MARK_SELECTED_ALERTS_IN_PROGRESS_BTN,
} from '../screens/detections';
import { REFRESH_BUTTON } from '../screens/security_header';

export const closeFirstAlert = () => {
cy.get(OPEN_CLOSE_ALERT_BTN).first().click({ force: true });
cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true });
cy.get(CLOSE_ALERT_BTN).click();
};

export const closeAlerts = () => {
cy.get(OPEN_CLOSE_ALERTS_BTN).click({ force: true });
cy.get(TAKE_ACTION_POPOVER_BTN).click({ force: true });
cy.get(CLOSE_SELECTED_ALERTS_BTN).click();
};

export const expandFirstAlert = () => {
cy.get(EXPAND_ALERT_BTN).first().click({ force: true });
};

export const goToClosedAlerts = () => {
cy.get(CLOSED_ALERTS_BTN).click({ force: true });
cy.get(CLOSED_ALERTS_FILTER_BTN).click({ force: true });
};

export const goToManageAlertDetectionRules = () => {
cy.get(MANAGE_ALERT_DETECTION_RULES_BTN).should('exist').click({ force: true });
};

export const goToOpenedAlerts = () => {
cy.get(OPENED_ALERTS_BTN).click({ force: true });
cy.get(OPENED_ALERTS_FILTER_BTN).click({ force: true });
};

export const openFirstAlert = () => {
cy.get(OPEN_CLOSE_ALERT_BTN).first().click({ force: true });
cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true });
cy.get(OPEN_ALERT_BTN).click();
};

export const openAlerts = () => {
cy.get(OPEN_CLOSE_ALERTS_BTN).click({ force: true });
cy.get(TAKE_ACTION_POPOVER_BTN).click({ force: true });
cy.get(OPEN_SELECTED_ALERTS_BTN).click();
};

export const goToInProgressAlerts = () => {
cy.get(IN_PROGRESS_ALERTS_FILTER_BTN).click({ force: true });
};

export const markInProgressFirstAlert = () => {
cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true });
cy.get(MARK_ALERT_IN_PROGRESS_BTN).click();
};

export const markInProgressAlerts = () => {
cy.get(TAKE_ACTION_POPOVER_BTN).click({ force: true });
cy.get(MARK_SELECTED_ALERTS_IN_PROGRESS_BTN).click();
};

export const selectNumberOfAlerts = (numberOfAlerts: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const updateAlertStatusAction = async ({
query,
alertIds,
status,
selectedStatus,
setEventsLoading,
setEventsDeleted,
onAlertStatusUpdateSuccess,
Expand All @@ -64,13 +65,13 @@ export const updateAlertStatusAction = async ({

const queryObject = query ? { query: JSON.parse(query) } : getUpdateAlertsQuery(alertIds);

const response = await updateAlertStatus({ query: queryObject, status });
const response = await updateAlertStatus({ query: queryObject, status: selectedStatus });
// TODO: Only delete those that were successfully updated from updatedRules
setEventsDeleted({ eventIds: alertIds, isDeleted: true });

onAlertStatusUpdateSuccess(response.updated, status);
onAlertStatusUpdateSuccess(response.updated, selectedStatus);
} catch (error) {
onAlertStatusUpdateFailure(status, error);
onAlertStatusUpdateFailure(selectedStatus, error);
} finally {
setEventsLoading({ eventIds: alertIds, isLoading: false });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@

import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui';
import React, { useCallback, useState } from 'react';
import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import * as i18n from '../translations';

export const FILTER_OPEN = 'open';
export const FILTER_CLOSED = 'closed';
export type AlertFilterOption = typeof FILTER_OPEN | typeof FILTER_CLOSED;
export const FILTER_OPEN: Status = 'open';
export const FILTER_CLOSED: Status = 'closed';
export const FILTER_IN_PROGRESS: Status = 'in-progress';

interface Props {
onFilterGroupChanged: (filterGroup: AlertFilterOption) => void;
onFilterGroupChanged: (filterGroup: Status) => void;
}

const AlertsTableFilterGroupComponent: React.FC<Props> = ({ onFilterGroupChanged }) => {
const [filterGroup, setFilterGroup] = useState(FILTER_OPEN);
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);

const onClickOpenFilterCallback = useCallback(() => {
setFilterGroup(FILTER_OPEN);
Expand All @@ -29,6 +30,11 @@ const AlertsTableFilterGroupComponent: React.FC<Props> = ({ onFilterGroupChanged
onFilterGroupChanged(FILTER_CLOSED);
}, [setFilterGroup, onFilterGroupChanged]);

const onClickInProgressFilterCallback = useCallback(() => {
setFilterGroup(FILTER_IN_PROGRESS);
onFilterGroupChanged(FILTER_IN_PROGRESS);
}, [setFilterGroup, onFilterGroupChanged]);

return (
<EuiFilterGroup>
<EuiFilterButton
Expand All @@ -40,6 +46,15 @@ const AlertsTableFilterGroupComponent: React.FC<Props> = ({ onFilterGroupChanged
{i18n.OPEN_ALERTS}
</EuiFilterButton>

<EuiFilterButton
data-test-subj="inProgressAlerts"
hasActiveFilters={filterGroup === FILTER_IN_PROGRESS}
onClick={onClickInProgressFilterCallback}
withNext
>
{i18n.IN_PROGRESS_ALERTS}
</EuiFilterButton>

<EuiFilterButton
data-test-subj="closedAlerts"
hasActiveFilters={filterGroup === FILTER_CLOSED}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ describe('AlertsUtilityBar', () => {
clearSelection={jest.fn()}
totalCount={100}
selectedEventIds={{}}
isFilteredToOpen={false}
currentFilter="closed"
selectAll={jest.fn()}
showClearSelection={true}
updateAlertsStatus={jest.fn()}
/>
);

expect(wrapper.find('[dataTestSubj="openCloseAlert"]')).toBeTruthy();
expect(wrapper.find('[dataTestSubj="alertActionPopover"]')).toBeTruthy();
});
});
Loading

0 comments on commit e3ba5e5

Please sign in to comment.