[Security Solution][Detection Alerts] Changes in-progress status to acknowledged (#107972)

This commit is contained in:
Davis Plumlee 2021-08-18 02:12:16 -04:00 committed by GitHub
parent 2908ecef3b
commit fc1a2bbd1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 202 additions and 160 deletions

View file

@ -170,7 +170,12 @@ export type RuleNameOverride = t.TypeOf<typeof rule_name_override>;
export const ruleNameOverrideOrUndefined = t.union([rule_name_override, t.undefined]);
export type RuleNameOverrideOrUndefined = t.TypeOf<typeof ruleNameOverrideOrUndefined>;
export const status = t.keyof({ open: null, closed: null, 'in-progress': null });
export const status = t.keyof({
open: null,
closed: null,
acknowledged: null,
'in-progress': null, // TODO: Remove after `acknowledged` migrations
});
export type Status = t.TypeOf<typeof status>;
export enum RuleExecutionStatus {

View file

@ -13,8 +13,8 @@ import {
waitForAlertsPanelToBeLoaded,
waitForAlerts,
waitForAlertsToBeLoaded,
markInProgressFirstAlert,
goToInProgressAlerts,
markAcknowledgedFirstAlert,
goToAcknowledgedAlerts,
waitForAlertsIndexToBeCreated,
goToOpenedAlerts,
} from '../../tasks/alerts';
@ -26,7 +26,7 @@ import { refreshPage } from '../../tasks/security_header';
import { ALERTS_URL } from '../../urls/navigation';
describe('Marking alerts as in-progress', () => {
describe('Marking alerts as acknowledged', () => {
beforeEach(() => {
cleanKibana();
loginAndWaitForPage(ALERTS_URL);
@ -37,30 +37,30 @@ describe('Marking alerts as in-progress', () => {
waitForAlertsToPopulate(500);
});
it('Mark one alert in progress when more than one open alerts are selected', () => {
it('Mark one alert as acknowledged when more than one open alerts are selected', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
const numberOfAlertsToBeMarkedInProgress = 1;
const numberOfAlertsToBeMarkedAcknowledged = 1;
const numberOfAlertsToBeSelected = 3;
cy.get(TAKE_ACTION_POPOVER_BTN).should('not.exist');
selectNumberOfAlerts(numberOfAlertsToBeSelected);
cy.get(TAKE_ACTION_POPOVER_BTN).should('exist');
markInProgressFirstAlert();
markAcknowledgedFirstAlert();
refreshPage();
waitForAlertsToBeLoaded();
goToOpenedAlerts();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress;
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedAcknowledged;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alerts`);
goToInProgressAlerts();
goToAcknowledgedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlertsToBeMarkedInProgress} alert`);
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlertsToBeMarkedAcknowledged} alert`);
});
});
});

View file

@ -51,7 +51,7 @@ import {
} from '../../screens/kibana_navigation';
import { cleanKibana } from '../../tasks/common';
describe('top-level navigation common to all pages in the Security app', () => {
describe.skip('top-level navigation common to all pages in the Security app', () => {
before(() => {
cleanKibana();
loginAndWaitForPage(TIMELINES_URL);
@ -111,7 +111,7 @@ describe('top-level navigation common to all pages in the Security app', () => {
});
});
describe('Kibana navigation to all pages in the Security app ', () => {
describe.skip('Kibana navigation to all pages in the Security app ', () => {
before(() => {
loginAndWaitForPage(KIBANA_HOME);
});

View file

@ -39,16 +39,16 @@ export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]';
export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]';
export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]';
export const ACKNOWLEDGED_ALERTS_FILTER_BTN = '[data-test-subj="acknowledgedAlerts"]';
export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]';
export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-detection-rules"]';
export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]';
export const MARK_ALERT_ACKNOWLEDGED_BTN = '[data-test-subj="acknowledged-alert-status"]';
export const MARK_SELECTED_ALERTS_IN_PROGRESS_BTN =
'[data-test-subj="markSelectedAlertsInProgressButton"]';
export const MARK_SELECTED_ALERTS_ACKNOWLEDGED_BTN =
'[data-test-subj="markSelectedAlertsAcknowledgedButton"]';
export const NUMBER_OF_ALERTS =
'[data-test-subj="events-viewer-panel"] [data-test-subj="server-side-event-count"]';

View file

@ -13,11 +13,11 @@ import {
CLOSE_SELECTED_ALERTS_BTN,
CLOSED_ALERTS_FILTER_BTN,
EXPAND_ALERT_BTN,
IN_PROGRESS_ALERTS_FILTER_BTN,
ACKNOWLEDGED_ALERTS_FILTER_BTN,
LOADING_ALERTS_PANEL,
MANAGE_ALERT_DETECTION_RULES_BTN,
MARK_ALERT_IN_PROGRESS_BTN,
MARK_SELECTED_ALERTS_IN_PROGRESS_BTN,
MARK_ALERT_ACKNOWLEDGED_BTN,
MARK_SELECTED_ALERTS_ACKNOWLEDGED_BTN,
OPEN_ALERT_BTN,
OPENED_ALERTS_FILTER_BTN,
SEND_ALERT_TO_TIMELINE_BTN,
@ -112,18 +112,21 @@ export const openAlerts = () => {
cy.get(OPEN_ALERT_BTN).click();
};
export const goToInProgressAlerts = () => {
cy.get(IN_PROGRESS_ALERTS_FILTER_BTN).click();
export const goToAcknowledgedAlerts = () => {
cy.get(ACKNOWLEDGED_ALERTS_FILTER_BTN).click();
cy.get(REFRESH_BUTTON).should('not.have.text', 'Updating');
cy.get(REFRESH_BUTTON).should('have.text', 'Refresh');
cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist');
};
export const markInProgressFirstAlert = () => {
export const markAcknowledgedFirstAlert = () => {
cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true });
cy.get(MARK_ALERT_IN_PROGRESS_BTN).click();
cy.get(MARK_ALERT_ACKNOWLEDGED_BTN).click();
};
export const markInProgressAlerts = () => {
export const markAcknowledgedAlerts = () => {
cy.get(TAKE_ACTION_POPOVER_BTN).click({ force: true });
cy.get(MARK_SELECTED_ALERTS_IN_PROGRESS_BTN).click();
cy.get(MARK_SELECTED_ALERTS_ACKNOWLEDGED_BTN).click();
};
export const selectNumberOfAlerts = (numberOfAlerts: number) => {

View file

@ -337,7 +337,9 @@ describe('EventsViewer', () => {
<EventsViewer
{...eventsViewerDefaultProps}
graphEventId={undefined}
headerFilterGroup={<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />}
headerFilterGroup={
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
}
/>
</TestProviders>
);
@ -350,7 +352,9 @@ describe('EventsViewer', () => {
<EventsViewer
{...eventsViewerDefaultProps}
graphEventId={undefined}
headerFilterGroup={<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />}
headerFilterGroup={
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
}
/>
</TestProviders>
);
@ -365,7 +369,9 @@ describe('EventsViewer', () => {
<EventsViewer
{...eventsViewerDefaultProps}
graphEventId=""
headerFilterGroup={<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />}
headerFilterGroup={
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
}
/>
</TestProviders>
);
@ -380,7 +386,9 @@ describe('EventsViewer', () => {
<EventsViewer
{...eventsViewerDefaultProps}
graphEventId="a valid id"
headerFilterGroup={<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />}
headerFilterGroup={
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
}
/>
</TestProviders>
);
@ -395,7 +403,9 @@ describe('EventsViewer', () => {
<EventsViewer
{...eventsViewerDefaultProps}
graphEventId="a valid id"
headerFilterGroup={<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />}
headerFilterGroup={
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
}
/>
</TestProviders>
);

View file

@ -12,7 +12,9 @@ import { AlertsTableFilterGroup } from './index';
describe('AlertsTableFilterGroup', () => {
it('renders correctly', () => {
const wrapper = shallow(<AlertsTableFilterGroup onFilterGroupChanged={jest.fn()} />);
const wrapper = shallow(
<AlertsTableFilterGroup status={'open'} onFilterGroupChanged={jest.fn()} />
);
expect(wrapper.find('EuiFilterButton')).toBeTruthy();
});

View file

@ -5,84 +5,58 @@
* 2.0.
*/
import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui';
import { rgba } from 'polished';
import React, { useCallback, useState } from 'react';
import styled from 'styled-components';
import { EuiButtonGroup, EuiButtonGroupOptionProps } from '@elastic/eui';
import React, { useCallback } from 'react';
import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import * as i18n from '../translations';
export const FILTER_OPEN: Status = 'open';
export const FILTER_CLOSED: Status = 'closed';
export const FILTER_IN_PROGRESS: Status = 'in-progress';
const StatusFilterButton = styled(EuiFilterButton)<{ $isActive: boolean }>`
background: ${({ $isActive, theme }) => ($isActive ? theme.eui.euiColorPrimary : '')};
`;
const StatusFilterGroup = styled(EuiFilterGroup)`
background: ${({ theme }) => rgba(theme.eui.euiColorPrimary, 0.2)};
.euiButtonEmpty--ghost:enabled:focus {
background-color: ${({ theme }) => theme.eui.euiColorPrimary};
}
`;
export const FILTER_ACKNOWLEDGED: Status = 'acknowledged';
interface Props {
status: Status;
onFilterGroupChanged: (filterGroup: Status) => void;
}
const AlertsTableFilterGroupComponent: React.FC<Props> = ({ onFilterGroupChanged }) => {
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);
const AlertsTableFilterGroupComponent: React.FC<Props> = ({
status = FILTER_OPEN,
onFilterGroupChanged,
}) => {
const options: EuiButtonGroupOptionProps[] = [
{
id: 'open',
label: i18n.OPEN_ALERTS,
'data-test-subj': 'openAlerts',
},
{
id: 'acknowledged',
label: i18n.ACKNOWLEDGED_ALERTS,
'data-test-subj': 'acknowledgedAlerts',
},
{
id: 'closed',
label: i18n.CLOSED_ALERTS,
'data-test-subj': 'closedAlerts',
},
];
const onClickOpenFilterCallback = useCallback(() => {
setFilterGroup(FILTER_OPEN);
onFilterGroupChanged(FILTER_OPEN);
}, [setFilterGroup, onFilterGroupChanged]);
const onClickCloseFilterCallback = useCallback(() => {
setFilterGroup(FILTER_CLOSED);
onFilterGroupChanged(FILTER_CLOSED);
}, [setFilterGroup, onFilterGroupChanged]);
const onClickInProgressFilterCallback = useCallback(() => {
setFilterGroup(FILTER_IN_PROGRESS);
onFilterGroupChanged(FILTER_IN_PROGRESS);
}, [setFilterGroup, onFilterGroupChanged]);
const onChange = useCallback(
(id: string) => {
onFilterGroupChanged(id as Status);
},
[onFilterGroupChanged]
);
return (
<StatusFilterGroup data-test-subj="alerts-table-filter-group">
<StatusFilterButton
data-test-subj="openAlerts"
hasActiveFilters={filterGroup === FILTER_OPEN}
$isActive={filterGroup === FILTER_OPEN}
onClick={onClickOpenFilterCallback}
withNext
color={filterGroup === FILTER_OPEN ? 'ghost' : 'primary'}
>
{i18n.OPEN_ALERTS}
</StatusFilterButton>
<StatusFilterButton
data-test-subj="inProgressAlerts"
hasActiveFilters={filterGroup === FILTER_IN_PROGRESS}
$isActive={filterGroup === FILTER_IN_PROGRESS}
onClick={onClickInProgressFilterCallback}
withNext
color={filterGroup === FILTER_IN_PROGRESS ? 'ghost' : 'primary'}
>
{i18n.IN_PROGRESS_ALERTS}
</StatusFilterButton>
<StatusFilterButton
data-test-subj="closedAlerts"
hasActiveFilters={filterGroup === FILTER_CLOSED}
$isActive={filterGroup === FILTER_CLOSED}
onClick={onClickCloseFilterCallback}
color={filterGroup === FILTER_CLOSED ? 'ghost' : 'primary'}
>
{i18n.CLOSED_ALERTS}
</StatusFilterButton>
</StatusFilterGroup>
<EuiButtonGroup
legend="filter status"
color="primary"
options={options}
idSelected={status}
data-test-subj="alerts-table-filter-group"
onChange={onChange}
/>
);
};

View file

@ -27,7 +27,7 @@ import * as i18n from './translations';
import { useUiSetting$ } from '../../../../common/lib/kibana';
import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline';
import { UpdateAlertsStatus } from '../types';
import { FILTER_CLOSED, FILTER_IN_PROGRESS, FILTER_OPEN } from '../alerts_filter_group';
import { FILTER_CLOSED, FILTER_ACKNOWLEDGED, FILTER_OPEN } from '../alerts_filter_group';
export interface AlertsUtilityBarProps {
areEventsLoading: boolean;
@ -126,18 +126,18 @@ const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
</EuiFlexItem>
)}
{currentFilter !== FILTER_IN_PROGRESS && (
{currentFilter !== FILTER_ACKNOWLEDGED && (
<EuiFlexItem>
<Link
aria-label="markSelectedAlertsInProgress"
aria-label="markSelectedAlertsAcknowledged"
onClick={() => {
closePopover();
handleUpdateStatus('in-progress');
handleUpdateStatus('acknowledged');
}}
color="text"
data-test-subj="markSelectedAlertsInProgressButton"
data-test-subj="markSelectedAlertsAcknowledgedButton"
>
{i18n.BATCH_ACTION_IN_PROGRESS_SELECTED}
{i18n.BATCH_ACTION_ACKNOWLEDGED_SELECTED}
</Link>
</EuiFlexItem>
)}

View file

@ -105,9 +105,9 @@ export const BATCH_ACTION_CLOSE_SELECTED = i18n.translate(
}
);
export const BATCH_ACTION_IN_PROGRESS_SELECTED = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.inProgressSelectedTitle',
export const BATCH_ACTION_ACKNOWLEDGED_SELECTED = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.acknowledgedSelectedTitle',
{
defaultMessage: 'Mark in progress',
defaultMessage: 'Mark as acknowledged',
}
);

View file

@ -174,7 +174,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
title = i18n.OPENED_ALERT_SUCCESS_TOAST(updated);
break;
case 'in-progress':
title = i18n.IN_PROGRESS_ALERT_SUCCESS_TOAST(updated);
case 'acknowledged':
title = i18n.ACKNOWLEDGED_ALERT_SUCCESS_TOAST(updated);
}
displaySuccessToast(title, dispatchToaster);
}
@ -193,7 +194,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
title = i18n.OPENED_ALERT_FAILED_TOAST;
break;
case 'in-progress':
title = i18n.IN_PROGRESS_ALERT_FAILED_TOAST;
case 'acknowledged':
title = i18n.ACKNOWLEDGED_ALERT_FAILED_TOAST;
}
displayErrorToast(title, [error.message], dispatchToaster);
},

View file

@ -7,30 +7,30 @@
import { EuiContextMenuItem } from '@elastic/eui';
import React from 'react';
import { FILTER_IN_PROGRESS } from '../../alerts_filter_group';
import { FILTER_ACKNOWLEDGED } from '../../alerts_filter_group';
import * as i18n from '../../translations';
interface InProgressAlertStatusProps {
interface AcknowledgedAlertStatusProps {
onClick: () => void;
disabled?: boolean;
}
const InProgressAlertStatusComponent: React.FC<InProgressAlertStatusProps> = ({
const AcknowledgedAlertStatusComponent: React.FC<AcknowledgedAlertStatusProps> = ({
onClick,
disabled,
}) => {
return (
<EuiContextMenuItem
key="in-progress-alert"
aria-label={i18n.ACTION_IN_PROGRESS_ALERT}
data-test-subj="in-progress-alert-status"
id={FILTER_IN_PROGRESS}
key="acknowledged-alert"
aria-label={i18n.ACTION_ACKNOWLEDGED_ALERT}
data-test-subj="acknowledged-alert-status"
id={FILTER_ACKNOWLEDGED}
onClick={onClick}
disabled={disabled}
>
{i18n.ACTION_IN_PROGRESS_ALERT}
{i18n.ACTION_ACKNOWLEDGED_ALERT}
</EuiContextMenuItem>
);
};
export const InProgressAlertStatus = React.memo(InProgressAlertStatusComponent);
export const AcknowledgedAlertStatus = React.memo(AcknowledgedAlertStatusComponent);

View file

@ -38,10 +38,10 @@ export const CLOSED_ALERTS = i18n.translate(
}
);
export const IN_PROGRESS_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.inProgressAlertsTitle',
export const ACKNOWLEDGED_ALERTS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertsTitle',
{
defaultMessage: 'In progress',
defaultMessage: 'Acknowledged',
}
);
@ -150,10 +150,10 @@ export const ACTION_CLOSE_ALERT = i18n.translate(
}
);
export const ACTION_IN_PROGRESS_ALERT = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.actions.inProgressAlertTitle',
export const ACTION_ACKNOWLEDGED_ALERT = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.actions.acknowledgedAlertTitle',
{
defaultMessage: 'Mark in progress',
defaultMessage: 'Mark as acknowledged',
}
);
@ -220,13 +220,13 @@ export const OPENED_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
'Successfully opened {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}}.',
});
export const IN_PROGRESS_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
export const ACKNOWLEDGED_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.inProgressAlertSuccessToastMessage',
'xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertSuccessToastMessage',
{
values: { totalAlerts },
defaultMessage:
'Successfully marked {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}} as in progress.',
'Successfully marked {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}} as acknowledged.',
}
);
@ -244,10 +244,10 @@ export const OPENED_ALERT_FAILED_TOAST = i18n.translate(
}
);
export const IN_PROGRESS_ALERT_FAILED_TOAST = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.inProgressAlertFailedToastMessage',
export const ACKNOWLEDGED_ALERT_FAILED_TOAST = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.acknowledgedAlertFailedToastMessage',
{
defaultMessage: 'Failed to mark alert(s) as in progress',
defaultMessage: 'Failed to mark alert(s) as acknowledged',
}
);

View file

@ -77,7 +77,7 @@ export const fetchQueryRuleRegistryAlerts = async <Hit, Aggregations>({
* Update alert status by query
*
* @param query of alerts to update
* @param status to update to('open' / 'closed' / 'in-progress')
* @param status to update to('open' / 'closed' / 'acknowledged')
* @param signal AbortSignal for cancelling request
*
* @throws An error if response is not OK

View file

@ -297,7 +297,10 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
<EuiHorizontalRule margin="m" />
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<AlertsTableFilterGroup onFilterGroupChanged={onFilterGroupChangedCallback} />
<AlertsTableFilterGroup
status={filterGroup}
onFilterGroupChanged={onFilterGroupChangedCallback}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{timelinesUi.getLastUpdated({ updatedAt: updatedAt || 0, showUpdating: loading })}

View file

@ -754,7 +754,10 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
<>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<AlertsTableFilterGroup onFilterGroupChanged={onFilterGroupChangedCallback} />
<AlertsTableFilterGroup
status={filterGroup}
onFilterGroupChanged={onFilterGroupChangedCallback}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{timelinesUi.getLastUpdated({

View file

@ -14,7 +14,7 @@ import { DefaultDraggable } from '../../../../../common/components/draggables';
const mapping = {
open: 'primary',
'in-progress': 'warning',
acknowledged: 'warning',
closed: 'default',
};

View file

@ -71,11 +71,15 @@ export const createMigration = async ({
enrichment.indicator.reference = indicator.event?.reference;
enrichment.matched = indicator.matched;
enrichment.indicator.remove("matched");
ctx._source.threat.enrichments.add(enrichment);
}
ctx._source.threat.remove("indicator");
}
// migrate status
if(ctx._source.signal?.status == "in-progress") {
ctx._source.signal.status = "acknowledged";
}
`,
params: {
version,

View file

@ -12,6 +12,12 @@ export const DEFAULT_NUMBER_FORMAT = 'format:number:defaultPattern';
export const FILTER_OPEN: AlertStatus = 'open';
export const FILTER_CLOSED: AlertStatus = 'closed';
/**
* @deprecated
* TODO: Remove after `acknowledged` migration
*/
export const FILTER_IN_PROGRESS: AlertStatus = 'in-progress';
export const FILTER_ACKNOWLEDGED: AlertStatus = 'acknowledged';
export const RAC_ALERTS_BULK_UPDATE_URL = '/internal/rac/alerts/bulk_update';

View file

@ -118,4 +118,10 @@ export interface BulkActionsObjectProp {
}
export type BulkActionsProp = boolean | BulkActionsObjectProp;
export type AlertStatus = 'open' | 'closed' | 'in-progress';
/**
* @deprecated
* TODO: remove when `acknowledged` migrations are finished
*/
export type InProgressStatus = 'in-progress';
export type AlertStatus = 'open' | 'closed' | 'acknowledged' | InProgressStatus;

View file

@ -45,10 +45,10 @@ export const BULK_ACTION_CLOSE_SELECTED = i18n.translate(
}
);
export const BULK_ACTION_IN_PROGRESS_SELECTED = i18n.translate(
'xpack.timelines.timeline.inProgressSelectedTitle',
export const BULK_ACTION_ACKNOWLEDGED_SELECTED = i18n.translate(
'xpack.timelines.timeline.acknowledgedSelectedTitle',
{
defaultMessage: 'Mark in progress',
defaultMessage: 'Mark as acknowledged',
}
);
@ -73,11 +73,11 @@ export const OPENED_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
'Successfully opened {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}}.',
});
export const IN_PROGRESS_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
i18n.translate('xpack.timelines.timeline.inProgressAlertSuccessToastMessage', {
export const ACKNOWLEDGED_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
i18n.translate('xpack.timelines.timeline.acknowledgedAlertSuccessToastMessage', {
values: { totalAlerts },
defaultMessage:
'Successfully marked {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}} as in progress.',
'Successfully marked {totalAlerts} {totalAlerts, plural, =1 {alert} other {alerts}} as acknowledged.',
});
export const CLOSED_ALERT_FAILED_TOAST = i18n.translate(
@ -94,10 +94,10 @@ export const OPENED_ALERT_FAILED_TOAST = i18n.translate(
}
);
export const IN_PROGRESS_ALERT_FAILED_TOAST = i18n.translate(
'xpack.timelines.timeline.inProgressAlertFailedToastMessage',
export const ACKNOWLEDGED_ALERT_FAILED_TOAST = i18n.translate(
'xpack.timelines.timeline.acknowledgedAlertFailedToastMessage',
{
defaultMessage: 'Failed to mark alert(s) as in progress',
defaultMessage: 'Failed to mark alert(s) as acknowledged',
}
);

View file

@ -7,7 +7,7 @@
import React, { useMemo, useCallback } from 'react';
import { EuiContextMenuItem } from '@elastic/eui';
import { FILTER_CLOSED, FILTER_IN_PROGRESS, FILTER_OPEN } from '../../common/constants';
import { FILTER_CLOSED, FILTER_ACKNOWLEDGED, FILTER_OPEN } from '../../common/constants';
import * as i18n from '../components/t_grid/translations';
import type { AlertStatus, StatusBulkActionsProps } from '../../common/types/timeline';
import { useUpdateAlertsStatus } from '../container/use_update_alerts';
@ -48,7 +48,8 @@ export const useStatusBulkActionItems = ({
title = i18n.OPENED_ALERT_SUCCESS_TOAST(updated);
break;
case 'in-progress':
title = i18n.IN_PROGRESS_ALERT_SUCCESS_TOAST(updated);
case 'acknowledged':
title = i18n.ACKNOWLEDGED_ALERT_SUCCESS_TOAST(updated);
}
addSuccess({ title });
}
@ -70,7 +71,8 @@ export const useStatusBulkActionItems = ({
title = i18n.OPENED_ALERT_FAILED_TOAST;
break;
case 'in-progress':
title = i18n.IN_PROGRESS_ALERT_FAILED_TOAST;
case 'acknowledged':
title = i18n.ACKNOWLEDGED_ALERT_FAILED_TOAST;
}
addError(error.message, { title });
if (onUpdateFailure) {
@ -131,15 +133,15 @@ export const useStatusBulkActionItems = ({
</EuiContextMenuItem>
);
}
if (currentStatus !== FILTER_IN_PROGRESS) {
if (currentStatus !== FILTER_ACKNOWLEDGED) {
actionItems.push(
<EuiContextMenuItem
key="progress"
data-test-subj="in-progress-alert-status"
onClick={() => onClickUpdate(FILTER_IN_PROGRESS)}
key="acknowledge"
data-test-subj="acknowledged-alert-status"
onClick={() => onClickUpdate(FILTER_ACKNOWLEDGED)}
size="s"
>
{i18n.BULK_ACTION_IN_PROGRESS_SELECTED}
{i18n.BULK_ACTION_ACKNOWLEDGED_SELECTED}
</EuiContextMenuItem>
);
}

View file

@ -20239,7 +20239,6 @@
"xpack.securitySolution.detectionEngine.alerts.actions.addEventFilter": "エンドポイントイベントフィルターを追加",
"xpack.securitySolution.detectionEngine.alerts.actions.addException": "ルール例外の追加",
"xpack.securitySolution.detectionEngine.alerts.actions.closeAlertTitle": "アラートを閉じる",
"xpack.securitySolution.detectionEngine.alerts.actions.inProgressAlertTitle": "実行中に設定",
"xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineAriaLabel": "アラートをタイムラインに送信",
"xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineTitle": "タイムラインで調査",
"xpack.securitySolution.detectionEngine.alerts.actions.openAlertTitle": "アラートを開く",
@ -20251,8 +20250,6 @@
"xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.stackByLabel": "積み上げ",
"xpack.securitySolution.detectionEngine.alerts.histogram.topNLabel": "トップ{fieldName}",
"xpack.securitySolution.detectionEngine.alerts.histogram.viewAlertsButtonLabel": "アラートを表示",
"xpack.securitySolution.detectionEngine.alerts.inProgressAlertFailedToastMessage": "アラートを実行中に設定できませんでした",
"xpack.securitySolution.detectionEngine.alerts.inProgressAlertsTitle": "進行中",
"xpack.securitySolution.detectionEngine.alerts.loadingAlertsTitle": "アラートを読み込んでいます",
"xpack.securitySolution.detectionEngine.alerts.moreActionsAriaLabel": "さらにアクションを表示",
"xpack.securitySolution.detectionEngine.alerts.openAlertsTitle": "開く",
@ -20263,7 +20260,6 @@
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts": "脅威インジケーターアラートのみを表示",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle": "追加のフィルター",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle": "選択した項目を閉じる",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.inProgressSelectedTitle": "実行中に設定",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle": "選択した項目を開く",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.viewSelectedInHostsTitle": "ホストで選択した項目を表示",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.viewSelectedInNetworkTitle": "ネットワークで選択した項目を表示",

View file

@ -20686,7 +20686,6 @@
"xpack.securitySolution.detectionEngine.alerts.actions.addEventFilter": "添加终端事件筛选",
"xpack.securitySolution.detectionEngine.alerts.actions.addException": "添加规则例外",
"xpack.securitySolution.detectionEngine.alerts.actions.closeAlertTitle": "关闭告警",
"xpack.securitySolution.detectionEngine.alerts.actions.inProgressAlertTitle": "标记为进行中",
"xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineAriaLabel": "将告警发送到时间线",
"xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineTitle": "在时间线中调查",
"xpack.securitySolution.detectionEngine.alerts.actions.openAlertTitle": "打开告警",
@ -20700,9 +20699,6 @@
"xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.stackByLabel": "堆叠依据",
"xpack.securitySolution.detectionEngine.alerts.histogram.topNLabel": "排名靠前的{fieldName}",
"xpack.securitySolution.detectionEngine.alerts.histogram.viewAlertsButtonLabel": "查看告警",
"xpack.securitySolution.detectionEngine.alerts.inProgressAlertFailedToastMessage": "无法将告警标记为进行中",
"xpack.securitySolution.detectionEngine.alerts.inProgressAlertsTitle": "进行中",
"xpack.securitySolution.detectionEngine.alerts.inProgressAlertSuccessToastMessage": "已成功将 {totalAlerts} 个{totalAlerts, plural, other {告警}}标记为进行中。",
"xpack.securitySolution.detectionEngine.alerts.loadingAlertsTitle": "正在加载告警",
"xpack.securitySolution.detectionEngine.alerts.moreActionsAriaLabel": "更多操作",
"xpack.securitySolution.detectionEngine.alerts.openAlertsTitle": "打开",
@ -20714,7 +20710,6 @@
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showOnlyThreatIndicatorAlerts": "仅显示威胁指标告警",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle": "其他筛选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.closeSelectedTitle": "关闭所选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.inProgressSelectedTitle": "标记为进行中",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.openSelectedTitle": "打开所选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.viewSelectedInHostsTitle": "查看主机中所选",
"xpack.securitySolution.detectionEngine.alerts.utilityBar.batchActions.viewSelectedInNetworkTitle": "查看网络中所选",

View file

@ -145,6 +145,37 @@ export default ({ getService }: FtrProviderContext) => {
);
expect(everySignalClosed).to.eql(true);
});
it('should be able mark 10 signals as acknowledged immediately and they all should be acknowledged', async () => {
const rule = getRuleForSignalTesting(['auditbeat-*']);
const { id } = await createRule(supertest, rule);
await waitForRuleSuccessOrStatus(supertest, id);
await waitForSignalsToBePresent(supertest, 10, [id]);
const signalsOpen = await getSignalsByIds(supertest, [id]);
const signalIds = signalsOpen.hits.hits.map((signal) => signal._id);
// set all of the signals to the state of acknowledged. There is no reason to use a waitUntil here
// as this route intentionally has a waitFor within it and should only return when the query has
// the data.
await supertest
.post(RAC_ALERTS_BULK_UPDATE_URL)
.set('kbn-xsrf', 'true')
.send({ ids: signalIds, status: 'acknowledged', index: '.siem-signals-default' })
.expect(200);
const {
body: acknowledgedSignals,
}: { body: estypes.SearchResponse<{ signal: Signal }> } = await supertest
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
.set('kbn-xsrf', 'true')
.send(getQuerySignalIds(signalIds))
.expect(200);
const everyAcknowledgedSignal = acknowledgedSignals.hits.hits.every(
(hit) => hit._source?.signal?.status === 'acknowledged'
);
expect(everyAcknowledgedSignal).to.eql(true);
});
});
});
};