diff --git a/x-pack/plugins/security_solution/public/common/translations.ts b/x-pack/plugins/security_solution/public/common/translations.ts index 551e977ec9861..676546b63b224 100644 --- a/x-pack/plugins/security_solution/public/common/translations.ts +++ b/x-pack/plugins/security_solution/public/common/translations.ts @@ -77,7 +77,7 @@ export const UNSAVED_TIMELINE_SAVE_PROMPT_TITLE = i18n.translate( export const getAgentTypeName = (agentType: ResponseActionAgentType) => { switch (agentType) { case 'endpoint': - return 'Endpoint'; + return 'Elastic Defend'; case 'sentinel_one': return 'SentinelOne'; default: diff --git a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts index 8a4d98ae76f90..62772fa029e66 100644 --- a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_data.ts @@ -39,7 +39,7 @@ const getThirdPartyAgentInfo = ( ) as ResponseActionAgentType, }, host: { - name: getFieldValue({ category: 'host', field: 'host.os.name' }, eventData), + name: getFieldValue({ category: 'host', field: 'host.name' }, eventData), os: { name: getFieldValue({ category: 'host', field: 'host.os.name' }, eventData), family: getFieldValue({ category: 'host', field: 'host.os.family' }, eventData), diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx index 03268178bc3d9..5a44f6584520a 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx @@ -9,6 +9,8 @@ import React, { memo, useMemo } from 'react'; import { EuiCodeBlock, EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { css, euiStyled } from '@kbn/kibana-react-plugin/common'; import { map } from 'lodash'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { getAgentTypeName } from '../../../../common/translations'; import { RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP } from '../../../../../common/endpoint/service/response_actions/constants'; import { isExecuteAction, @@ -178,7 +180,19 @@ export const ActionsLogExpandedTray = memo<{ }>(({ action, 'data-test-subj': dataTestSubj }) => { const getTestId = useTestIdGenerator(dataTestSubj); - const { hosts, startedAt, completedAt, command: _command, comment, parameters } = action; + const isSentinelOneV1Enabled = useIsExperimentalFeatureEnabled( + 'responseActionsSentinelOneV1Enabled' + ); + + const { + hosts, + startedAt, + completedAt, + command: _command, + comment, + parameters, + agentType, + } = action; const parametersList = useMemo( () => @@ -192,45 +206,61 @@ export const ActionsLogExpandedTray = memo<{ const command = RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP[_command]; - const dataList = useMemo( - () => - [ - { - title: OUTPUT_MESSAGES.expandSection.placedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.startedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.completedAt, - description: `${completedAt ?? emptyValue}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.input, - description: `${command}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.parameters, - description: parametersList ? parametersList.join(', ') : emptyValue, - }, - { - title: OUTPUT_MESSAGES.expandSection.comment, - description: comment ? comment : emptyValue, - }, - { - title: OUTPUT_MESSAGES.expandSection.hostname, - description: map(hosts, (host) => host.name).join(', ') || emptyValue, - }, - ].map(({ title, description }) => { - return { - title: {title}, - description: {description}, - }; - }), - [command, comment, completedAt, hosts, parametersList, startedAt] - ); + const dataList = useMemo(() => { + const list = [ + { + title: OUTPUT_MESSAGES.expandSection.placedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.startedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.completedAt, + description: `${completedAt ?? emptyValue}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.input, + description: `${command}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.parameters, + description: parametersList ? parametersList.join(', ') : emptyValue, + }, + { + title: OUTPUT_MESSAGES.expandSection.comment, + description: comment ? comment : emptyValue, + }, + { + title: OUTPUT_MESSAGES.expandSection.hostname, + description: map(hosts, (host) => host.name).join(', ') || emptyValue, + }, + ]; + + if (isSentinelOneV1Enabled) { + list.push({ + title: OUTPUT_MESSAGES.expandSection.agentType, + description: getAgentTypeName(agentType) || emptyValue, + }); + } + + return list.map(({ title, description }) => { + return { + title: {title}, + description: {description}, + }; + }); + }, [ + agentType, + command, + comment, + completedAt, + hosts, + isSentinelOneV1Enabled, + parametersList, + startedAt, + ]); const outputList = useMemo( () => [ diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index 36d5d8d1f556b..b8e08f557aa2d 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -189,18 +189,28 @@ const getTypesFilterInitialState = ( // v8.13 onwards // for showing agent types and action types in the same filter if (isSentinelOneV1Enabled) { + if (!isFlyout) { + return [ + { + label: FILTER_NAMES.agentTypes, + isGroupLabel: true, + }, + ...RESPONSE_ACTION_AGENT_TYPE.map((type) => + getFilterOptions({ + key: type, + label: getAgentTypeName(type), + checked: !isFlyout && agentTypes?.includes(type) ? 'on' : undefined, + }) + ), + { + label: FILTER_NAMES.actionTypes, + isGroupLabel: true, + }, + ...defaultFilterOptions, + ]; + } + return [ - { - label: FILTER_NAMES.agentTypes, - isGroupLabel: true, - }, - ...RESPONSE_ACTION_AGENT_TYPE.map((type) => - getFilterOptions({ - key: type, - label: getAgentTypeName(type), - checked: !isFlyout && agentTypes?.includes(type) ? 'on' : undefined, - }) - ), { label: FILTER_NAMES.actionTypes, isGroupLabel: true, @@ -336,7 +346,10 @@ export const useActionsLogFilter = ({ () => items.filter((item) => item.checked === 'on').length, [items] ); - const numFilters = useMemo(() => items.filter((item) => item.checked !== 'on').length, [items]); + const numFilters = useMemo( + () => items.filter((item) => item.key && item.checked !== 'on').length, + [items] + ); return { areHostsSelectedOnMount, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx index 2b909e637bd20..3486da1191b4c 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx @@ -28,8 +28,10 @@ import { getActionListMock } from '../mocks'; import { useGetEndpointsList } from '../../../hooks/endpoint/use_get_endpoints_list'; import { v4 as uuidv4 } from 'uuid'; import { + RESPONSE_ACTION_AGENT_TYPE, RESPONSE_ACTION_API_COMMAND_TO_CONSOLE_COMMAND_MAP, RESPONSE_ACTION_API_COMMANDS_NAMES, + RESPONSE_ACTION_TYPE, } from '../../../../../common/endpoint/service/response_actions/constants'; import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; import { responseActionsHttpMocks } from '../../../mocks/response_actions_http_mocks'; @@ -571,6 +573,30 @@ describe('Response actions history', () => { ); }); + it('should contain agent type info in each expanded row', async () => { + mockedContext.setExperimentalFlag({ responseActionsSentinelOneV1Enabled: true }); + render(); + const { getAllByTestId } = renderResult; + + const expandButtons = getAllByTestId(`${testPrefix}-expand-button`); + expandButtons.map((button) => userEvent.click(button)); + const trays = getAllByTestId(`${testPrefix}-details-tray`); + expect(trays).toBeTruthy(); + expect(Array.from(trays[0].querySelectorAll('dt')).map((title) => title.textContent)).toEqual( + [ + 'Command placed', + 'Execution started on', + 'Execution completed', + 'Input', + 'Parameters', + 'Comment', + 'Hostname', + 'Agent type', + 'Output:', + ] + ); + }); + it('should refresh data when autoRefresh is toggled on', async () => { const listHookResponse = getBaseMockedActionList(); useGetEndpointActionListMock.mockReturnValue(listHookResponse); @@ -1380,4 +1406,49 @@ describe('Response actions history', () => { ); }); }); + + describe('Types filter', () => { + const filterPrefix = 'types-filter'; + it('should show a list of action types when opened', () => { + render(); + const { getByTestId, getAllByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`); + expect(filterList).toBeTruthy(); + expect(getAllByTestId(`${filterPrefix}-option`).length).toEqual(RESPONSE_ACTION_TYPE.length); + expect(getAllByTestId(`${filterPrefix}-option`).map((option) => option.textContent)).toEqual([ + 'Triggered by rule', + 'Triggered manually', + ]); + }); + + it('should show a list of agent and action types when opened in page view', () => { + mockedContext.setExperimentalFlag({ responseActionsSentinelOneV1Enabled: true }); + render({ isFlyout: false }); + const { getByTestId, getAllByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`); + expect(filterList).toBeTruthy(); + expect(getAllByTestId(`${filterPrefix}-option`).length).toEqual( + [...RESPONSE_ACTION_AGENT_TYPE, ...RESPONSE_ACTION_TYPE].length + ); + expect(getAllByTestId(`${filterPrefix}-option`).map((option) => option.textContent)).toEqual([ + 'Elastic Defend', + 'SentinelOne', + 'Triggered by rule', + 'Triggered manually', + ]); + }); + + it('should have `clear all` button `disabled` when no selected values', () => { + render(); + const { getByTestId } = renderResult; + + userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)); + const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`); + expect(clearAllButton.hasAttribute('disabled')).toBeTruthy(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index 907348790e92d..b11f31bbd1b72 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -77,6 +77,12 @@ export const OUTPUT_MESSAGES = Object.freeze({ defaultMessage: 'Hostname', } ), + agentType: i18n.translate( + 'xpack.securitySolution.responseActionsList.list.item.expandSection.agentType', + { + defaultMessage: 'Agent type', + } + ), }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx index 19ce46111d1c7..65cd4ffc09908 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.test.tsx @@ -437,7 +437,7 @@ describe('Response actions history page', () => { }, []); expect(selectedFilterOptions.length).toEqual(1); - expect(selectedFilterOptions).toEqual(['Endpoint. Checked option.']); + expect(selectedFilterOptions).toEqual(['Elastic Defend. Checked option.']); expect(history.location.search).toEqual('?agentTypes=endpoint'); }); });