[Security Solution][Detection Alerts] Fixes follow-up alert refresh bugs (#112169)

This commit is contained in:
Davis Plumlee 2021-10-12 09:40:54 -05:00 committed by GitHub
parent f50e3f5cce
commit 0b46bb1b93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 545 additions and 442 deletions

View file

@ -6,7 +6,11 @@
*/
import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT, TAKE_ACTION_POPOVER_BTN } from '../../screens/alerts';
import {
ALERTS_COUNT,
TAKE_ACTION_POPOVER_BTN,
ALERT_COUNT_TABLE_FIRST_ROW_COUNT,
} from '../../screens/alerts';
import {
selectNumberOfAlerts,
@ -50,11 +54,16 @@ describe('Marking alerts as acknowledged', () => {
markAcknowledgedFirstAlert();
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedAcknowledged;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should('have.text', `${expectedNumberOfAlerts}`);
goToAcknowledgedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlertsToBeMarkedAcknowledged} alert`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${numberOfAlertsToBeMarkedAcknowledged}`
);
});
});
});

View file

@ -6,7 +6,13 @@
*/
import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT, SELECTED_ALERTS, TAKE_ACTION_POPOVER_BTN } from '../../screens/alerts';
import {
ALERTS_COUNT,
SELECTED_ALERTS,
TAKE_ACTION_POPOVER_BTN,
ALERT_COUNT_TABLE_FIRST_ROW_COUNT,
ALERTS_TREND_SIGNAL_RULE_NAME_PANEL,
} from '../../screens/alerts';
import {
closeFirstAlert,
@ -46,6 +52,7 @@ describe('Closing alerts', () => {
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should('have.text', `${numberOfAlerts}`);
selectNumberOfAlerts(numberOfAlertsToBeClosed);
@ -56,6 +63,10 @@ describe('Closing alerts', () => {
const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlertsAfterClosing} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${expectedNumberOfAlertsAfterClosing}`
);
goToClosedAlerts();
waitForAlerts();
@ -75,6 +86,10 @@ describe('Closing alerts', () => {
'have.text',
`${expectedNumberOfClosedAlertsAfterOpened} alerts`
);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${expectedNumberOfClosedAlertsAfterOpened}`
);
goToOpenedAlerts();
waitForAlerts();
@ -83,6 +98,10 @@ describe('Closing alerts', () => {
+numberOfAlerts - expectedNumberOfClosedAlertsAfterOpened;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfOpenedAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${expectedNumberOfOpenedAlerts}`
);
});
});
@ -103,11 +122,59 @@ describe('Closing alerts', () => {
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeClosed;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should('have.text', `${expectedNumberOfAlerts}`);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlertsToBeClosed} alert`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${numberOfAlertsToBeClosed}`
);
});
});
it('Updates trend histogram whenever alert status is updated in table', () => {
const numberOfAlertsToBeClosed = 1;
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
const numberOfAlerts = alertNumberString.split(' ')[0];
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should('have.text', `${numberOfAlerts}`);
selectNumberOfAlerts(numberOfAlertsToBeClosed);
cy.get(SELECTED_ALERTS).should('have.text', `Selected ${numberOfAlertsToBeClosed} alert`);
closeAlerts();
waitForAlerts();
const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlertsAfterClosing} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${expectedNumberOfAlertsAfterClosing}`
);
goToClosedAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).should('have.text', `${numberOfAlertsToBeClosed} alert`);
const numberOfAlertsToBeOpened = 1;
selectNumberOfAlerts(numberOfAlertsToBeOpened);
cy.get(SELECTED_ALERTS).should('have.text', `Selected ${numberOfAlertsToBeOpened} alert`);
cy.get(ALERTS_TREND_SIGNAL_RULE_NAME_PANEL).should('exist');
openAlerts();
waitForAlerts();
cy.get(ALERTS_COUNT).should('not.exist');
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should('not.exist');
cy.get(ALERTS_TREND_SIGNAL_RULE_NAME_PANEL).should('not.exist');
});
});
});

View file

@ -6,7 +6,12 @@
*/
import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT, SELECTED_ALERTS, TAKE_ACTION_POPOVER_BTN } from '../../screens/alerts';
import {
ALERTS_COUNT,
SELECTED_ALERTS,
TAKE_ACTION_POPOVER_BTN,
ALERT_COUNT_TABLE_FIRST_ROW_COUNT,
} from '../../screens/alerts';
import {
closeAlerts,
@ -74,6 +79,10 @@ describe('Opening alerts', () => {
const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened;
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alerts`);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${expectedNumberOfAlerts}`
);
goToOpenedAlerts();
waitForAlerts();
@ -82,6 +91,10 @@ describe('Opening alerts', () => {
'have.text',
`${numberOfOpenedAlerts + numberOfAlertsToBeOpened} alerts`.toString()
);
cy.get(ALERT_COUNT_TABLE_FIRST_ROW_COUNT).should(
'have.text',
`${numberOfOpenedAlerts + numberOfAlertsToBeOpened}`
);
});
});
});

View file

@ -70,3 +70,9 @@ export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="selectedShowBulkActions
export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]';
export const ATTACH_ALERT_TO_CASE_BUTTON = '[data-test-subj="attach-alert-to-case-button"]';
export const ALERT_COUNT_TABLE_FIRST_ROW_COUNT =
'[data-test-subj="alertsCountTable"] tr:nth-child(1) td:nth-child(2) .euiTableCellContent__text';
export const ALERTS_TREND_SIGNAL_RULE_NAME_PANEL =
'[data-test-subj="render-content-signal.rule.name"]';

View file

@ -12,6 +12,7 @@ import { indexOf } from 'lodash';
import { connect, ConnectedProps } from 'react-redux';
import { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types';
import { get } from 'lodash/fp';
import { useRouteSpy } from '../../../../common/utils/route/use_route_spy';
import { buildGetAlertByIdQuery } from '../../../../common/components/exceptions/helpers';
import { EventsTdContent } from '../../../../timelines/components/timeline/styles';
import { DEFAULT_ICON_BUTTON_WIDTH } from '../../../../timelines/components/timeline/helpers';
@ -63,6 +64,7 @@ const AlertContextMenuComponent: React.FC<AlertContextMenuProps & PropsFromRedux
timelineQuery,
}) => {
const [isPopoverOpen, setPopover] = useState(false);
const [routeProps] = useRouteSpy();
const afterItemSelection = useCallback(() => {
setPopover(false);
@ -112,10 +114,13 @@ const AlertContextMenuComponent: React.FC<AlertContextMenuProps & PropsFromRedux
const refetchAll = useCallback(() => {
if (timelineId === TimelineId.active) {
refetchQuery([timelineQuery]);
if (routeProps.pageName === 'alerts') {
refetchQuery(globalQuery);
}
} else {
refetchQuery(globalQuery);
}
}, [timelineId, globalQuery, timelineQuery]);
}, [timelineId, globalQuery, timelineQuery, routeProps]);
const {
exceptionModalType,

View file

@ -9,8 +9,7 @@ import React, { useState, useCallback, useMemo } from 'react';
import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui';
import type { ExceptionListType } from '@kbn/securitysolution-io-ts-list-types';
import { isEmpty } from 'lodash/fp';
import { connect, ConnectedProps } from 'react-redux';
import { TimelineEventsDetailsItem, TimelineId } from '../../../../common';
import { TimelineEventsDetailsItem } from '../../../../common';
import { TAKE_ACTION } from '../alerts_table/alerts_utility_bar/translations';
import { useExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions';
import { useAlertsActions } from '../alerts_table/timeline_actions/use_alerts_actions';
@ -24,8 +23,6 @@ import { Status } from '../../../../common/detection_engine/schemas/common/schem
import { isAlertFromEndpointAlert } from '../../../common/utils/endpoint_alert_check';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { useAddToCaseActions } from '../alerts_table/timeline_actions/use_add_to_case_actions';
import { inputsModel, inputsSelectors, State } from '../../../common/store';
interface ActionsData {
alertStatus: Status;
eventId: string;
@ -48,7 +45,7 @@ export interface TakeActionDropdownProps {
timelineId: string;
}
export const TakeActionDropdownComponent = React.memo(
export const TakeActionDropdown = React.memo(
({
detailsData,
ecsData,
@ -61,9 +58,7 @@ export const TakeActionDropdownComponent = React.memo(
onAddIsolationStatusClick,
refetch,
timelineId,
globalQuery,
timelineQuery,
}: TakeActionDropdownProps & PropsFromRedux) => {
}: TakeActionDropdownProps) => {
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@ -146,24 +141,12 @@ export const TakeActionDropdownComponent = React.memo(
closePopoverHandler();
}, [closePopoverHandler]);
const refetchQuery = (newQueries: inputsModel.GlobalQuery[]) => {
newQueries.forEach((q) => q.refetch && (q.refetch as inputsModel.Refetch)());
};
const refetchAll = useCallback(() => {
if (timelineId === TimelineId.active) {
refetchQuery([timelineQuery]);
} else {
refetchQuery(globalQuery);
}
}, [timelineId, globalQuery, timelineQuery]);
const { actionItems: statusActionItems } = useAlertsActions({
alertStatus: actionsData.alertStatus,
closePopover: closePopoverAndFlyout,
eventId: actionsData.eventId,
indexName,
refetch: refetchAll,
refetch,
timelineId,
});
@ -233,21 +216,3 @@ export const TakeActionDropdownComponent = React.memo(
) : null;
}
);
const makeMapStateToProps = () => {
const getGlobalQueries = inputsSelectors.globalQuery();
const getTimelineQuery = inputsSelectors.timelineQueryByIdSelector();
const mapStateToProps = (state: State, { timelineId }: TakeActionDropdownProps) => {
return {
globalQuery: getGlobalQueries(state),
timelineQuery: getTimelineQuery(state, timelineId),
};
};
return mapStateToProps;
};
const connector = connect(makeMapStateToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;
export const TakeActionDropdown = connector(React.memo(TakeActionDropdownComponent));

View file

@ -1056,7 +1056,7 @@ Array [
</div>
</EuiFlyoutBody>
</Styled(EuiFlyoutBody)>
<Memo()
<Connect(Component)
detailsData={null}
expandedEvent={
Object {
@ -1219,187 +1219,199 @@ Array [
onAddIsolationStatusClick={[Function]}
timelineId="test"
>
<EuiFlyoutFooter>
<div
className="euiFlyoutFooter"
>
<EuiFlexGroup
justifyContent="flexEnd"
<Memo()
detailsData={null}
dispatch={[Function]}
expandedEvent={
Object {
"ecsData": Object {
"_id": "1",
"destination": Object {
"ip": Array [
"192.168.0.3",
],
"port": Array [
6343,
],
},
"event": Object {
"action": Array [
"Action",
],
"category": Array [
"Access",
],
"id": Array [
"1",
],
"module": Array [
"nginx",
],
"severity": Array [
3,
],
},
"geo": Object {
"country_iso_code": Array [
"xx",
],
"region_name": Array [
"xx",
],
},
"host": Object {
"ip": Array [
"192.168.0.1",
],
"name": Array [
"apache",
],
},
"signal": Object {
"rule": Object {
"created_at": Array [
"2020-01-10T21:11:45.839Z",
],
"created_by": Array [
"elastic",
],
"description": Array [
"24/7",
],
"enabled": Array [
true,
],
"false_positives": Array [
"test-1",
],
"filters": Array [],
"from": Array [
"now-300s",
],
"id": Array [
"b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea",
],
"immutable": Array [
false,
],
"index": Array [
"auditbeat-*",
],
"interval": Array [
"5m",
],
"language": Array [
"kuery",
],
"max_signals": Array [
100,
],
"note": Array [
"# this is some markdown documentation",
],
"output_index": Array [
".siem-signals-default",
],
"query": Array [
"user.name: root or user.name: admin",
],
"references": Array [
"www.test.co",
],
"risk_score": Array [
"21",
],
"rule_id": Array [
"rule-id-1",
],
"saved_id": Array [
"Garrett's IP",
],
"severity": Array [
"low",
],
"tags": Array [],
"threat": Array [],
"timeline_id": Array [
"1234-2136-11ea-9864-ebc8cc1cb8c2",
],
"timeline_title": Array [
"Untitled timeline",
],
"to": Array [
"now",
],
"type": Array [
"saved_query",
],
"updated_at": Array [
"2020-01-10T21:11:45.839Z",
],
"updated_by": Array [
"elastic",
],
"version": Array [
"1",
],
},
},
"source": Object {
"ip": Array [
"192.168.0.1",
],
"port": Array [
80,
],
},
"timestamp": "2018-11-05T19:03:25.937Z",
"user": Object {
"id": Array [
"1",
],
"name": Array [
"john.dee",
],
},
},
"eventId": "my-id",
"indexName": "my-index",
}
}
globalQuery={Array []}
handleOnEventClosed={[Function]}
isHostIsolationPanelOpen={false}
loadingEventDetails={true}
onAddIsolationStatusClick={[Function]}
timelineId="test"
timelineQuery={
Object {
"id": "",
"inspect": null,
"isInspected": false,
"loading": false,
"refetch": null,
"selectedInspectIndex": 0,
}
}
>
<EuiFlyoutFooter>
<div
className="euiFlyoutFooter"
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive"
<EuiFlexGroup
justifyContent="flexEnd"
>
<EuiFlexItem
grow={false}
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
<EuiFlexItem
grow={false}
>
<Connect(Component)
detailsData={null}
ecsData={
Object {
"_id": "1",
"destination": Object {
"ip": Array [
"192.168.0.3",
],
"port": Array [
6343,
],
},
"event": Object {
"action": Array [
"Action",
],
"category": Array [
"Access",
],
"id": Array [
"1",
],
"module": Array [
"nginx",
],
"severity": Array [
3,
],
},
"geo": Object {
"country_iso_code": Array [
"xx",
],
"region_name": Array [
"xx",
],
},
"host": Object {
"ip": Array [
"192.168.0.1",
],
"name": Array [
"apache",
],
},
"signal": Object {
"rule": Object {
"created_at": Array [
"2020-01-10T21:11:45.839Z",
],
"created_by": Array [
"elastic",
],
"description": Array [
"24/7",
],
"enabled": Array [
true,
],
"false_positives": Array [
"test-1",
],
"filters": Array [],
"from": Array [
"now-300s",
],
"id": Array [
"b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea",
],
"immutable": Array [
false,
],
"index": Array [
"auditbeat-*",
],
"interval": Array [
"5m",
],
"language": Array [
"kuery",
],
"max_signals": Array [
100,
],
"note": Array [
"# this is some markdown documentation",
],
"output_index": Array [
".siem-signals-default",
],
"query": Array [
"user.name: root or user.name: admin",
],
"references": Array [
"www.test.co",
],
"risk_score": Array [
"21",
],
"rule_id": Array [
"rule-id-1",
],
"saved_id": Array [
"Garrett's IP",
],
"severity": Array [
"low",
],
"tags": Array [],
"threat": Array [],
"timeline_id": Array [
"1234-2136-11ea-9864-ebc8cc1cb8c2",
],
"timeline_title": Array [
"Untitled timeline",
],
"to": Array [
"now",
],
"type": Array [
"saved_query",
],
"updated_at": Array [
"2020-01-10T21:11:45.839Z",
],
"updated_by": Array [
"elastic",
],
"version": Array [
"1",
],
},
},
"source": Object {
"ip": Array [
"192.168.0.1",
],
"port": Array [
80,
],
},
"timestamp": "2018-11-05T19:03:25.937Z",
"user": Object {
"id": Array [
"1",
],
"name": Array [
"john.dee",
],
},
}
}
handleOnEventClosed={[Function]}
indexName="my-index"
isHostIsolationPanelOpen={false}
loadingEventDetails={true}
onAddEventFilterClick={[Function]}
onAddExceptionTypeClick={[Function]}
onAddIsolationStatusClick={[Function]}
timelineId="test"
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<Memo()
detailsData={null}
dispatch={[Function]}
ecsData={
Object {
"_id": "1",
@ -1551,7 +1563,6 @@ Array [
},
}
}
globalQuery={Array []}
handleOnEventClosed={[Function]}
indexName="my-index"
isHostIsolationPanelOpen={false}
@ -1559,26 +1570,17 @@ Array [
onAddEventFilterClick={[Function]}
onAddExceptionTypeClick={[Function]}
onAddIsolationStatusClick={[Function]}
refetch={[Function]}
timelineId="test"
timelineQuery={
Object {
"id": "",
"inspect": null,
"isInspected": false,
"loading": false,
"refetch": null,
"selectedInspectIndex": 0,
}
}
/>
</Connect(Component)>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiFlyoutFooter>
</Memo()>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiFlyoutFooter>
</Memo()>
</Connect(Component)>
</EventDetailsPanelComponent>
</div>
</EuiFlyout>,
@ -2093,7 +2095,7 @@ Array [
</div>
</EuiFlyoutBody>
</Styled(EuiFlyoutBody)>
<Memo()
<Connect(Component)
detailsData={null}
expandedEvent={
Object {
@ -2256,187 +2258,199 @@ Array [
onAddIsolationStatusClick={[Function]}
timelineId="test"
>
<EuiFlyoutFooter>
<div
className="euiFlyoutFooter"
>
<EuiFlexGroup
justifyContent="flexEnd"
<Memo()
detailsData={null}
dispatch={[Function]}
expandedEvent={
Object {
"ecsData": Object {
"_id": "1",
"destination": Object {
"ip": Array [
"192.168.0.3",
],
"port": Array [
6343,
],
},
"event": Object {
"action": Array [
"Action",
],
"category": Array [
"Access",
],
"id": Array [
"1",
],
"module": Array [
"nginx",
],
"severity": Array [
3,
],
},
"geo": Object {
"country_iso_code": Array [
"xx",
],
"region_name": Array [
"xx",
],
},
"host": Object {
"ip": Array [
"192.168.0.1",
],
"name": Array [
"apache",
],
},
"signal": Object {
"rule": Object {
"created_at": Array [
"2020-01-10T21:11:45.839Z",
],
"created_by": Array [
"elastic",
],
"description": Array [
"24/7",
],
"enabled": Array [
true,
],
"false_positives": Array [
"test-1",
],
"filters": Array [],
"from": Array [
"now-300s",
],
"id": Array [
"b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea",
],
"immutable": Array [
false,
],
"index": Array [
"auditbeat-*",
],
"interval": Array [
"5m",
],
"language": Array [
"kuery",
],
"max_signals": Array [
100,
],
"note": Array [
"# this is some markdown documentation",
],
"output_index": Array [
".siem-signals-default",
],
"query": Array [
"user.name: root or user.name: admin",
],
"references": Array [
"www.test.co",
],
"risk_score": Array [
"21",
],
"rule_id": Array [
"rule-id-1",
],
"saved_id": Array [
"Garrett's IP",
],
"severity": Array [
"low",
],
"tags": Array [],
"threat": Array [],
"timeline_id": Array [
"1234-2136-11ea-9864-ebc8cc1cb8c2",
],
"timeline_title": Array [
"Untitled timeline",
],
"to": Array [
"now",
],
"type": Array [
"saved_query",
],
"updated_at": Array [
"2020-01-10T21:11:45.839Z",
],
"updated_by": Array [
"elastic",
],
"version": Array [
"1",
],
},
},
"source": Object {
"ip": Array [
"192.168.0.1",
],
"port": Array [
80,
],
},
"timestamp": "2018-11-05T19:03:25.937Z",
"user": Object {
"id": Array [
"1",
],
"name": Array [
"john.dee",
],
},
},
"eventId": "my-id",
"indexName": "my-index",
}
}
globalQuery={Array []}
handleOnEventClosed={[Function]}
isHostIsolationPanelOpen={false}
loadingEventDetails={true}
onAddIsolationStatusClick={[Function]}
timelineId="test"
timelineQuery={
Object {
"id": "",
"inspect": null,
"isInspected": false,
"loading": false,
"refetch": null,
"selectedInspectIndex": 0,
}
}
>
<EuiFlyoutFooter>
<div
className="euiFlyoutFooter"
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive"
<EuiFlexGroup
justifyContent="flexEnd"
>
<EuiFlexItem
grow={false}
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentFlexEnd euiFlexGroup--directionRow euiFlexGroup--responsive"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
<EuiFlexItem
grow={false}
>
<Connect(Component)
detailsData={null}
ecsData={
Object {
"_id": "1",
"destination": Object {
"ip": Array [
"192.168.0.3",
],
"port": Array [
6343,
],
},
"event": Object {
"action": Array [
"Action",
],
"category": Array [
"Access",
],
"id": Array [
"1",
],
"module": Array [
"nginx",
],
"severity": Array [
3,
],
},
"geo": Object {
"country_iso_code": Array [
"xx",
],
"region_name": Array [
"xx",
],
},
"host": Object {
"ip": Array [
"192.168.0.1",
],
"name": Array [
"apache",
],
},
"signal": Object {
"rule": Object {
"created_at": Array [
"2020-01-10T21:11:45.839Z",
],
"created_by": Array [
"elastic",
],
"description": Array [
"24/7",
],
"enabled": Array [
true,
],
"false_positives": Array [
"test-1",
],
"filters": Array [],
"from": Array [
"now-300s",
],
"id": Array [
"b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea",
],
"immutable": Array [
false,
],
"index": Array [
"auditbeat-*",
],
"interval": Array [
"5m",
],
"language": Array [
"kuery",
],
"max_signals": Array [
100,
],
"note": Array [
"# this is some markdown documentation",
],
"output_index": Array [
".siem-signals-default",
],
"query": Array [
"user.name: root or user.name: admin",
],
"references": Array [
"www.test.co",
],
"risk_score": Array [
"21",
],
"rule_id": Array [
"rule-id-1",
],
"saved_id": Array [
"Garrett's IP",
],
"severity": Array [
"low",
],
"tags": Array [],
"threat": Array [],
"timeline_id": Array [
"1234-2136-11ea-9864-ebc8cc1cb8c2",
],
"timeline_title": Array [
"Untitled timeline",
],
"to": Array [
"now",
],
"type": Array [
"saved_query",
],
"updated_at": Array [
"2020-01-10T21:11:45.839Z",
],
"updated_by": Array [
"elastic",
],
"version": Array [
"1",
],
},
},
"source": Object {
"ip": Array [
"192.168.0.1",
],
"port": Array [
80,
],
},
"timestamp": "2018-11-05T19:03:25.937Z",
"user": Object {
"id": Array [
"1",
],
"name": Array [
"john.dee",
],
},
}
}
handleOnEventClosed={[Function]}
indexName="my-index"
isHostIsolationPanelOpen={false}
loadingEventDetails={true}
onAddEventFilterClick={[Function]}
onAddExceptionTypeClick={[Function]}
onAddIsolationStatusClick={[Function]}
timelineId="test"
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<Memo()
detailsData={null}
dispatch={[Function]}
ecsData={
Object {
"_id": "1",
@ -2588,7 +2602,6 @@ Array [
},
}
}
globalQuery={Array []}
handleOnEventClosed={[Function]}
indexName="my-index"
isHostIsolationPanelOpen={false}
@ -2596,26 +2609,17 @@ Array [
onAddEventFilterClick={[Function]}
onAddExceptionTypeClick={[Function]}
onAddIsolationStatusClick={[Function]}
refetch={[Function]}
timelineId="test"
timelineQuery={
Object {
"id": "",
"inspect": null,
"isInspected": false,
"loading": false,
"refetch": null,
"selectedInspectIndex": 0,
}
}
/>
</Connect(Component)>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiFlyoutFooter>
</Memo()>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiFlyoutFooter>
</Memo()>
</Connect(Component)>
</EventDetailsPanelComponent>
</div>,
]

View file

@ -5,11 +5,12 @@
* 2.0.
*/
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { find, get, isEmpty } from 'lodash/fp';
import { connect, ConnectedProps } from 'react-redux';
import { TakeActionDropdown } from '../../../../detections/components/take_action_dropdown';
import type { TimelineEventsDetailsItem } from '../../../../../common';
import { TimelineEventsDetailsItem, TimelineId } from '../../../../../common';
import { useExceptionModal } from '../../../../detections/components/alerts_table/timeline_actions/use_add_exception_modal';
import { AddExceptionModalWrapper } from '../../../../detections/components/alerts_table/timeline_actions/alert_context_menu';
import { EventFiltersModal } from '../../../../management/pages/event_filters/view/components/modal';
@ -18,6 +19,7 @@ import { getFieldValue } from '../../../../detections/components/host_isolation/
import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import { Ecs } from '../../../../../common/ecs';
import { useFetchEcsAlertsData } from '../../../../detections/containers/detection_engine/alerts/use_fetch_ecs_alerts_data';
import { inputsModel, inputsSelectors, State } from '../../../../common/store';
interface EventDetailsFooterProps {
detailsData: TimelineEventsDetailsItem[] | null;
@ -41,7 +43,7 @@ interface AddExceptionModalWrapperData {
ruleName: string;
}
export const EventDetailsFooter = React.memo(
export const EventDetailsFooterComponent = React.memo(
({
detailsData,
expandedEvent,
@ -50,7 +52,9 @@ export const EventDetailsFooter = React.memo(
loadingEventDetails,
onAddIsolationStatusClick,
timelineId,
}: EventDetailsFooterProps) => {
globalQuery,
timelineQuery,
}: EventDetailsFooterProps & PropsFromRedux) => {
const ruleIndex = useMemo(
() => find({ category: 'signal', field: 'signal.rule.index' }, detailsData)?.values,
[detailsData]
@ -78,6 +82,18 @@ export const EventDetailsFooter = React.memo(
[expandedEvent?.eventId]
);
const refetchQuery = (newQueries: inputsModel.GlobalQuery[]) => {
newQueries.forEach((q) => q.refetch && (q.refetch as inputsModel.Refetch)());
};
const refetchAll = useCallback(() => {
if (timelineId === TimelineId.active) {
refetchQuery([timelineQuery]);
} else {
refetchQuery(globalQuery);
}
}, [timelineId, globalQuery, timelineQuery]);
const {
exceptionModalType,
onAddExceptionTypeClick,
@ -86,7 +102,7 @@ export const EventDetailsFooter = React.memo(
ruleIndices,
} = useExceptionModal({
ruleIndex,
refetch: expandedEvent?.refetch,
refetch: refetchAll,
timelineId,
});
const { closeAddEventFilterModal, isAddEventFilterModalOpen, onAddEventFilterClick } =
@ -113,7 +129,7 @@ export const EventDetailsFooter = React.memo(
onAddEventFilterClick={onAddEventFilterClick}
onAddExceptionTypeClick={onAddExceptionTypeClick}
onAddIsolationStatusClick={onAddIsolationStatusClick}
refetch={expandedEvent?.refetch}
refetch={refetchAll}
indexName={expandedEvent.indexName}
timelineId={timelineId}
/>
@ -142,3 +158,21 @@ export const EventDetailsFooter = React.memo(
);
}
);
const makeMapStateToProps = () => {
const getGlobalQueries = inputsSelectors.globalQuery();
const getTimelineQuery = inputsSelectors.timelineQueryByIdSelector();
const mapStateToProps = (state: State, { timelineId }: EventDetailsFooterProps) => {
return {
globalQuery: getGlobalQueries(state),
timelineQuery: getTimelineQuery(state, timelineId),
};
};
return mapStateToProps;
};
const connector = connect(makeMapStateToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;
export const EventDetailsFooter = connector(React.memo(EventDetailsFooterComponent));

View file

@ -17,6 +17,8 @@ import {
/**
* Update alert status by query
*
* @param useDetectionEngine logic flag for using the regular Detection Engine URL or the RAC URL
*
* @param status to update to('open' / 'closed' / 'acknowledged')
* @param index index to be updated
@ -26,7 +28,7 @@ import {
* @throws An error if response is not OK
*/
export const useUpdateAlertsStatus = (
timelineId: string
useDetectionEngine: boolean = false
): {
updateAlertStatus: (params: {
status: AlertStatus;
@ -37,7 +39,7 @@ export const useUpdateAlertsStatus = (
const { http } = useKibana<CoreStart>().services;
return {
updateAlertStatus: async ({ status, index, query }) => {
if (['detections-page', 'detections-rules-details-page'].includes(timelineId)) {
if (useDetectionEngine) {
return http!.fetch(DETECTION_ENGINE_SIGNALS_STATUS_URL, {
method: 'POST',
body: JSON.stringify({ status, query }),
@ -51,5 +53,3 @@ export const useUpdateAlertsStatus = (
},
};
};
//

View file

@ -28,7 +28,7 @@ export const useStatusBulkActionItems = ({
onUpdateFailure,
timelineId,
}: StatusBulkActionsProps) => {
const { updateAlertStatus } = useUpdateAlertsStatus(timelineId ?? '');
const { updateAlertStatus } = useUpdateAlertsStatus(timelineId != null);
const { addSuccess, addError, addWarning } = useAppToasts();
const onAlertStatusUpdateSuccess = useCallback(