[Security Solution] [Endpoint] Allow filtering activity log with date range (#104085)

* use date range in search query

fixes elastic/security-team/issues/1137

* make any date selection fetch matching log

fixes elastic/security-team/issues/1137

* use a single action for updating paging info and fetching data

fixes elastic/security-team/issues/1137

* use consistent types

for some reason TS was complaining earlier with `undefined`

* reset date picker on tab load

fixes elastic/security-team/issues/1137

* refactor date pickers into a component

refs elastic/security-team/issues/1137

* clear dates on change of endpoint

fixes elastic/security-team/issues/1137

* do not show empty state if date filtering results return empty data

fixes elastic/security-team/issues/1137

* add tests

fixes elastic/security-team/issues/1137

* review changes

* update comment

refs f551b67d66

* store invalidDateRange on redux store and decouple logic from the component

review changes

* fix test

* fix lint

* review changes

* expand date picker to use the full width of the flyout

review changes

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Ashokaditya 2021-07-09 18:24:18 +02:00 committed by GitHub
parent facaeb7d65
commit 81f09a863d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 416 additions and 67 deletions

View file

@ -23,6 +23,8 @@ export const EndpointActionLogRequestSchema = {
query: schema.object({
page: schema.number({ defaultValue: 1, min: 1 }),
page_size: schema.number({ defaultValue: 10, min: 1, max: 100 }),
start_date: schema.maybe(schema.string()),
end_date: schema.maybe(schema.string()),
}),
params: schema.object({
agent_id: schema.string(),

View file

@ -60,6 +60,8 @@ export type ActivityLogEntry = ActivityLogAction | ActivityLogActionResponse;
export interface ActivityLog {
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
data: ActivityLogEntry[];
}

View file

@ -146,13 +146,6 @@ export type EndpointIsolationRequestStateChange = Action<'endpointIsolationReque
payload: EndpointState['isolationRequestState'];
};
export interface AppRequestedEndpointActivityLog {
type: 'appRequestedEndpointActivityLog';
payload: {
page: number;
pageSize: number;
};
}
export type EndpointDetailsActivityLogChanged = Action<'endpointDetailsActivityLogChanged'> & {
payload: EndpointState['endpointDetails']['activityLog']['logData'];
};
@ -165,9 +158,18 @@ export interface EndpointDetailsActivityLogUpdatePaging {
type: 'endpointDetailsActivityLogUpdatePaging';
payload: {
// disable paging when no more data after paging
disabled: boolean;
disabled?: boolean;
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
};
}
export interface EndpointDetailsActivityLogUpdateIsInvalidDateRange {
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange';
payload: {
isInvalidDateRange?: boolean;
};
}
@ -181,8 +183,8 @@ export type EndpointAction =
| ServerFailedToReturnEndpointList
| ServerReturnedEndpointDetails
| ServerFailedToReturnEndpointDetails
| AppRequestedEndpointActivityLog
| EndpointDetailsActivityLogUpdatePaging
| EndpointDetailsActivityLogUpdateIsInvalidDateRange
| EndpointDetailsFlyoutTabChanged
| EndpointDetailsActivityLogChanged
| ServerReturnedEndpointPolicyResponse

View file

@ -25,6 +25,9 @@ export const initialEndpointPageState = (): Immutable<EndpointState> => {
disabled: false,
page: 1,
pageSize: 50,
startDate: undefined,
endDate: undefined,
isInvalidDateRange: false,
},
logData: createUninitialisedResourceState(),
},

View file

@ -48,6 +48,7 @@ describe('EndpointList store concerns', () => {
disabled: false,
page: 1,
pageSize: 50,
isInvalidDateRange: false,
},
logData: { type: 'UninitialisedResourceState' },
},

View file

@ -65,6 +65,7 @@ import { resolvePathVariables } from '../../../../common/utils/resolve_path_vari
import { EndpointPackageInfoStateChanged } from './action';
import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions';
import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs';
import { getIsInvalidDateRange } from '../utils';
type EndpointPageStore = ImmutableMiddlewareAPI<EndpointState, AppAction>;
@ -400,21 +401,50 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
}
// page activity log API
if (action.type === 'appRequestedEndpointActivityLog' && hasSelectedEndpoint(getState())) {
dispatch({
type: 'endpointDetailsActivityLogChanged',
// ts error to be fixed when AsyncResourceState is refactored (#830)
// @ts-expect-error
payload: createLoadingResourceState<ActivityLog>(getActivityLogData(getState())),
});
if (
action.type === 'endpointDetailsActivityLogUpdatePaging' &&
hasSelectedEndpoint(getState())
) {
try {
const { page, pageSize } = getActivityLogDataPaging(getState());
const { disabled, page, pageSize, startDate, endDate } = getActivityLogDataPaging(
getState()
);
// don't page when paging is disabled or when date ranges are invalid
if (disabled) {
return;
}
if (getIsInvalidDateRange({ startDate, endDate })) {
dispatch({
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange',
payload: {
isInvalidDateRange: true,
},
});
return;
}
dispatch({
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange',
payload: {
isInvalidDateRange: false,
},
});
dispatch({
type: 'endpointDetailsActivityLogChanged',
// ts error to be fixed when AsyncResourceState is refactored (#830)
// @ts-expect-error
payload: createLoadingResourceState<ActivityLog>(getActivityLogData(getState())),
});
const route = resolvePathVariables(ENDPOINT_ACTION_LOG_ROUTE, {
agent_id: selectedAgent(getState()),
});
const activityLog = await coreStart.http.get<ActivityLog>(route, {
query: { page, page_size: pageSize },
query: {
page,
page_size: pageSize,
start_date: startDate,
end_date: endDate,
},
});
const lastLoadedLogData = getLastLoadedActivityLogData(getState());
@ -428,6 +458,8 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
const updatedLogData = {
page: activityLog.page,
pageSize: activityLog.pageSize,
startDate: activityLog.startDate,
endDate: activityLog.endDate,
data: activityLog.page === 1 ? activityLog.data : updatedLogDataItems,
};
dispatch({
@ -439,8 +471,10 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: true,
page: activityLog.page - 1,
page: activityLog.page > 1 ? activityLog.page - 1 : 1,
pageSize: activityLog.pageSize,
startDate: activityLog.startDate,
endDate: activityLog.endDate,
},
});
}

View file

@ -41,6 +41,8 @@ const handleEndpointDetailsActivityLogChanged: CaseReducer<EndpointDetailsActivi
...state.endpointDetails.activityLog.paging,
page: action.payload.data.page,
pageSize: action.payload.data.pageSize,
startDate: action.payload.data.startDate,
endDate: action.payload.data.endDate,
},
}
: { ...state.endpointDetails.activityLog };
@ -162,33 +164,31 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
},
},
};
} else if (action.type === 'appRequestedEndpointActivityLog') {
const paging = {
disabled: state.endpointDetails.activityLog.paging.disabled,
page: action.payload.page,
pageSize: action.payload.pageSize,
};
} else if (action.type === 'endpointDetailsActivityLogUpdatePaging') {
return {
...state,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
paging,
paging: {
...state.endpointDetails.activityLog.paging,
...action.payload,
},
},
},
};
} else if (action.type === 'endpointDetailsActivityLogUpdatePaging') {
const paging = {
...action.payload,
};
} else if (action.type === 'endpointDetailsActivityLogUpdateIsInvalidDateRange') {
return {
...state,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
paging,
paging: {
...state.endpointDetails.activityLog.paging,
...action.payload,
},
},
},
};
@ -304,6 +304,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
disabled: false,
page: 1,
pageSize: 50,
isInvalidDateRange: false,
},
};

View file

@ -40,9 +40,12 @@ export interface EndpointState {
flyoutView: EndpointIndexUIQueryParams['show'];
activityLog: {
paging: {
disabled: boolean;
disabled?: boolean;
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
isInvalidDateRange: boolean;
};
logData: AsyncResourceState<ActivityLog>;
};

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import moment from 'moment';
import { getIsInvalidDateRange } from './utils';
describe('utils', () => {
describe('getIsInvalidDateRange', () => {
it('should return FALSE when either dates are undefined', () => {
expect(getIsInvalidDateRange({})).toBe(false);
expect(getIsInvalidDateRange({ startDate: moment().subtract(1, 'd').toISOString() })).toBe(
false
);
expect(getIsInvalidDateRange({ endDate: moment().toISOString() })).toBe(false);
});
it('should return TRUE when startDate is after endDate', () => {
expect(
getIsInvalidDateRange({
startDate: moment().toISOString(),
endDate: moment().subtract(1, 'd').toISOString(),
})
).toBe(true);
});
});
});

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import moment from 'moment';
import { HostInfo, HostMetadata } from '../../../../common/endpoint/types';
export const isPolicyOutOfDate = (
@ -23,3 +24,18 @@ export const isPolicyOutOfDate = (
reported.endpoint_policy_version >= current.endpoint.revision
);
};
export const getIsInvalidDateRange = ({
startDate,
endDate,
}: {
startDate?: string;
endDate?: string;
}) => {
if (startDate && endDate) {
const start = moment(startDate);
const end = moment(endDate);
return start.isAfter(end);
}
return false;
};

View file

@ -0,0 +1,127 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useDispatch } from 'react-redux';
import React, { memo, useCallback } from 'react';
import styled from 'styled-components';
import moment from 'moment';
import { EuiFlexGroup, EuiFlexItem, EuiDatePicker, EuiDatePickerRange } from '@elastic/eui';
import * as i18 from '../../../translations';
import { useEndpointSelector } from '../../../hooks';
import { getActivityLogDataPaging } from '../../../../store/selectors';
const DatePickerWrapper = styled.div`
width: ${(props) => props.theme.eui.fractions.single.percentage};
background: white;
`;
const StickyFlexItem = styled(EuiFlexItem)`
position: sticky;
top: ${(props) => props.theme.eui.euiSizeM};
z-index: 1;
`;
export const DateRangePicker = memo(() => {
const dispatch = useDispatch();
const { page, pageSize, startDate, endDate, isInvalidDateRange } = useEndpointSelector(
getActivityLogDataPaging
);
const onClear = useCallback(
({ clearStart = false, clearEnd = false }: { clearStart?: boolean; clearEnd?: boolean }) => {
dispatch({
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: false,
page,
pageSize,
startDate: clearStart ? undefined : startDate,
endDate: clearEnd ? undefined : endDate,
},
});
},
[dispatch, endDate, startDate, page, pageSize]
);
const onChangeStartDate = useCallback(
(date) => {
dispatch({
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: false,
page,
pageSize,
startDate: date ? date?.toISOString() : undefined,
endDate: endDate ? endDate : undefined,
},
});
},
[dispatch, endDate, page, pageSize]
);
const onChangeEndDate = useCallback(
(date) => {
dispatch({
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: false,
page,
pageSize,
startDate: startDate ? startDate : undefined,
endDate: date ? date.toISOString() : undefined,
},
});
},
[dispatch, startDate, page, pageSize]
);
return (
<StickyFlexItem grow={false}>
<EuiFlexGroup justifyContent="flexEnd" responsive>
<DatePickerWrapper>
<EuiFlexItem>
<EuiDatePickerRange
fullWidth={true}
data-test-subj="activityLogDateRangePicker"
startDateControl={
<EuiDatePicker
aria-label="Start date"
endDate={endDate ? moment(endDate) : undefined}
isInvalid={isInvalidDateRange}
maxDate={moment(endDate) || moment()}
onChange={onChangeStartDate}
onClear={() => onClear({ clearStart: true })}
placeholderText={i18.ACTIVITY_LOG.datePicker.startDate}
selected={startDate ? moment(startDate) : undefined}
showTimeSelect
startDate={startDate ? moment(startDate) : undefined}
/>
}
endDateControl={
<EuiDatePicker
aria-label="End date"
endDate={endDate ? moment(endDate) : undefined}
isInvalid={isInvalidDateRange}
maxDate={moment()}
minDate={startDate ? moment(startDate) : undefined}
onChange={onChangeEndDate}
onClear={() => onClear({ clearEnd: true })}
placeholderText={i18.ACTIVITY_LOG.datePicker.endDate}
selected={endDate ? moment(endDate) : undefined}
showTimeSelect
startDate={startDate ? moment(startDate) : undefined}
/>
}
/>
</EuiFlexItem>
</DatePickerWrapper>
</EuiFlexGroup>
</StickyFlexItem>
);
});
DateRangePicker.displayName = 'DateRangePicker';

View file

@ -56,19 +56,14 @@ export const EndpointDetailsFlyoutTabs = memo(
},
});
if (tab.id === EndpointDetailsTabsTypes.activityLog) {
const paging = {
page: 1,
pageSize,
};
dispatch({
type: 'appRequestedEndpointActivityLog',
payload: paging,
});
dispatch({
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: false,
...paging,
page: 1,
pageSize,
startDate: undefined,
endDate: undefined,
},
});
}

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { memo, useCallback, useEffect, useRef } from 'react';
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';
import styled from 'styled-components';
import {
@ -17,6 +17,7 @@ import {
} from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { LogEntry } from './components/log_entry';
import { DateRangePicker } from './components/activity_log_date_range_picker';
import * as i18 from '../translations';
import { Immutable, ActivityLog } from '../../../../../../common/endpoint/types';
import { AsyncResourceState } from '../../../../state';
@ -31,12 +32,12 @@ import {
getActivityLogRequestLoading,
} from '../../store/selectors';
const StyledEuiFlexGroup = styled(EuiFlexGroup)`
height: 85vh;
const StyledEuiFlexGroup = styled(EuiFlexGroup)<{ isShorter: boolean }>`
height: ${({ isShorter }) => (isShorter ? '25vh' : '85vh')};
`;
const LoadMoreTrigger = styled.div`
height: 6px;
width: 100%;
height: ${(props) => props.theme.eui.euiSizeXS};
width: ${(props) => props.theme.eui.fractions.single.percentage};
`;
export const EndpointActivityLog = memo(
@ -48,25 +49,37 @@ export const EndpointActivityLog = memo(
const activityLogSize = activityLogData.length;
const activityLogError = useEndpointSelector(getActivityLogError);
const dispatch = useDispatch<(action: EndpointAction) => void>();
const { page, pageSize, disabled: isPagingDisabled } = useEndpointSelector(
const { page, pageSize, startDate, endDate, disabled: isPagingDisabled } = useEndpointSelector(
getActivityLogDataPaging
);
const hasActiveDateRange = useMemo(() => !!startDate || !!endDate, [startDate, endDate]);
const showEmptyState = useMemo(
() => (activityLogLoaded && !activityLogSize && !hasActiveDateRange) || activityLogError,
[activityLogLoaded, activityLogSize, hasActiveDateRange, activityLogError]
);
const isShorter = useMemo(
() => !!(hasActiveDateRange && isPagingDisabled && !activityLogLoading && !activityLogSize),
[hasActiveDateRange, isPagingDisabled, activityLogLoading, activityLogSize]
);
const loadMoreTrigger = useRef<HTMLInputElement | null>(null);
const getActivityLog = useCallback(
(entries: IntersectionObserverEntry[]) => {
const isTargetIntersecting = entries.some((entry) => entry.isIntersecting);
if (isTargetIntersecting && activityLogLoaded && !isPagingDisabled) {
dispatch({
type: 'appRequestedEndpointActivityLog',
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
page: page + 1,
pageSize,
startDate,
endDate,
},
});
}
},
[activityLogLoaded, dispatch, isPagingDisabled, page, pageSize]
[activityLogLoaded, dispatch, isPagingDisabled, page, pageSize, startDate, endDate]
);
useEffect(() => {
@ -82,8 +95,8 @@ export const EndpointActivityLog = memo(
return (
<>
<StyledEuiFlexGroup direction="column" responsive={false}>
{(activityLogLoaded && !activityLogSize) || activityLogError ? (
<StyledEuiFlexGroup direction="column" responsive={false} isShorter={isShorter}>
{showEmptyState ? (
<EuiFlexItem>
<EuiEmptyPrompt
iconType="editorUnorderedList"
@ -95,6 +108,7 @@ export const EndpointActivityLog = memo(
</EuiFlexItem>
) : (
<>
<DateRangePicker />
<EuiFlexItem grow={true}>
{activityLogLoaded &&
activityLogData.map((logEntry) => (

View file

@ -889,6 +889,27 @@ describe('when on the endpoint list page', () => {
const emptyState = await renderResult.queryByTestId('activityLogEmpty');
expect(emptyState).not.toBe(null);
});
it('should not display empty state with no log data while date range filter is active', async () => {
const activityLogTab = await renderResult.findByTestId('activity_log');
reactTestingLibrary.act(() => {
reactTestingLibrary.fireEvent.click(activityLogTab);
});
await middlewareSpy.waitForAction('endpointDetailsActivityLogChanged');
reactTestingLibrary.act(() => {
dispatchEndpointDetailsActivityLogChanged('success', {
page: 1,
pageSize: 50,
startDate: new Date().toISOString(),
data: [],
});
});
const emptyState = await renderResult.queryByTestId('activityLogEmpty');
const dateRangePicker = await renderResult.queryByTestId('activityLogDateRangePicker');
expect(emptyState).toBe(null);
expect(dateRangePicker).not.toBe(null);
});
});
describe('when showing host Policy Response panel', () => {

View file

@ -15,6 +15,20 @@ export const ACTIVITY_LOG = {
tabTitle: i18n.translate('xpack.securitySolution.endpointDetails.activityLog', {
defaultMessage: 'Activity Log',
}),
datePicker: {
startDate: i18n.translate(
'xpack.securitySolution.endpointDetails.activityLog.datePicker.startDate',
{
defaultMessage: 'Pick a start date',
}
),
endDate: i18n.translate(
'xpack.securitySolution.endpointDetails.activityLog.datePicker.endDate',
{
defaultMessage: 'Pick an end date',
}
),
},
LogEntry: {
endOfLog: i18n.translate(
'xpack.securitySolution.endpointDetails.activityLog.logEntry.action.endOfLog',

View file

@ -60,6 +60,37 @@ describe('Action Log API', () => {
}).not.toThrow();
});
it('should work with all query params', () => {
expect(() => {
EndpointActionLogRequestSchema.query.validate({
page: 10,
page_size: 100,
start_date: new Date(new Date().setDate(new Date().getDate() - 1)).toISOString(), // yesterday
end_date: new Date().toISOString(), // today
});
}).not.toThrow();
});
it('should work with just startDate', () => {
expect(() => {
EndpointActionLogRequestSchema.query.validate({
page: 1,
page_size: 100,
start_date: new Date(new Date().setDate(new Date().getDate() - 1)).toISOString(), // yesterday
});
}).not.toThrow();
});
it('should work with just endDate', () => {
expect(() => {
EndpointActionLogRequestSchema.query.validate({
page: 1,
page_size: 100,
end_date: new Date().toISOString(), // today
});
}).not.toThrow();
});
it('should not work without allowed page and page_size params', () => {
expect(() => {
EndpointActionLogRequestSchema.query.validate({ page_size: 101 });
@ -176,5 +207,20 @@ describe('Action Log API', () => {
expect(error.message).toEqual(`Error fetching actions log for agent_id ${mockID}`);
}
});
it('should return date ranges if present in the query', async () => {
havingActionsAndResponses([], []);
const startDate = new Date(new Date().setDate(new Date().getDate() - 1)).toISOString();
const endDate = new Date().toISOString();
const response = await getActivityLog({
page: 1,
page_size: 50,
start_date: startDate,
end_date: endDate,
});
expect(response.ok).toBeCalled();
expect((response.ok.mock.calls[0][0]?.body as ActivityLog).startDate).toEqual(startDate);
expect((response.ok.mock.calls[0][0]?.body as ActivityLog).endDate).toEqual(endDate);
});
});
});

View file

@ -27,10 +27,18 @@ export const actionsLogRequestHandler = (
return async (context, req, res) => {
const {
params: { agent_id: elasticAgentId },
query: { page, page_size: pageSize },
query: { page, page_size: pageSize, start_date: startDate, end_date: endDate },
} = req;
const body = await getAuditLogResponse({ elasticAgentId, page, pageSize, context, logger });
const body = await getAuditLogResponse({
elasticAgentId,
page,
pageSize,
startDate,
endDate,
context,
logger,
});
return res.ok({
body,
});

View file

@ -19,28 +19,37 @@ export const getAuditLogResponse = async ({
elasticAgentId,
page,
pageSize,
startDate,
endDate,
context,
logger,
}: {
elasticAgentId: string;
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
context: SecuritySolutionRequestHandlerContext;
logger: Logger;
}): Promise<{
page: number;
pageSize: number;
data: ActivityLog['data'];
}> => {
}): Promise<ActivityLog> => {
const size = Math.floor(pageSize / 2);
const from = page <= 1 ? 0 : page * size - size + 1;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const data = await getActivityLog({ esClient, from, size, elasticAgentId, logger });
const data = await getActivityLog({
esClient,
from,
size,
startDate,
endDate,
elasticAgentId,
logger,
});
return {
page,
pageSize,
startDate,
endDate,
data,
};
};
@ -49,6 +58,8 @@ const getActivityLog = async ({
esClient,
size,
from,
startDate,
endDate,
elasticAgentId,
logger,
}: {
@ -56,6 +67,8 @@ const getActivityLog = async ({
elasticAgentId: string;
size: number;
from: number;
startDate?: string;
endDate?: string;
logger: Logger;
}) => {
const options = {
@ -67,8 +80,22 @@ const getActivityLog = async ({
let actionsResult;
let responsesResult;
const dateFilters = [];
if (startDate) {
dateFilters.push({ range: { '@timestamp': { gte: startDate } } });
}
if (endDate) {
dateFilters.push({ range: { '@timestamp': { lte: endDate } } });
}
try {
// fetch actions with matching agent_id
const baseActionFilters = [
{ term: { agents: elasticAgentId } },
{ term: { input_type: 'endpoint' } },
{ term: { type: 'INPUT_ACTION' } },
];
const actionsFilters = [...baseActionFilters, ...dateFilters];
actionsResult = await esClient.search(
{
index: AGENT_ACTIONS_INDEX,
@ -77,11 +104,8 @@ const getActivityLog = async ({
body: {
query: {
bool: {
filter: [
{ term: { agents: elasticAgentId } },
{ term: { input_type: 'endpoint' } },
{ term: { type: 'INPUT_ACTION' } },
],
// @ts-ignore
filter: actionsFilters,
},
},
sort: [
@ -99,6 +123,12 @@ const getActivityLog = async ({
(e) => (e._source as EndpointAction).action_id
);
// fetch responses with matching `action_id`s
const baseResponsesFilter = [
{ term: { agent_id: elasticAgentId } },
{ terms: { action_id: actionIds } },
];
const responsesFilters = [...baseResponsesFilter, ...dateFilters];
responsesResult = await esClient.search(
{
index: AGENT_ACTIONS_RESULTS_INDEX,
@ -106,7 +136,7 @@ const getActivityLog = async ({
body: {
query: {
bool: {
filter: [{ term: { agent_id: elasticAgentId } }, { terms: { action_id: actionIds } }],
filter: responsesFilters,
},
},
},