mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security solution] [Hosts] Endpoint overview on host details page (#71466)
This commit is contained in:
parent
04cdb5ad6f
commit
f5259ed373
17 changed files with 677 additions and 164 deletions
|
@ -6525,10 +6525,18 @@
|
|||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lastSeen",
|
||||
"name": "cloud",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "endpoint",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "OBJECT", "name": "EndpointFields", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
@ -6540,14 +6548,6 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "cloud",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "inspect",
|
||||
"description": "",
|
||||
|
@ -6555,6 +6555,14 @@
|
|||
"type": { "kind": "OBJECT", "name": "Inspect", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lastSeen",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
@ -6659,6 +6667,65 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EndpointFields",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "endpointPolicy",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "sensorVersion",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "policyStatus",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "ENUM", "name": "HostPolicyResponseActionStatus", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "HostPolicyResponseActionStatus",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "success",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "failure",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{ "name": "warning", "description": "", "isDeprecated": false, "deprecationReason": null }
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "FirstLastSeenHost",
|
||||
|
|
|
@ -301,6 +301,12 @@ export enum HostsFields {
|
|||
lastSeen = 'lastSeen',
|
||||
}
|
||||
|
||||
export enum HostPolicyResponseActionStatus {
|
||||
success = 'success',
|
||||
failure = 'failure',
|
||||
warning = 'warning',
|
||||
}
|
||||
|
||||
export enum UsersFields {
|
||||
name = 'name',
|
||||
count = 'count',
|
||||
|
@ -1442,13 +1448,15 @@ export interface HostsEdges {
|
|||
export interface HostItem {
|
||||
_id?: Maybe<string>;
|
||||
|
||||
lastSeen?: Maybe<string>;
|
||||
cloud?: Maybe<CloudFields>;
|
||||
|
||||
endpoint?: Maybe<EndpointFields>;
|
||||
|
||||
host?: Maybe<HostEcsFields>;
|
||||
|
||||
cloud?: Maybe<CloudFields>;
|
||||
|
||||
inspect?: Maybe<Inspect>;
|
||||
|
||||
lastSeen?: Maybe<string>;
|
||||
}
|
||||
|
||||
export interface CloudFields {
|
||||
|
@ -1469,6 +1477,14 @@ export interface CloudMachine {
|
|||
type?: Maybe<(Maybe<string>)[]>;
|
||||
}
|
||||
|
||||
export interface EndpointFields {
|
||||
endpointPolicy?: Maybe<string>;
|
||||
|
||||
sensorVersion?: Maybe<string>;
|
||||
|
||||
policyStatus?: Maybe<HostPolicyResponseActionStatus>;
|
||||
}
|
||||
|
||||
export interface FirstLastSeenHost {
|
||||
inspect?: Maybe<Inspect>;
|
||||
|
||||
|
@ -3044,6 +3060,8 @@ export namespace GetHostOverviewQuery {
|
|||
cloud: Maybe<Cloud>;
|
||||
|
||||
inspect: Maybe<Inspect>;
|
||||
|
||||
endpoint: Maybe<Endpoint>;
|
||||
};
|
||||
|
||||
export type Host = {
|
||||
|
@ -3107,6 +3125,16 @@ export namespace GetHostOverviewQuery {
|
|||
|
||||
response: string[];
|
||||
};
|
||||
|
||||
export type Endpoint = {
|
||||
__typename?: 'EndpointFields';
|
||||
|
||||
endpointPolicy: Maybe<string>;
|
||||
|
||||
policyStatus: Maybe<HostPolicyResponseActionStatus>;
|
||||
|
||||
sensorVersion: Maybe<string>;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace GetKpiHostDetailsQuery {
|
||||
|
|
|
@ -46,6 +46,11 @@ export const HostOverviewQuery = gql`
|
|||
dsl
|
||||
response
|
||||
}
|
||||
endpoint {
|
||||
endpointPolicy
|
||||
policyStatus
|
||||
sensorVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { TestProviders } from '../../../../common/mock';
|
||||
|
||||
import { EndpointOverview } from './index';
|
||||
import { HostPolicyResponseActionStatus } from '../../../../graphql/types';
|
||||
|
||||
describe('EndpointOverview Component', () => {
|
||||
test('it renders with endpoint data', () => {
|
||||
const endpointData = {
|
||||
endpointPolicy: 'demo',
|
||||
policyStatus: HostPolicyResponseActionStatus.success,
|
||||
sensorVersion: '7.9.0-SNAPSHOT',
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EndpointOverview data={endpointData} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
const findData = wrapper.find(
|
||||
'dl[data-test-subj="endpoint-overview"] dd.euiDescriptionList__description'
|
||||
);
|
||||
expect(findData.at(0).text()).toEqual(endpointData.endpointPolicy);
|
||||
expect(findData.at(1).text()).toEqual(endpointData.policyStatus);
|
||||
expect(findData.at(2).text()).toContain(endpointData.sensorVersion); // contain because drag adds a space
|
||||
});
|
||||
test('it renders with null data', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EndpointOverview data={null} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
const findData = wrapper.find(
|
||||
'dl[data-test-subj="endpoint-overview"] dd.euiDescriptionList__description'
|
||||
);
|
||||
expect(findData.at(0).text()).toEqual('—');
|
||||
expect(findData.at(1).text()).toEqual('—');
|
||||
expect(findData.at(2).text()).toContain('—'); // contain because drag adds a space
|
||||
});
|
||||
});
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexItem, EuiHealth } from '@elastic/eui';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { DescriptionList } from '../../../../../common/utility_types';
|
||||
import { getEmptyTagValue } from '../../../../common/components/empty_value';
|
||||
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
|
||||
import { EndpointFields, HostPolicyResponseActionStatus } from '../../../../graphql/types';
|
||||
import { DescriptionListStyled } from '../../../../common/components/page';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface Props {
|
||||
data: EndpointFields | null;
|
||||
}
|
||||
|
||||
const getDescriptionList = (descriptionList: DescriptionList[], key: number) => (
|
||||
<EuiFlexItem key={key}>
|
||||
<DescriptionListStyled data-test-subj="endpoint-overview" listItems={descriptionList} />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
export const EndpointOverview = React.memo<Props>(({ data }) => {
|
||||
const getDefaultRenderer = useCallback(
|
||||
(fieldName: string, fieldData: EndpointFields, attrName: string) => (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={[getOr('', fieldName, fieldData)]}
|
||||
attrName={attrName}
|
||||
idPrefix="endpoint-overview"
|
||||
/>
|
||||
),
|
||||
[]
|
||||
);
|
||||
const descriptionLists: Readonly<DescriptionList[][]> = useMemo(
|
||||
() => [
|
||||
[
|
||||
{
|
||||
title: i18n.ENDPOINT_POLICY,
|
||||
description:
|
||||
data != null && data.endpointPolicy != null ? data.endpointPolicy : getEmptyTagValue(),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
title: i18n.POLICY_STATUS,
|
||||
description:
|
||||
data != null && data.policyStatus != null ? (
|
||||
<EuiHealth
|
||||
aria-label={data.policyStatus}
|
||||
color={
|
||||
data.policyStatus === HostPolicyResponseActionStatus.failure
|
||||
? 'danger'
|
||||
: data.policyStatus
|
||||
}
|
||||
>
|
||||
{data.policyStatus}
|
||||
</EuiHealth>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
title: i18n.SENSORVERSION,
|
||||
description:
|
||||
data != null && data.sensorVersion != null
|
||||
? getDefaultRenderer('sensorVersion', data, 'agent.version')
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
],
|
||||
[], // needs 4 columns for design
|
||||
],
|
||||
[data, getDefaultRenderer]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{descriptionLists.map((descriptionList, index) => getDescriptionList(descriptionList, index))}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
EndpointOverview.displayName = 'EndpointOverview';
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const ENDPOINT_POLICY = i18n.translate(
|
||||
'xpack.securitySolution.host.details.endpoint.endpointPolicy',
|
||||
{
|
||||
defaultMessage: 'Endpoint policy',
|
||||
}
|
||||
);
|
||||
|
||||
export const POLICY_STATUS = i18n.translate(
|
||||
'xpack.securitySolution.host.details.endpoint.policyStatus',
|
||||
{
|
||||
defaultMessage: 'Policy status',
|
||||
}
|
||||
);
|
||||
|
||||
export const SENSORVERSION = i18n.translate(
|
||||
'xpack.securitySolution.host.details.endpoint.sensorversion',
|
||||
{
|
||||
defaultMessage: 'Sensorversion',
|
||||
}
|
||||
);
|
|
@ -11,7 +11,6 @@ import { TestProviders } from '../../../common/mock';
|
|||
import { HostOverview } from './index';
|
||||
import { mockData } from './mock';
|
||||
import { mockAnomalies } from '../../../common/components/ml/mock';
|
||||
|
||||
describe('Host Summary Component', () => {
|
||||
describe('rendering', () => {
|
||||
test('it renders the default Host Summary', () => {
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
|
||||
import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { DEFAULT_DARK_MODE } from '../../../../common/constants';
|
||||
import { DescriptionList } from '../../../../common/utility_types';
|
||||
|
@ -33,6 +33,7 @@ import {
|
|||
} from '../../../hosts/components/first_last_seen_host';
|
||||
|
||||
import * as i18n from './translations';
|
||||
import { EndpointOverview } from './endpoint_overview';
|
||||
|
||||
interface HostSummaryProps {
|
||||
data: HostItem;
|
||||
|
@ -53,143 +54,183 @@ const getDescriptionList = (descriptionList: DescriptionList[], key: number) =>
|
|||
|
||||
export const HostOverview = React.memo<HostSummaryProps>(
|
||||
({
|
||||
data,
|
||||
loading,
|
||||
id,
|
||||
startDate,
|
||||
endDate,
|
||||
isLoadingAnomaliesData,
|
||||
anomaliesData,
|
||||
data,
|
||||
endDate,
|
||||
id,
|
||||
isLoadingAnomaliesData,
|
||||
loading,
|
||||
narrowDateRange,
|
||||
startDate,
|
||||
}) => {
|
||||
const capabilities = useMlCapabilities();
|
||||
const userPermissions = hasMlUserPermissions(capabilities);
|
||||
const [darkMode] = useUiSetting$<boolean>(DEFAULT_DARK_MODE);
|
||||
|
||||
const getDefaultRenderer = (fieldName: string, fieldData: HostItem) => (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], fieldName, fieldData)}
|
||||
attrName={fieldName}
|
||||
idPrefix="host-overview"
|
||||
/>
|
||||
const getDefaultRenderer = useCallback(
|
||||
(fieldName: string, fieldData: HostItem) => (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], fieldName, fieldData)}
|
||||
attrName={fieldName}
|
||||
idPrefix="host-overview"
|
||||
/>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const column: DescriptionList[] = [
|
||||
{
|
||||
title: i18n.HOST_ID,
|
||||
description: data.host
|
||||
? hostIdRenderer({ host: data.host, noLink: true })
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
{
|
||||
title: i18n.FIRST_SEEN,
|
||||
description:
|
||||
data.host != null && data.host.name && data.host.name.length ? (
|
||||
<FirstLastSeenHost
|
||||
hostname={data.host.name[0]}
|
||||
type={FirstLastSeenHostType.FIRST_SEEN}
|
||||
/>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.LAST_SEEN,
|
||||
description:
|
||||
data.host != null && data.host.name && data.host.name.length ? (
|
||||
<FirstLastSeenHost
|
||||
hostname={data.host.name[0]}
|
||||
type={FirstLastSeenHostType.LAST_SEEN}
|
||||
/>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
},
|
||||
];
|
||||
const firstColumn = userPermissions
|
||||
? [
|
||||
...column,
|
||||
const column: DescriptionList[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: i18n.HOST_ID,
|
||||
description: data.host
|
||||
? hostIdRenderer({ host: data.host, noLink: true })
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
{
|
||||
title: i18n.FIRST_SEEN,
|
||||
description:
|
||||
data.host != null && data.host.name && data.host.name.length ? (
|
||||
<FirstLastSeenHost
|
||||
hostname={data.host.name[0]}
|
||||
type={FirstLastSeenHostType.FIRST_SEEN}
|
||||
/>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.LAST_SEEN,
|
||||
description:
|
||||
data.host != null && data.host.name && data.host.name.length ? (
|
||||
<FirstLastSeenHost
|
||||
hostname={data.host.name[0]}
|
||||
type={FirstLastSeenHostType.LAST_SEEN}
|
||||
/>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
},
|
||||
],
|
||||
[data]
|
||||
);
|
||||
const firstColumn = useMemo(
|
||||
() =>
|
||||
userPermissions
|
||||
? [
|
||||
...column,
|
||||
{
|
||||
title: i18n.MAX_ANOMALY_SCORE_BY_JOB,
|
||||
description: (
|
||||
<AnomalyScores
|
||||
anomalies={anomaliesData}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
isLoading={isLoadingAnomaliesData}
|
||||
narrowDateRange={narrowDateRange}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
: column,
|
||||
[
|
||||
anomaliesData,
|
||||
column,
|
||||
endDate,
|
||||
isLoadingAnomaliesData,
|
||||
narrowDateRange,
|
||||
startDate,
|
||||
userPermissions,
|
||||
]
|
||||
);
|
||||
|
||||
const descriptionLists: Readonly<DescriptionList[][]> = useMemo(
|
||||
() => [
|
||||
firstColumn,
|
||||
[
|
||||
{
|
||||
title: i18n.MAX_ANOMALY_SCORE_BY_JOB,
|
||||
title: i18n.IP_ADDRESSES,
|
||||
description: (
|
||||
<AnomalyScores
|
||||
anomalies={anomaliesData}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
isLoading={isLoadingAnomaliesData}
|
||||
narrowDateRange={narrowDateRange}
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], 'host.ip', data)}
|
||||
attrName={'host.ip'}
|
||||
idPrefix="host-overview"
|
||||
render={(ip) => (ip != null ? <IPDetailsLink ip={ip} /> : getEmptyTagValue())}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
: column;
|
||||
|
||||
const descriptionLists: Readonly<DescriptionList[][]> = [
|
||||
firstColumn,
|
||||
[
|
||||
{
|
||||
title: i18n.IP_ADDRESSES,
|
||||
description: (
|
||||
<DefaultFieldRenderer
|
||||
rowItems={getOr([], 'host.ip', data)}
|
||||
attrName={'host.ip'}
|
||||
idPrefix="host-overview"
|
||||
render={(ip) => (ip != null ? <IPDetailsLink ip={ip} /> : getEmptyTagValue())}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.MAC_ADDRESSES,
|
||||
description: getDefaultRenderer('host.mac', data),
|
||||
},
|
||||
{ title: i18n.PLATFORM, description: getDefaultRenderer('host.os.platform', data) },
|
||||
{
|
||||
title: i18n.MAC_ADDRESSES,
|
||||
description: getDefaultRenderer('host.mac', data),
|
||||
},
|
||||
{ title: i18n.PLATFORM, description: getDefaultRenderer('host.os.platform', data) },
|
||||
],
|
||||
[
|
||||
{ title: i18n.OS, description: getDefaultRenderer('host.os.name', data) },
|
||||
{ title: i18n.FAMILY, description: getDefaultRenderer('host.os.family', data) },
|
||||
{ title: i18n.VERSION, description: getDefaultRenderer('host.os.version', data) },
|
||||
{ title: i18n.ARCHITECTURE, description: getDefaultRenderer('host.architecture', data) },
|
||||
],
|
||||
[
|
||||
{
|
||||
title: i18n.CLOUD_PROVIDER,
|
||||
description: getDefaultRenderer('cloud.provider', data),
|
||||
},
|
||||
{
|
||||
title: i18n.REGION,
|
||||
description: getDefaultRenderer('cloud.region', data),
|
||||
},
|
||||
{
|
||||
title: i18n.INSTANCE_ID,
|
||||
description: getDefaultRenderer('cloud.instance.id', data),
|
||||
},
|
||||
{
|
||||
title: i18n.MACHINE_TYPE,
|
||||
description: getDefaultRenderer('cloud.machine.type', data),
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
{ title: i18n.OS, description: getDefaultRenderer('host.os.name', data) },
|
||||
{ title: i18n.FAMILY, description: getDefaultRenderer('host.os.family', data) },
|
||||
{ title: i18n.VERSION, description: getDefaultRenderer('host.os.version', data) },
|
||||
{ title: i18n.ARCHITECTURE, description: getDefaultRenderer('host.architecture', data) },
|
||||
],
|
||||
[
|
||||
{
|
||||
title: i18n.CLOUD_PROVIDER,
|
||||
description: getDefaultRenderer('cloud.provider', data),
|
||||
},
|
||||
{
|
||||
title: i18n.REGION,
|
||||
description: getDefaultRenderer('cloud.region', data),
|
||||
},
|
||||
{
|
||||
title: i18n.INSTANCE_ID,
|
||||
description: getDefaultRenderer('cloud.instance.id', data),
|
||||
},
|
||||
{
|
||||
title: i18n.MACHINE_TYPE,
|
||||
description: getDefaultRenderer('cloud.machine.type', data),
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
[data, firstColumn, getDefaultRenderer]
|
||||
);
|
||||
return (
|
||||
<InspectButtonContainer>
|
||||
<OverviewWrapper>
|
||||
<InspectButton queryId={id} title={i18n.INSPECT_TITLE} inspectIndex={0} />
|
||||
<>
|
||||
<InspectButtonContainer>
|
||||
<OverviewWrapper>
|
||||
<InspectButton queryId={id} title={i18n.INSPECT_TITLE} inspectIndex={0} />
|
||||
|
||||
{descriptionLists.map((descriptionList, index) =>
|
||||
getDescriptionList(descriptionList, index)
|
||||
)}
|
||||
{descriptionLists.map((descriptionList, index) =>
|
||||
getDescriptionList(descriptionList, index)
|
||||
)}
|
||||
|
||||
{loading && (
|
||||
<Loader
|
||||
overlay
|
||||
overlayBackground={
|
||||
darkMode ? darkTheme.euiPageBackgroundColor : lightTheme.euiPageBackgroundColor
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
)}
|
||||
</OverviewWrapper>
|
||||
</InspectButtonContainer>
|
||||
{loading && (
|
||||
<Loader
|
||||
overlay
|
||||
overlayBackground={
|
||||
darkMode ? darkTheme.euiPageBackgroundColor : lightTheme.euiPageBackgroundColor
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
)}
|
||||
</OverviewWrapper>
|
||||
</InspectButtonContainer>
|
||||
{data.endpoint != null ? (
|
||||
<>
|
||||
<EuiHorizontalRule />
|
||||
<OverviewWrapper>
|
||||
<EndpointOverview data={data.endpoint} />
|
||||
|
||||
{loading && (
|
||||
<Loader
|
||||
overlay
|
||||
overlayBackground={
|
||||
darkMode ? darkTheme.euiPageBackgroundColor : lightTheme.euiPageBackgroundColor
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
)}
|
||||
</OverviewWrapper>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
import { IRouter, Logger, RequestHandlerContext } from 'kibana/server';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import Boom from 'boom';
|
||||
|
||||
import { metadataIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { getESQueryHostMetadataByID, kibanaRequestToMetadataListESQuery } from './query_builders';
|
||||
import {
|
||||
|
|
|
@ -41,12 +41,25 @@ export const hostsSchema = gql`
|
|||
region: [String]
|
||||
}
|
||||
|
||||
enum HostPolicyResponseActionStatus {
|
||||
success
|
||||
failure
|
||||
warning
|
||||
}
|
||||
|
||||
type EndpointFields {
|
||||
endpointPolicy: String
|
||||
sensorVersion: String
|
||||
policyStatus: HostPolicyResponseActionStatus
|
||||
}
|
||||
|
||||
type HostItem {
|
||||
_id: String
|
||||
lastSeen: Date
|
||||
host: HostEcsFields
|
||||
cloud: CloudFields
|
||||
endpoint: EndpointFields
|
||||
host: HostEcsFields
|
||||
inspect: Inspect
|
||||
lastSeen: Date
|
||||
}
|
||||
|
||||
type HostsEdges {
|
||||
|
|
|
@ -303,6 +303,12 @@ export enum HostsFields {
|
|||
lastSeen = 'lastSeen',
|
||||
}
|
||||
|
||||
export enum HostPolicyResponseActionStatus {
|
||||
success = 'success',
|
||||
failure = 'failure',
|
||||
warning = 'warning',
|
||||
}
|
||||
|
||||
export enum UsersFields {
|
||||
name = 'name',
|
||||
count = 'count',
|
||||
|
@ -1444,13 +1450,15 @@ export interface HostsEdges {
|
|||
export interface HostItem {
|
||||
_id?: Maybe<string>;
|
||||
|
||||
lastSeen?: Maybe<string>;
|
||||
cloud?: Maybe<CloudFields>;
|
||||
|
||||
endpoint?: Maybe<EndpointFields>;
|
||||
|
||||
host?: Maybe<HostEcsFields>;
|
||||
|
||||
cloud?: Maybe<CloudFields>;
|
||||
|
||||
inspect?: Maybe<Inspect>;
|
||||
|
||||
lastSeen?: Maybe<string>;
|
||||
}
|
||||
|
||||
export interface CloudFields {
|
||||
|
@ -1471,6 +1479,14 @@ export interface CloudMachine {
|
|||
type?: Maybe<(Maybe<string>)[]>;
|
||||
}
|
||||
|
||||
export interface EndpointFields {
|
||||
endpointPolicy?: Maybe<string>;
|
||||
|
||||
sensorVersion?: Maybe<string>;
|
||||
|
||||
policyStatus?: Maybe<HostPolicyResponseActionStatus>;
|
||||
}
|
||||
|
||||
export interface FirstLastSeenHost {
|
||||
inspect?: Maybe<Inspect>;
|
||||
|
||||
|
@ -6325,13 +6341,15 @@ export namespace HostItemResolvers {
|
|||
export interface Resolvers<TContext = SiemContext, TypeParent = HostItem> {
|
||||
_id?: _IdResolver<Maybe<string>, TypeParent, TContext>;
|
||||
|
||||
lastSeen?: LastSeenResolver<Maybe<string>, TypeParent, TContext>;
|
||||
cloud?: CloudResolver<Maybe<CloudFields>, TypeParent, TContext>;
|
||||
|
||||
endpoint?: EndpointResolver<Maybe<EndpointFields>, TypeParent, TContext>;
|
||||
|
||||
host?: HostResolver<Maybe<HostEcsFields>, TypeParent, TContext>;
|
||||
|
||||
cloud?: CloudResolver<Maybe<CloudFields>, TypeParent, TContext>;
|
||||
|
||||
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
|
||||
|
||||
lastSeen?: LastSeenResolver<Maybe<string>, TypeParent, TContext>;
|
||||
}
|
||||
|
||||
export type _IdResolver<R = Maybe<string>, Parent = HostItem, TContext = SiemContext> = Resolver<
|
||||
|
@ -6339,8 +6357,13 @@ export namespace HostItemResolvers {
|
|||
Parent,
|
||||
TContext
|
||||
>;
|
||||
export type LastSeenResolver<
|
||||
R = Maybe<string>,
|
||||
export type CloudResolver<
|
||||
R = Maybe<CloudFields>,
|
||||
Parent = HostItem,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
export type EndpointResolver<
|
||||
R = Maybe<EndpointFields>,
|
||||
Parent = HostItem,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
|
@ -6349,13 +6372,13 @@ export namespace HostItemResolvers {
|
|||
Parent = HostItem,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
export type CloudResolver<
|
||||
R = Maybe<CloudFields>,
|
||||
export type InspectResolver<
|
||||
R = Maybe<Inspect>,
|
||||
Parent = HostItem,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
export type InspectResolver<
|
||||
R = Maybe<Inspect>,
|
||||
export type LastSeenResolver<
|
||||
R = Maybe<string>,
|
||||
Parent = HostItem,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
|
@ -6418,6 +6441,36 @@ export namespace CloudMachineResolvers {
|
|||
> = Resolver<R, Parent, TContext>;
|
||||
}
|
||||
|
||||
export namespace EndpointFieldsResolvers {
|
||||
export interface Resolvers<TContext = SiemContext, TypeParent = EndpointFields> {
|
||||
endpointPolicy?: EndpointPolicyResolver<Maybe<string>, TypeParent, TContext>;
|
||||
|
||||
sensorVersion?: SensorVersionResolver<Maybe<string>, TypeParent, TContext>;
|
||||
|
||||
policyStatus?: PolicyStatusResolver<
|
||||
Maybe<HostPolicyResponseActionStatus>,
|
||||
TypeParent,
|
||||
TContext
|
||||
>;
|
||||
}
|
||||
|
||||
export type EndpointPolicyResolver<
|
||||
R = Maybe<string>,
|
||||
Parent = EndpointFields,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
export type SensorVersionResolver<
|
||||
R = Maybe<string>,
|
||||
Parent = EndpointFields,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
export type PolicyStatusResolver<
|
||||
R = Maybe<HostPolicyResponseActionStatus>,
|
||||
Parent = EndpointFields,
|
||||
TContext = SiemContext
|
||||
> = Resolver<R, Parent, TContext>;
|
||||
}
|
||||
|
||||
export namespace FirstLastSeenHostResolvers {
|
||||
export interface Resolvers<TContext = SiemContext, TypeParent = FirstLastSeenHost> {
|
||||
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
|
||||
|
@ -9331,6 +9384,7 @@ export type IResolvers<TContext = SiemContext> = {
|
|||
CloudFields?: CloudFieldsResolvers.Resolvers<TContext>;
|
||||
CloudInstance?: CloudInstanceResolvers.Resolvers<TContext>;
|
||||
CloudMachine?: CloudMachineResolvers.Resolvers<TContext>;
|
||||
EndpointFields?: EndpointFieldsResolvers.Resolvers<TContext>;
|
||||
FirstLastSeenHost?: FirstLastSeenHostResolvers.Resolvers<TContext>;
|
||||
IpOverviewData?: IpOverviewDataResolvers.Resolvers<TContext>;
|
||||
Overview?: OverviewResolvers.Resolvers<TContext>;
|
||||
|
|
|
@ -32,11 +32,13 @@ import * as note from '../note/saved_object';
|
|||
import * as pinnedEvent from '../pinned_event/saved_object';
|
||||
import * as timeline from '../timeline/saved_object';
|
||||
import { ElasticsearchMatrixHistogramAdapter, MatrixHistogram } from '../matrix_histogram';
|
||||
import { EndpointAppContext } from '../../endpoint/types';
|
||||
|
||||
export function compose(
|
||||
core: CoreSetup,
|
||||
plugins: SetupPlugins,
|
||||
isProductionMode: boolean
|
||||
isProductionMode: boolean,
|
||||
endpointContext: EndpointAppContext
|
||||
): AppBackendLibs {
|
||||
const framework = new KibanaBackendFrameworkAdapter(core, plugins, isProductionMode);
|
||||
const sources = new Sources(new ConfigurationSourcesAdapter());
|
||||
|
@ -46,7 +48,7 @@ export function compose(
|
|||
authentications: new Authentications(new ElasticsearchAuthenticationAdapter(framework)),
|
||||
events: new Events(new ElasticsearchEventsAdapter(framework)),
|
||||
fields: new IndexFields(new ElasticsearchIndexFieldAdapter(framework)),
|
||||
hosts: new Hosts(new ElasticsearchHostsAdapter(framework)),
|
||||
hosts: new Hosts(new ElasticsearchHostsAdapter(framework, endpointContext)),
|
||||
ipDetails: new IpDetails(new ElasticsearchIpDetailsAdapter(framework)),
|
||||
tls: new TLS(new ElasticsearchTlsAdapter(framework)),
|
||||
kpiHosts: new KpiHosts(new ElasticsearchKpiHostsAdapter(framework)),
|
||||
|
|
|
@ -9,6 +9,7 @@ import { FrameworkAdapter, FrameworkRequest } from '../framework';
|
|||
|
||||
import { ElasticsearchHostsAdapter, formatHostEdgesData } from './elasticsearch_adapter';
|
||||
import {
|
||||
mockEndpointMetadata,
|
||||
mockGetHostOverviewOptions,
|
||||
mockGetHostOverviewRequest,
|
||||
mockGetHostOverviewResponse,
|
||||
|
@ -26,6 +27,10 @@ import {
|
|||
mockGetHostsQueryDsl,
|
||||
} from './mock';
|
||||
import { HostAggEsItem } from './types';
|
||||
import { EndpointAppContext } from '../../endpoint/types';
|
||||
import { mockLogger } from '../detection_engine/signals/__mocks__/es_results';
|
||||
import { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services';
|
||||
import { createMockEndpointAppContextServiceStartContract } from '../../endpoint/mocks';
|
||||
|
||||
jest.mock('./query.hosts.dsl', () => {
|
||||
return {
|
||||
|
@ -44,6 +49,11 @@ jest.mock('./query.last_first_seen_host.dsl', () => {
|
|||
buildLastFirstSeenHostQuery: jest.fn(() => mockGetHostLastFirstSeenDsl),
|
||||
};
|
||||
});
|
||||
jest.mock('../../endpoint/routes/metadata', () => {
|
||||
return {
|
||||
getHostData: jest.fn(() => mockEndpointMetadata),
|
||||
};
|
||||
});
|
||||
|
||||
describe('hosts elasticsearch_adapter', () => {
|
||||
describe('#formatHostsData', () => {
|
||||
|
@ -155,6 +165,15 @@ describe('hosts elasticsearch_adapter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
const endpointAppContextService = new EndpointAppContextService();
|
||||
const startContract = createMockEndpointAppContextServiceStartContract();
|
||||
endpointAppContextService.start(startContract);
|
||||
|
||||
const endpointContext: EndpointAppContext = {
|
||||
logFactory: mockLogger,
|
||||
service: endpointAppContextService,
|
||||
config: jest.fn(),
|
||||
};
|
||||
describe('#getHosts', () => {
|
||||
const mockCallWithRequest = jest.fn();
|
||||
mockCallWithRequest.mockResolvedValue(mockGetHostsResponse);
|
||||
|
@ -166,7 +185,7 @@ describe('hosts elasticsearch_adapter', () => {
|
|||
jest.doMock('../framework', () => ({ callWithRequest: mockCallWithRequest }));
|
||||
|
||||
test('Happy Path', async () => {
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework);
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework, endpointContext);
|
||||
const data: HostsData = await EsHosts.getHosts(
|
||||
mockGetHostsRequest as FrameworkRequest,
|
||||
mockGetHostsOptions
|
||||
|
@ -186,7 +205,7 @@ describe('hosts elasticsearch_adapter', () => {
|
|||
jest.doMock('../framework', () => ({ callWithRequest: mockCallWithRequest }));
|
||||
|
||||
test('Happy Path', async () => {
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework);
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework, endpointContext);
|
||||
const data: HostItem = await EsHosts.getHostOverview(
|
||||
mockGetHostOverviewRequest as FrameworkRequest,
|
||||
mockGetHostOverviewOptions
|
||||
|
@ -206,7 +225,7 @@ describe('hosts elasticsearch_adapter', () => {
|
|||
jest.doMock('../framework', () => ({ callWithRequest: mockCallWithRequest }));
|
||||
|
||||
test('Happy Path', async () => {
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework);
|
||||
const EsHosts = new ElasticsearchHostsAdapter(mockFramework, endpointContext);
|
||||
const data: FirstLastSeenHost = await EsHosts.getHostFirstLastSeen(
|
||||
mockGetHostLastFirstSeenRequest as FrameworkRequest,
|
||||
mockGetHostLastFirstSeenOptions
|
||||
|
|
|
@ -6,12 +6,17 @@
|
|||
|
||||
import { get, getOr, has, head, set } from 'lodash/fp';
|
||||
|
||||
import { FirstLastSeenHost, HostItem, HostsData, HostsEdges } from '../../graphql/types';
|
||||
import {
|
||||
FirstLastSeenHost,
|
||||
HostItem,
|
||||
HostsData,
|
||||
HostsEdges,
|
||||
EndpointFields,
|
||||
} from '../../graphql/types';
|
||||
import { inspectStringifyObject } from '../../utils/build_query';
|
||||
import { hostFieldsMap } from '../ecs_fields';
|
||||
import { FrameworkAdapter, FrameworkRequest } from '../framework';
|
||||
import { TermAggregation } from '../types';
|
||||
|
||||
import { buildHostOverviewQuery } from './query.detail_host.dsl';
|
||||
import { buildHostsQuery } from './query.hosts.dsl';
|
||||
import { buildLastFirstSeenHostQuery } from './query.last_first_seen_host.dsl';
|
||||
|
@ -27,9 +32,14 @@ import {
|
|||
HostValue,
|
||||
} from './types';
|
||||
import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants';
|
||||
import { EndpointAppContext } from '../../endpoint/types';
|
||||
import { getHostData } from '../../endpoint/routes/metadata';
|
||||
|
||||
export class ElasticsearchHostsAdapter implements HostsAdapter {
|
||||
constructor(private readonly framework: FrameworkAdapter) {}
|
||||
constructor(
|
||||
private readonly framework: FrameworkAdapter,
|
||||
private readonly endpointContext: EndpointAppContext
|
||||
) {}
|
||||
|
||||
public async getHosts(
|
||||
request: FrameworkRequest,
|
||||
|
@ -83,8 +93,47 @@ export class ElasticsearchHostsAdapter implements HostsAdapter {
|
|||
dsl: [inspectStringifyObject(dsl)],
|
||||
response: [inspectStringifyObject(response)],
|
||||
};
|
||||
const formattedHostItem = formatHostItem(options.fields, aggregations);
|
||||
const hostId =
|
||||
formattedHostItem.host && formattedHostItem.host.id
|
||||
? Array.isArray(formattedHostItem.host.id)
|
||||
? formattedHostItem.host.id[0]
|
||||
: formattedHostItem.host.id
|
||||
: null;
|
||||
const endpoint: EndpointFields | null = await this.getHostEndpoint(request, hostId);
|
||||
return { inspect, _id: options.hostName, ...formattedHostItem, endpoint };
|
||||
}
|
||||
|
||||
return { inspect, _id: options.hostName, ...formatHostItem(options.fields, aggregations) };
|
||||
public async getHostEndpoint(
|
||||
request: FrameworkRequest,
|
||||
hostId: string | null
|
||||
): Promise<EndpointFields | null> {
|
||||
const logger = this.endpointContext.logFactory.get('metadata');
|
||||
try {
|
||||
const agentService = this.endpointContext.service.getAgentService();
|
||||
if (agentService === undefined) {
|
||||
throw new Error('agentService not available');
|
||||
}
|
||||
const metadataRequestContext = {
|
||||
agentService,
|
||||
logger,
|
||||
requestHandlerContext: request.context,
|
||||
};
|
||||
const endpointData =
|
||||
hostId != null && metadataRequestContext.agentService != null
|
||||
? await getHostData(metadataRequestContext, hostId)
|
||||
: null;
|
||||
return endpointData != null && endpointData.metadata
|
||||
? {
|
||||
endpointPolicy: endpointData.metadata.Endpoint.policy.applied.name,
|
||||
policyStatus: endpointData.metadata.Endpoint.policy.applied.status,
|
||||
sensorVersion: endpointData.metadata.agent.version,
|
||||
}
|
||||
: null;
|
||||
} catch (err) {
|
||||
logger.warn(JSON.stringify(err, null, 2));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async getHostFirstLastSeen(
|
||||
|
|
|
@ -497,6 +497,11 @@ export const mockGetHostOverviewResult = {
|
|||
provider: ['gce'],
|
||||
region: ['us-east-1'],
|
||||
},
|
||||
endpoint: {
|
||||
endpointPolicy: 'demo',
|
||||
policyStatus: 'success',
|
||||
sensorVersion: '7.9.0-SNAPSHOT',
|
||||
},
|
||||
};
|
||||
|
||||
export const mockGetHostLastFirstSeenOptions: HostLastFirstSeenRequestOptions = {
|
||||
|
@ -564,3 +569,64 @@ export const mockGetHostLastFirstSeenResult = {
|
|||
firstSeen: '2019-02-22T03:41:32.826Z',
|
||||
lastSeen: '2019-04-09T16:18:12.178Z',
|
||||
};
|
||||
|
||||
export const mockEndpointMetadata = {
|
||||
metadata: {
|
||||
'@timestamp': '2020-07-13T01:08:37.68896700Z',
|
||||
Endpoint: {
|
||||
policy: {
|
||||
applied: { id: '3de86380-aa5a-11ea-b969-0bee1b260ab8', name: 'demo', status: 'success' },
|
||||
},
|
||||
status: 'enrolled',
|
||||
},
|
||||
agent: {
|
||||
build: {
|
||||
original:
|
||||
'version: 7.9.0-SNAPSHOT, compiled: Thu Jul 09 07:56:12 2020, branch: 7.x, commit: 713a1071de475f15b3a1f0944d3602ed532597a5',
|
||||
},
|
||||
id: 'c29e0de1-7476-480b-b242-38f0394bf6a1',
|
||||
type: 'endpoint',
|
||||
version: '7.9.0-SNAPSHOT',
|
||||
},
|
||||
dataset: { name: 'endpoint.metadata', namespace: 'default', type: 'metrics' },
|
||||
ecs: { version: '1.5.0' },
|
||||
elastic: { agent: { id: '' } },
|
||||
event: {
|
||||
action: 'endpoint_metadata',
|
||||
category: ['host'],
|
||||
created: '2020-07-13T01:08:37.68896700Z',
|
||||
dataset: 'endpoint.metadata',
|
||||
id: 'Lkio+AHbZGSPFb7q++++++2E',
|
||||
kind: 'metric',
|
||||
module: 'endpoint',
|
||||
sequence: 146,
|
||||
type: ['info'],
|
||||
},
|
||||
host: {
|
||||
architecture: 'x86_64',
|
||||
hostname: 'DESKTOP-4I1B23J',
|
||||
id: 'a4148b63-1758-ab1f-a6d3-f95075cb1a9c',
|
||||
ip: [
|
||||
'172.16.166.129',
|
||||
'fe80::c07e:eee9:3e8d:ea6d',
|
||||
'169.254.205.96',
|
||||
'fe80::1027:b13d:a4a7:cd60',
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
],
|
||||
mac: ['00:0c:29:89:ff:73', '3c:22:fb:3c:93:4c'],
|
||||
name: 'DESKTOP-4I1B23J',
|
||||
os: {
|
||||
Ext: { variant: 'Windows 10 Pro' },
|
||||
family: 'windows',
|
||||
full: 'Windows 10 Pro 2004 (10.0.19041.329)',
|
||||
kernel: '2004 (10.0.19041.329)',
|
||||
name: 'Windows',
|
||||
platform: 'windows',
|
||||
version: '2004 (10.0.19041.329)',
|
||||
},
|
||||
},
|
||||
message: 'Endpoint metadata',
|
||||
},
|
||||
host_status: 'error',
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@ import { EndpointAppContextService } from './endpoint/endpoint_app_context_servi
|
|||
import { EndpointAppContext } from './endpoint/types';
|
||||
import { registerDownloadExceptionListRoute } from './endpoint/routes/artifacts';
|
||||
import { initUsageCollectors } from './usage';
|
||||
import { AppRequestContext } from './types';
|
||||
|
||||
export interface SetupPlugins {
|
||||
alerts: AlertingSetup;
|
||||
|
@ -127,9 +128,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
};
|
||||
|
||||
const router = core.http.createRouter();
|
||||
core.http.registerRouteHandlerContext(APP_ID, (context, request, response) => ({
|
||||
getAppClient: () => this.appClientFactory.create(request),
|
||||
}));
|
||||
core.http.registerRouteHandlerContext(
|
||||
APP_ID,
|
||||
(context, request, response): AppRequestContext => ({
|
||||
getAppClient: () => this.appClientFactory.create(request),
|
||||
})
|
||||
);
|
||||
|
||||
this.appClientFactory.setup({
|
||||
getSpaceId: plugins.spaces?.spacesService?.getSpaceId,
|
||||
|
@ -144,7 +148,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins.security,
|
||||
plugins.ml
|
||||
);
|
||||
|
||||
registerEndpointRoutes(router, endpointContext);
|
||||
registerResolverRoutes(router, endpointContext);
|
||||
registerPolicyRoutes(router, endpointContext);
|
||||
|
@ -249,7 +252,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
});
|
||||
}
|
||||
|
||||
const libs = compose(core, plugins, this.context.env.mode.prod);
|
||||
const libs = compose(core, plugins, this.context.env.mode.prod, endpointContext);
|
||||
initServer(libs);
|
||||
|
||||
return {};
|
||||
|
|
|
@ -105,6 +105,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
it('Make sure that we get Host Overview data', () => {
|
||||
const expectedHost: Omit<GetHostOverviewQuery.HostOverview, 'inspect'> = {
|
||||
_id: 'zeek-sensor-san-francisco',
|
||||
endpoint: null,
|
||||
host: {
|
||||
architecture: ['x86_64'],
|
||||
id: [CURSOR_ID],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue