mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][Endpoint] Show integration type on Response Console header (#186855)
## Summary - Adds information about the integration associated with an agent type, which informs the user which EDR vendor is being used to execute the response action. The following views were updated: - Response console - Alert Isolation flyout panel headers > [!NOTE] > The host isolation flyout that is displayed from the Endpoint list page was not updated to show this information about the integration. Thats because from the Endpoint list, a user only sees hosts that are running the Elastic Defend integration, thus there is not need to show the indicator.
This commit is contained in:
parent
51a902b579
commit
41817d0b2b
16 changed files with 321 additions and 19 deletions
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import type { AppContextTestRender } from '../../../../mock/endpoint';
|
||||
import { createAppRootMockRenderer } from '../../../../mock/endpoint';
|
||||
import type { AgentTypeIntegrationProps } from './agent_type_integration';
|
||||
import { AgentTypeIntegration, INTEGRATION_SECTION_LABEL } from './agent_type_integration';
|
||||
import { getAgentTypeName } from '../../../../translations';
|
||||
import { RESPONSE_ACTION_AGENT_TYPE } from '../../../../../../common/endpoint/service/response_actions/constants';
|
||||
|
||||
describe('AgentTypeIntegration component', () => {
|
||||
let props: AgentTypeIntegrationProps;
|
||||
let render: () => ReturnType<AppContextTestRender['render']>;
|
||||
|
||||
describe.each(RESPONSE_ACTION_AGENT_TYPE)('for agent type: %s', (agentType) => {
|
||||
beforeEach(() => {
|
||||
const mockedContext = createAppRootMockRenderer();
|
||||
|
||||
props = { agentType, 'data-test-subj': 'test' };
|
||||
|
||||
render = () => {
|
||||
return mockedContext.render(<AgentTypeIntegration {...props} />);
|
||||
};
|
||||
});
|
||||
|
||||
it('should display agent type vendor name', () => {
|
||||
const { getByTestId } = render();
|
||||
|
||||
expect(getByTestId('test-name')).toHaveTextContent(getAgentTypeName(agentType));
|
||||
});
|
||||
|
||||
it('should display agent type vendor icon', () => {
|
||||
const { getByTestId } = render();
|
||||
|
||||
expect(getByTestId('test-vendorLogo'));
|
||||
});
|
||||
|
||||
it('should display label', () => {
|
||||
const { getByTestId } = render();
|
||||
|
||||
expect(getByTestId('test-label')).toHaveTextContent(INTEGRATION_SECTION_LABEL);
|
||||
});
|
||||
|
||||
it('should include tooltip', () => {
|
||||
const { getByTestId } = render();
|
||||
|
||||
expect(getByTestId('test-tooltipAnchor'));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 React, { memo } from 'react';
|
||||
import type { EuiTextProps } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTestIdGenerator } from '../../../../../management/hooks/use_test_id_generator';
|
||||
import { AgentTypeVendorLogo } from '../agent_type_vendor_logo';
|
||||
import { getAgentTypeName } from '../../../../translations';
|
||||
import type { ResponseActionAgentType } from '../../../../../../common/endpoint/service/response_actions/constants';
|
||||
|
||||
export const INTEGRATION_SECTION_LABEL = i18n.translate(
|
||||
'xpack.securitySolution.agentTypeIntegration.integrationSectionLabel',
|
||||
{ defaultMessage: 'Integration' }
|
||||
);
|
||||
|
||||
/**
|
||||
* Shows the vendor Icon and name associated with an agent type
|
||||
*/
|
||||
export interface AgentTypeIntegrationProps {
|
||||
agentType: ResponseActionAgentType;
|
||||
textSize?: EuiTextProps['size'];
|
||||
/**
|
||||
* If content should be shown vertically (label and integration name on separate lines) or horizontally (next to each other).
|
||||
* Defaults to `vertical`
|
||||
*/
|
||||
layout?: 'vertical' | 'horizontal';
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
export const AgentTypeIntegration = memo<AgentTypeIntegrationProps>(
|
||||
({ agentType, textSize = 's', layout = 'vertical', 'data-test-subj': dataTestSubj }) => {
|
||||
const testId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
direction={layout === 'horizontal' ? 'row' : 'column'}
|
||||
gutterSize="s"
|
||||
data-test-subj={testId()}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size={textSize} data-test-subj={testId('label')}>
|
||||
{INTEGRATION_SECTION_LABEL}
|
||||
<EuiIconTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.responder.header.integrationSectionLabelTooltip"
|
||||
defaultMessage="The integration used to execute response actions on this host"
|
||||
/>
|
||||
}
|
||||
position={layout === 'horizontal' ? 'bottom' : 'right'}
|
||||
anchorProps={{ 'data-test-subj': testId('tooltipAnchor') }}
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup responsive={false} wrap={false} gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<AgentTypeVendorLogo agentType={agentType} data-test-subj={testId('vendorLogo')} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size={textSize} data-test-subj={testId('name')}>
|
||||
{getAgentTypeName(agentType)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
);
|
||||
AgentTypeIntegration.displayName = 'AgentTypeIntegration';
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './agent_type_integration';
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 { RESPONSE_ACTION_AGENT_TYPE } from '../../../../../../common/endpoint/service/response_actions/constants';
|
||||
import type { AppContextTestRender } from '../../../../mock/endpoint';
|
||||
import { createAppRootMockRenderer } from '../../../../mock/endpoint';
|
||||
import React from 'react';
|
||||
import type { AgentTypeVendorLogoProps } from './agent_type_vendor_logo';
|
||||
import { AgentTypeVendorLogo } from './agent_type_vendor_logo';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { getAgentTypeName } from '../../../../translations';
|
||||
|
||||
describe('AgentTypeVendorLogo component', () => {
|
||||
let props: AgentTypeVendorLogoProps;
|
||||
let render: () => ReturnType<AppContextTestRender['render']>;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockedContext = createAppRootMockRenderer();
|
||||
|
||||
props = { agentType: 'endpoint', 'data-test-subj': 'test' };
|
||||
|
||||
render = () => {
|
||||
return mockedContext.render(<AgentTypeVendorLogo {...props} />);
|
||||
};
|
||||
});
|
||||
|
||||
it.each(RESPONSE_ACTION_AGENT_TYPE)('should display logo for: %s', async (agentType) => {
|
||||
props.agentType = agentType;
|
||||
const { getByTitle } = render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByTitle(getAgentTypeName(agentType)));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 React, { memo, useEffect, useState } from 'react';
|
||||
import type { EuiIconProps } from '@elastic/eui';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { useIsMounted } from '@kbn/securitysolution-hook-utils';
|
||||
import { getAgentTypeName } from '../../../../translations';
|
||||
import type { ResponseActionAgentType } from '../../../../../../common/endpoint/service/response_actions/constants';
|
||||
|
||||
const fetchSentinelOneLogoSvg = (): Promise<string> =>
|
||||
import('./images/sentinelone_logo.svg').then((response) => response.default);
|
||||
const fetchCrowdstrikeLogoSvg = (): Promise<string> =>
|
||||
import('./images/crowdstrike_logo.svg').then((response) => response.default);
|
||||
|
||||
export interface AgentTypeVendorLogoProps
|
||||
extends Pick<EuiIconProps, 'size' | 'data-test-subj' | 'color'> {
|
||||
agentType: ResponseActionAgentType;
|
||||
}
|
||||
|
||||
export const AgentTypeVendorLogo = memo<AgentTypeVendorLogoProps>(
|
||||
({ agentType, ...otherIconProps }) => {
|
||||
const isMounted = useIsMounted();
|
||||
const [iconType, setIconType] = useState<EuiIconProps['type']>('empty');
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted()) {
|
||||
const setSvgToState = (svgContent: string) => {
|
||||
if (isMounted()) {
|
||||
setIconType(svgContent);
|
||||
}
|
||||
};
|
||||
|
||||
switch (agentType) {
|
||||
case 'endpoint':
|
||||
setIconType('logoSecurity');
|
||||
break;
|
||||
|
||||
case 'sentinel_one':
|
||||
fetchSentinelOneLogoSvg().then(setSvgToState);
|
||||
break;
|
||||
|
||||
case 'crowdstrike':
|
||||
fetchCrowdstrikeLogoSvg().then(setSvgToState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [agentType, isMounted]);
|
||||
|
||||
return (
|
||||
<EuiIcon
|
||||
type={iconType}
|
||||
title={iconType === 'empty' ? '' : getAgentTypeName(agentType)}
|
||||
size="m"
|
||||
{...otherIconProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
AgentTypeVendorLogo.displayName = 'AgentTypeVendorLogo';
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 116.4 88.8" style="enable-background:new 0 0 116.4 88.8" xml:space="preserve"><style>.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#fc0000}</style><g id="Page-1"><g id="Home-v4.1" transform="translate(-26 -22)"><g id="CS-Logo" transform="translate(26 22)"><path id="Fill-110" class="st0" d="M113.3 71.7c-2.6-.2-7.1-.8-12.9 1.8-5.7 2.7-8 2.8-10.8 2.5.8 1.4 2.5 3.3 7.7 3.7 5.2.3 7.8.5 5 6.6.1-1.8-.4-5.4-5.6-4.8s-6.4 5-.8 7.1c-1.8.3-5.7.5-8.4-6.1-1.9.8-4.8 2.3-10.2-1.5 1.9.6 4.2.7 4.2.7-4.7-2.1-9.3-6-12.1-9.7 2.3 1.6 4.8 3.2 7.4 3.5-3-3.4-10-10.3-18.6-17.3 5.5 3.3 12.2 8.6 23 7.4 10.9-1.2 18.2-3.5 32.1 6.1"/><path id="Fill-112" class="st0" d="M67.4 70.4c-7.3-2.7-8.8-3.3-18.2-5.3-9.3-2.1-18.5-6.3-24.7-13 4.3 2.8 13.2 8.3 22.3 7.7-1.4-1.8-3.9-3.1-7-4.5 3.4.7 13.8 3 27.6 15.1"/><path id="Fill-118" class="st0" d="M104.1 64.3c6.4.6 6.1 1.5 6.1 3.1-2.7-2-6.1-3.1-6.1-3.1M65.5 31.2C37.9 23.3 26.9 13.4 18.4 3.1c3.9 11.9 13.1 16.2 23 24.2s10.5 12.3 13.4 17.1c6.5 10.6 7.5 12.3 14 16.9 7.6 5 16.8 1.6 26.9 3.2s18.4 9.2 20.2 12.1c2.1-3.7-2.9-9.1-4.3-10.5.7-4.9-10.9-7.1-15.4-8.7-.9-.3-3-.8-1.2-5.2 2.5-6.1 5.2-11.4-29.5-21"/><path id="Fill-114" class="st0" d="M52.1 45.9c-1.8-4.9-4.9-11.1-19.9-20.4C24.8 20.8 14.1 15 0 0c1 4 5.5 14.5 27.9 28.2 7.4 4.9 17 7.9 24.2 17.7"/><path id="Fill-116" class="st0" d="M52.1 55.1c-1.7-4.1-5.3-9.4-19-16.9-6.4-3.6-17.2-9.2-27-19.8.9 3.8 5.4 12.2 24.9 22.7 5.4 3.1 14.5 5.9 21.1 14"/></g></g></g></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><g stroke="null"><path fill="#6B0AEA" stroke="none" d="M13.367.09h4.295v25.68h-4.295zM18.703 31.968l4.292-2.66V7.077a10.707 10.707 0 0 0-4.292-1.901v26.793zm-10.68-2.646 4.294 2.66V5.187a10.7 10.7 0 0 0-4.294 1.888v22.246zM24.04 1.31v27.528l1.995-1.236a5.086 5.086 0 0 0 2.29-4.322v-13c.01-3.75-4.284-8.97-4.284-8.97zM2.698 23.27a5.076 5.076 0 0 0 2.29 4.322l1.995 1.237V1.311s-4.285 5.22-4.285 8.969v12.99z"/></g></svg>
|
After Width: | Height: | Size: 484 B |
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './agent_type_vendor_logo';
|
|
@ -95,6 +95,8 @@ describe('Isolation Flyout PanelHeader', () => {
|
|||
} else {
|
||||
expect(queryByTestId('flyoutHostIsolationHeaderBadge')).toBeNull();
|
||||
}
|
||||
|
||||
expect(getByTestId('flyoutHostIsolationHeaderIntegration'));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { AgentTypeIntegration } from '../../../common/components/endpoint/agents/agent_type_integration';
|
||||
import { useAlertResponseActionsSupport } from '../../../common/hooks/endpoint/use_alert_response_actions_support';
|
||||
import { TECHNICAL_PREVIEW, TECHNICAL_PREVIEW_TOOLTIP } from '../../../common/translations';
|
||||
import { useIsolateHostPanelContext } from './context';
|
||||
|
@ -33,6 +34,12 @@ export const PanelHeader: FC = () => {
|
|||
<EuiFlexGroup responsive gutterSize="s">
|
||||
<EuiFlexItem grow={false} data-test-subj="flyoutHostIsolationHeaderTitle">
|
||||
{isolateAction === 'isolateHost' ? ISOLATE_HOST : UNISOLATE_HOST}
|
||||
<EuiSpacer size="s" />
|
||||
<AgentTypeIntegration
|
||||
agentType={agentType}
|
||||
layout="horizontal"
|
||||
data-test-subj="flyoutHostIsolationHeaderIntegration"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{showTechPreviewBadge && (
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -55,12 +55,13 @@ export const ConsoleHeader = memo<ConsoleHeaderProps>(({ TitleComponent }) => {
|
|||
<EuiFlexItem
|
||||
grow={1}
|
||||
className="eui-textTruncate noThemeOverrides"
|
||||
css={{ maxWidth: '95%' }}
|
||||
data-test-subj={getTestId('titleComponentContainer')}
|
||||
>
|
||||
{TitleComponent ? <TitleComponent /> : ''}
|
||||
</EuiFlexItem>
|
||||
{!isHelpOpen && (
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<StyledEuiButtonEmpty
|
||||
style={{ marginLeft: 'auto' }}
|
||||
onClick={handleHelpButtonOnClick}
|
||||
|
|
|
@ -67,7 +67,7 @@ describe('Responder header Agent Info', () => {
|
|||
});
|
||||
render(agentType);
|
||||
|
||||
const name = await renderResult.findByTestId('responderHeaderHostName');
|
||||
const name = await renderResult.findByTestId('responseConsole-hostName');
|
||||
expect(name.textContent).toBe('test-agent');
|
||||
});
|
||||
|
||||
|
@ -102,7 +102,7 @@ describe('Responder header Agent Info', () => {
|
|||
});
|
||||
render(agentType);
|
||||
|
||||
const lastUpdated = await renderResult.findByTestId('responderHeaderLastSeen');
|
||||
const lastUpdated = await renderResult.findByTestId('responseConsole-lastSeen');
|
||||
expect(lastUpdated).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -116,8 +116,21 @@ describe('Responder header Agent Info', () => {
|
|||
});
|
||||
render(agentType);
|
||||
|
||||
const platformIcon = await renderResult.findByTestId('responderHeaderHostPlatformIcon');
|
||||
const platformIcon = await renderResult.findByTestId('responseConsole-platformIcon');
|
||||
expect(platformIcon).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show agent type integration info', async () => {
|
||||
getAgentStatusMock.mockReturnValue({
|
||||
data: {
|
||||
[agentId]: { ...baseData, agentType, status: HostStatus.HEALTHY },
|
||||
},
|
||||
isLoading: false,
|
||||
isFetched: true,
|
||||
});
|
||||
render(agentType);
|
||||
|
||||
expect(renderResult.getByTestId('responseConsole-integration'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,8 @@ export const AgentInfo = memo<AgentInfoProps>(({ agentId, platform, hostName, ag
|
|||
platform={platform.toLowerCase() as Platform}
|
||||
hostName={hostName}
|
||||
lastCheckin={lastCheckin}
|
||||
agentType={agentType}
|
||||
data-test-subj="responseConsole"
|
||||
>
|
||||
<AgentStatus
|
||||
agentId={agentId}
|
||||
|
|
|
@ -55,7 +55,7 @@ describe('Responder header endpoint info', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
it('should show endpoint name', async () => {
|
||||
const name = await renderResult.findByTestId('responderHeaderHostName');
|
||||
const name = await renderResult.findByTestId('responseConsole-hostName');
|
||||
expect(name.textContent).toBe(`${endpointDetails.metadata.host.name}`);
|
||||
});
|
||||
it('should show agent and isolation status', async () => {
|
||||
|
@ -65,11 +65,11 @@ describe('Responder header endpoint info', () => {
|
|||
expect(agentStatus.textContent).toBe(`Healthy`);
|
||||
});
|
||||
it('should show last checkin time', async () => {
|
||||
const lastUpdated = await renderResult.findByTestId('responderHeaderLastSeen');
|
||||
const lastUpdated = await renderResult.findByTestId('responseConsole-lastSeen');
|
||||
expect(lastUpdated).toBeTruthy();
|
||||
});
|
||||
it('should show platform icon', async () => {
|
||||
const platformIcon = await renderResult.findByTestId('responderHeaderHostPlatformIcon');
|
||||
const platformIcon = await renderResult.findByTestId('responseConsole-platformIcon');
|
||||
expect(platformIcon).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,6 +34,8 @@ export const HeaderEndpointInfo = memo<HeaderEndpointInfoProps>(({ endpointId })
|
|||
platform={endpointDetails.metadata.host.os.name.toLowerCase() as Platform}
|
||||
hostName={endpointDetails.metadata.host.name}
|
||||
lastCheckin={endpointDetails.last_checkin}
|
||||
agentType="endpoint"
|
||||
data-test-subj="responseConsole"
|
||||
>
|
||||
<AgentStatus
|
||||
agentId={endpointId}
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiToolTip } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui';
|
||||
import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react';
|
||||
import { AgentTypeIntegration } from '../../../../../common/components/endpoint/agents/agent_type_integration';
|
||||
import { useTestIdGenerator } from '../../../../hooks/use_test_id_generator';
|
||||
import type { ResponseActionAgentType } from '../../../../../../common/endpoint/service/response_actions/constants';
|
||||
import type { Platform } from './platforms';
|
||||
import { PlatformIcon } from './platforms';
|
||||
|
||||
|
@ -16,24 +19,36 @@ interface HeaderAgentInfoProps {
|
|||
hostName: string;
|
||||
lastCheckin: string;
|
||||
children: React.ReactNode;
|
||||
agentType?: ResponseActionAgentType;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
export const HeaderAgentInfo = memo<HeaderAgentInfoProps>(
|
||||
({ platform, hostName, lastCheckin, children }) => {
|
||||
({ platform, hostName, lastCheckin, agentType, 'data-test-subj': dataTestSubj, children }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const testId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexGroup
|
||||
gutterSize="s"
|
||||
responsive={false}
|
||||
alignItems="center"
|
||||
data-test-subj={testId('agentInfo')}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="center">
|
||||
<PlatformIcon data-test-subj="responderHeaderHostPlatformIcon" platform={platform} />
|
||||
</EuiFlexGroup>
|
||||
<PlatformIcon data-test-subj={testId('platformIcon')} platform={platform} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem grow={false} className="eui-textTruncate">
|
||||
<EuiFlexGroup direction="column" gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs">
|
||||
<EuiFlexItem grow={false} className="eui-textTruncate">
|
||||
<EuiToolTip content={hostName} anchorClassName="eui-textTruncate">
|
||||
<EuiText size="s" data-test-subj="responderHeaderHostName">
|
||||
<EuiText
|
||||
size="s"
|
||||
data-test-subj={testId('hostName')}
|
||||
className="eui-textTruncate"
|
||||
>
|
||||
<h6 className="eui-textTruncate">{hostName}</h6>
|
||||
</EuiText>
|
||||
</EuiToolTip>
|
||||
|
@ -41,9 +56,9 @@ export const HeaderAgentInfo = memo<HeaderAgentInfoProps>(
|
|||
<EuiFlexItem grow={false}>{children}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="xs" />
|
||||
<EuiText color="subdued" size="s" data-test-subj="responderHeaderLastSeen">
|
||||
<EuiText color="subdued" size="s" data-test-subj={testId('lastSeen')}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.responder.header.lastSeen"
|
||||
defaultMessage="Last seen {date}"
|
||||
|
@ -55,6 +70,12 @@ export const HeaderAgentInfo = memo<HeaderAgentInfoProps>(
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
{agentType && (
|
||||
<EuiFlexItem grow={false} css={{ paddingLeft: euiTheme.size.l }}>
|
||||
<AgentTypeIntegration agentType={agentType} data-test-subj={testId('integration')} />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue