Remove <7.14 EP Metrics from Security Solution usage collector (#103632)
* Remove <7.14 EP Metrics from Security Solution usage collector. * Update telemetry schema. * Fix reworked method signature.
This commit is contained in:
parent
cdfc90ca30
commit
096ee92f3b
|
@ -171,7 +171,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
|
||||
initUsageCollectors({
|
||||
core,
|
||||
endpointAppContext: endpointContext,
|
||||
kibanaIndex: globalConfig.kibana.index,
|
||||
signalsIndex: config.signalsIndex,
|
||||
ml: plugins.ml,
|
||||
|
|
|
@ -9,11 +9,9 @@ import { CoreSetup, SavedObjectsClientContract } from '../../../../../src/core/s
|
|||
import { CollectorFetchContext } from '../../../../../src/plugins/usage_collection/server';
|
||||
import { CollectorDependencies } from './types';
|
||||
import { fetchDetectionsMetrics } from './detections';
|
||||
import { EndpointUsage, getEndpointTelemetryFromFleet } from './endpoints';
|
||||
|
||||
export type RegisterCollector = (deps: CollectorDependencies) => void;
|
||||
export interface UsageData {
|
||||
endpoints: EndpointUsage | {};
|
||||
detectionMetrics: {};
|
||||
}
|
||||
|
||||
|
@ -25,7 +23,6 @@ export async function getInternalSavedObjectsClient(core: CoreSetup) {
|
|||
|
||||
export const registerCollector: RegisterCollector = ({
|
||||
core,
|
||||
endpointAppContext,
|
||||
kibanaIndex,
|
||||
signalsIndex,
|
||||
ml,
|
||||
|
@ -397,69 +394,15 @@ export const registerCollector: RegisterCollector = ({
|
|||
},
|
||||
},
|
||||
},
|
||||
endpoints: {
|
||||
total_installed: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The number of installed endpoints' },
|
||||
},
|
||||
active_within_last_24_hours: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The number of active endpoints' },
|
||||
},
|
||||
os: {
|
||||
type: 'array',
|
||||
items: {
|
||||
full_name: {
|
||||
type: 'keyword',
|
||||
_meta: { description: 'Full name of the operating system' },
|
||||
},
|
||||
platform: {
|
||||
type: 'keyword',
|
||||
_meta: { description: 'OS Platform. eg Centos, Ubuntu' },
|
||||
},
|
||||
version: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description:
|
||||
'The version of the operating system, eg 16.04.7 LTS (Xenial Xerus), 8 (Core)',
|
||||
},
|
||||
},
|
||||
count: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The total number of endpoints from that platform' },
|
||||
},
|
||||
},
|
||||
},
|
||||
policies: {
|
||||
malware: {
|
||||
active: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The total number of active malware policies' },
|
||||
},
|
||||
inactive: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The total number of inactive malware policies' },
|
||||
},
|
||||
failure: {
|
||||
type: 'long',
|
||||
_meta: { description: 'The total number of failing malware policies' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isReady: () => true,
|
||||
fetch: async ({ esClient }: CollectorFetchContext): Promise<UsageData> => {
|
||||
const internalSavedObjectsClient = await getInternalSavedObjectsClient(core);
|
||||
const savedObjectsClient = (internalSavedObjectsClient as unknown) as SavedObjectsClientContract;
|
||||
const [detectionMetrics, endpoints] = await Promise.allSettled([
|
||||
fetchDetectionsMetrics(kibanaIndex, signalsIndex, esClient, ml, savedObjectsClient),
|
||||
getEndpointTelemetryFromFleet(savedObjectsClient, endpointAppContext, esClient),
|
||||
]);
|
||||
const soClient = (internalSavedObjectsClient as unknown) as SavedObjectsClientContract;
|
||||
|
||||
return {
|
||||
detectionMetrics: detectionMetrics.status === 'fulfilled' ? detectionMetrics.value : {},
|
||||
endpoints: endpoints.status === 'fulfilled' ? endpoints.value : {},
|
||||
detectionMetrics:
|
||||
(await fetchDetectionsMetrics(kibanaIndex, signalsIndex, esClient, soClient, ml)) || {},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
});
|
||||
|
||||
it('returns zeroed counts if calls are empty', async () => {
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -99,7 +99,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
.mockReturnValue({ body: getMockRuleAlertsResponse(3400) });
|
||||
(savedObjectsClient.find as jest.Mock).mockReturnValue(getMockAlertCasesResponse());
|
||||
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -174,7 +174,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
.mockReturnValue({ body: getMockRuleAlertsResponse(800) });
|
||||
(savedObjectsClient.find as jest.Mock).mockReturnValue(getMockAlertCasesResponse());
|
||||
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -236,7 +236,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
.mockReturnValue({ body: getMockRuleAlertsResponse(0) });
|
||||
(savedObjectsClient.find as jest.Mock).mockReturnValue(getMockAlertCasesResponse());
|
||||
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -317,7 +317,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
jobs: null,
|
||||
jobStats: null,
|
||||
} as unknown) as ReturnType<typeof mlMock.anomalyDetectorsProvider>);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -347,7 +347,7 @@ describe('Detections Usage and Metrics', () => {
|
|||
datafeedStats: mockDatafeedStatsResponse,
|
||||
} as unknown) as ReturnType<typeof mlMock.anomalyDetectorsProvider>);
|
||||
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, mlMock, savedObjectsClient);
|
||||
const result = await fetchDetectionsMetrics('', '', esClientMock, savedObjectsClient, mlMock);
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
|
|
|
@ -20,12 +20,12 @@ export const fetchDetectionsMetrics = async (
|
|||
kibanaIndex: string,
|
||||
signalsIndex: string,
|
||||
esClient: ElasticsearchClient,
|
||||
ml: MlPluginSetup | undefined,
|
||||
savedObjectClient: SavedObjectsClientContract
|
||||
soClient: SavedObjectsClientContract,
|
||||
mlClient: MlPluginSetup | undefined
|
||||
): Promise<DetectionMetrics> => {
|
||||
const [mlJobMetrics, detectionRuleMetrics] = await Promise.allSettled([
|
||||
getMlJobMetrics(ml, savedObjectClient),
|
||||
getDetectionRuleMetrics(kibanaIndex, signalsIndex, esClient, savedObjectClient),
|
||||
getMlJobMetrics(mlClient, soClient),
|
||||
getDetectionRuleMetrics(kibanaIndex, signalsIndex, esClient, soClient),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,259 +0,0 @@
|
|||
/*
|
||||
* 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 { SavedObjectsFindResponse } from 'src/core/server';
|
||||
|
||||
import { Agent, FLEET_ENDPOINT_PACKAGE } from '../../../../fleet/common';
|
||||
import { AGENT_EVENT_SAVED_OBJECT_TYPE } from './fleet_saved_objects';
|
||||
|
||||
const testAgentId = 'testAgentId';
|
||||
const testAgentPolicyId = 'testAgentPolicyId';
|
||||
const testHostId = 'randoHostId';
|
||||
const testHostName = 'testDesktop';
|
||||
|
||||
/** Mock OS Platform for endpoint telemetry */
|
||||
export const MockOSPlatform = 'somePlatform';
|
||||
/** Mock OS Name for endpoint telemetry */
|
||||
export const MockOSName = 'somePlatformName';
|
||||
/** Mock OS Version for endpoint telemetry */
|
||||
export const MockOSVersion = '1';
|
||||
/** Mock OS Full Name for endpoint telemetry */
|
||||
export const MockOSFullName = 'somePlatformFullName';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lastCheckIn - the last time the agent checked in. Defaults to current ISO time.
|
||||
* @description We request the install and OS related telemetry information from the 'fleet-agents' saved objects in ingest_manager. This mocks that response
|
||||
*/
|
||||
export const mockFleetObjectsResponse = (
|
||||
hasDuplicates = true,
|
||||
lastCheckIn = new Date().toISOString()
|
||||
): { agents: Agent[]; total: number; page: number; perPage: number } | undefined => ({
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
total: 1,
|
||||
agents: [
|
||||
{
|
||||
active: true,
|
||||
id: testAgentId,
|
||||
policy_id: 'randoAgentPolicyId',
|
||||
type: 'PERMANENT',
|
||||
user_provided_metadata: {},
|
||||
enrolled_at: lastCheckIn,
|
||||
local_metadata: {
|
||||
elastic: {
|
||||
agent: {
|
||||
id: testAgentId,
|
||||
},
|
||||
},
|
||||
host: {
|
||||
hostname: testHostName,
|
||||
name: testHostName,
|
||||
id: testHostId,
|
||||
},
|
||||
os: {
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
name: MockOSName,
|
||||
full: MockOSFullName,
|
||||
},
|
||||
},
|
||||
packages: [FLEET_ENDPOINT_PACKAGE, 'system'],
|
||||
last_checkin: lastCheckIn,
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
id: 'oldTestAgentId',
|
||||
policy_id: 'randoAgentPolicyId',
|
||||
type: 'PERMANENT',
|
||||
user_provided_metadata: {},
|
||||
enrolled_at: lastCheckIn,
|
||||
local_metadata: {
|
||||
elastic: {
|
||||
agent: {
|
||||
id: 'oldTestAgentId',
|
||||
},
|
||||
},
|
||||
host: {
|
||||
hostname: hasDuplicates ? testHostName : 'oldRandoHostName',
|
||||
name: hasDuplicates ? testHostName : 'oldRandoHostName',
|
||||
id: hasDuplicates ? testHostId : 'oldRandoHostId',
|
||||
},
|
||||
os: {
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
name: MockOSName,
|
||||
full: MockOSFullName,
|
||||
},
|
||||
},
|
||||
packages: [FLEET_ENDPOINT_PACKAGE, 'system'],
|
||||
last_checkin: lastCheckIn,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const mockPolicyPayload = (
|
||||
policyStatus: 'success' | 'warning' | 'failure',
|
||||
policyMode: 'prevent' | 'detect' | 'off' = 'prevent'
|
||||
) =>
|
||||
JSON.stringify({
|
||||
'endpoint-security': {
|
||||
Endpoint: {
|
||||
configuration: {
|
||||
inputs: [
|
||||
{
|
||||
id: '0d466df0-c60f-11ea-a5c5-151665e785c4',
|
||||
policy: {
|
||||
linux: {
|
||||
events: {
|
||||
file: true,
|
||||
network: true,
|
||||
process: true,
|
||||
},
|
||||
logging: {
|
||||
file: 'info',
|
||||
},
|
||||
},
|
||||
mac: {
|
||||
events: {
|
||||
file: true,
|
||||
network: true,
|
||||
process: true,
|
||||
},
|
||||
logging: {
|
||||
file: 'info',
|
||||
},
|
||||
malware: {
|
||||
mode: policyMode,
|
||||
},
|
||||
},
|
||||
windows: {
|
||||
events: {
|
||||
dll_and_driver_load: true,
|
||||
dns: true,
|
||||
file: true,
|
||||
network: true,
|
||||
process: true,
|
||||
registry: true,
|
||||
security: true,
|
||||
},
|
||||
logging: {
|
||||
file: 'info',
|
||||
},
|
||||
malware: {
|
||||
mode: policyMode,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
policy: {
|
||||
applied: {
|
||||
id: '0d466df0-c60f-11ea-a5c5-151665e785c4',
|
||||
response: {
|
||||
configurations: {
|
||||
malware: {
|
||||
concerned_actions: [
|
||||
'load_config',
|
||||
'workflow',
|
||||
'download_global_artifacts',
|
||||
'download_user_artifacts',
|
||||
'configure_malware',
|
||||
'read_malware_config',
|
||||
'load_malware_model',
|
||||
'read_kernel_config',
|
||||
'configure_kernel',
|
||||
'detect_process_events',
|
||||
'detect_file_write_events',
|
||||
'connect_kernel',
|
||||
'detect_file_open_events',
|
||||
'detect_sync_image_load_events',
|
||||
],
|
||||
status: `${policyStatus}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
status: `${policyStatus}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
agent: {
|
||||
id: 'testAgentId',
|
||||
version: '8.0.0-SNAPSHOT',
|
||||
},
|
||||
host: {
|
||||
architecture: 'x86_64',
|
||||
id: 'a4148b63-1758-ab1f-a6d3-f95075cb1a9c',
|
||||
os: {
|
||||
Ext: {
|
||||
variant: 'Windows 10 Pro',
|
||||
},
|
||||
full: 'Windows 10 Pro 2004 (10.0.19041.329)',
|
||||
name: 'Windows',
|
||||
version: '2004 (10.0.19041.329)',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param running - allows us to set whether the mocked endpoint is in an active or disabled/failed state
|
||||
* @param updatedDate - the last time the endpoint was updated. Defaults to current ISO time.
|
||||
* @description We request the events triggered by the agent and get the most recent endpoint event to confirm it is still running. This allows us to mock both scenarios
|
||||
*/
|
||||
export const mockFleetEventsObjectsResponse = (
|
||||
running?: boolean,
|
||||
updatedDate = new Date().toISOString(),
|
||||
policyStatus: 'success' | 'failure' = running ? 'success' : 'failure',
|
||||
policyMode: 'prevent' | 'detect' | 'off' = 'prevent'
|
||||
): SavedObjectsFindResponse => {
|
||||
return {
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
total: 2,
|
||||
saved_objects: [
|
||||
{
|
||||
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
|
||||
id: 'id1',
|
||||
attributes: {
|
||||
agent_id: testAgentId,
|
||||
type: running ? 'STATE' : 'ERROR',
|
||||
timestamp: updatedDate,
|
||||
subtype: running ? 'RUNNING' : 'FAILED',
|
||||
message: `Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to ${
|
||||
running ? 'RUNNING' : 'FAILED'
|
||||
}: `,
|
||||
payload: running ? mockPolicyPayload(policyStatus, policyMode) : undefined,
|
||||
policy_id: testAgentPolicyId,
|
||||
},
|
||||
references: [],
|
||||
updated_at: updatedDate,
|
||||
version: 'WzExOCwxXQ==',
|
||||
score: 0,
|
||||
},
|
||||
{
|
||||
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
|
||||
id: 'id2',
|
||||
attributes: {
|
||||
agent_id: testAgentId,
|
||||
type: 'STATE',
|
||||
timestamp: updatedDate,
|
||||
subtype: 'STARTING',
|
||||
message:
|
||||
'Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to STARTING: Starting',
|
||||
policy_id: testAgentPolicyId,
|
||||
},
|
||||
references: [],
|
||||
updated_at: updatedDate,
|
||||
version: 'WzExNywxXQ==',
|
||||
score: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
|
@ -1,394 +0,0 @@
|
|||
/*
|
||||
* 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 { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks';
|
||||
import {
|
||||
mockFleetObjectsResponse,
|
||||
mockFleetEventsObjectsResponse,
|
||||
MockOSFullName,
|
||||
MockOSPlatform,
|
||||
MockOSVersion,
|
||||
} from './endpoint.mocks';
|
||||
import { SavedObjectsClientContract, SavedObjectsFindResponse } from 'src/core/server';
|
||||
import { Agent } from '../../../../fleet/common';
|
||||
import * as endpointTelemetry from './index';
|
||||
import * as fleetSavedObjects from './fleet_saved_objects';
|
||||
import { createMockEndpointAppContext } from '../../endpoint/mocks';
|
||||
import { EndpointAppContext } from '../../endpoint/types';
|
||||
|
||||
describe('test security solution endpoint telemetry', () => {
|
||||
let mockSavedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
|
||||
let mockEndpointAppContext: EndpointAppContext;
|
||||
let mockEsClient: ReturnType<typeof elasticsearchServiceMock.createElasticsearchClient>;
|
||||
let getEndpointIntegratedFleetMetadataSpy: jest.SpyInstance<
|
||||
Promise<{ agents: Agent[]; total: number; page: number; perPage: number } | undefined>
|
||||
>;
|
||||
let getLatestFleetEndpointEventSpy: jest.SpyInstance<Promise<SavedObjectsFindResponse>>;
|
||||
|
||||
beforeAll(() => {
|
||||
getLatestFleetEndpointEventSpy = jest.spyOn(fleetSavedObjects, 'getLatestFleetEndpointEvent');
|
||||
getEndpointIntegratedFleetMetadataSpy = jest.spyOn(
|
||||
fleetSavedObjects,
|
||||
'getEndpointIntegratedFleetMetadata'
|
||||
);
|
||||
mockSavedObjectsClient = savedObjectsClientMock.create();
|
||||
mockEndpointAppContext = createMockEndpointAppContext();
|
||||
mockEsClient = elasticsearchServiceMock.createElasticsearchClient();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should have a default shape', () => {
|
||||
expect(endpointTelemetry.getDefaultEndpointTelemetry()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"active_within_last_24_hours": 0,
|
||||
"os": Array [],
|
||||
"policies": Object {
|
||||
"malware": Object {
|
||||
"active": 0,
|
||||
"failure": 0,
|
||||
"inactive": 0,
|
||||
},
|
||||
},
|
||||
"total_installed": 0,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('when a request for endpoint agents fails', () => {
|
||||
it('should return an empty object', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.reject(Error('No agents for you'))
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(getEndpointIntegratedFleetMetadataSpy).toHaveBeenCalled();
|
||||
expect(endpointUsage).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when an agent has not been installed', () => {
|
||||
it('should return the default shape if no agents are found', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve({ agents: [], total: 0, perPage: 0, page: 0 })
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(getEndpointIntegratedFleetMetadataSpy).toHaveBeenCalled();
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 0,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when agent(s) have been installed', () => {
|
||||
describe('when a request for events has failed', () => {
|
||||
it('should show only one endpoint installed but it is inactive', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.reject(Error('No events for you'))
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a request for events is successful', () => {
|
||||
it('should show one endpoint installed but endpoint has failed to run', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetEventsObjectsResponse())
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should show two endpoints installed but both endpoints have failed to run', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse(false))
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetEventsObjectsResponse())
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 2,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 2,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should show two endpoints installed but agents have not checked in within past day', async () => {
|
||||
const twoDaysAgo = new Date();
|
||||
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
|
||||
const twoDaysAgoISOString = twoDaysAgo.toISOString();
|
||||
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse(false, twoDaysAgoISOString))
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(
|
||||
() => Promise.resolve(mockFleetEventsObjectsResponse(true, twoDaysAgoISOString)) // agent_id doesn't matter for mock here
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 2,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 2,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 2,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should show one endpoint installed and endpoint is running', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetEventsObjectsResponse(true))
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 1,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 1,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('malware policy', () => {
|
||||
it('should have failed to enable', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(
|
||||
mockFleetEventsObjectsResponse(true, new Date().toISOString(), 'failure')
|
||||
)
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 1,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 1,
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should be enabled successfully', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetEventsObjectsResponse(true))
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 1,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 1,
|
||||
inactive: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should be disabled successfully', async () => {
|
||||
getEndpointIntegratedFleetMetadataSpy.mockImplementation(() =>
|
||||
Promise.resolve(mockFleetObjectsResponse())
|
||||
);
|
||||
getLatestFleetEndpointEventSpy.mockImplementation(() =>
|
||||
Promise.resolve(
|
||||
mockFleetEventsObjectsResponse(true, new Date().toISOString(), 'success', 'off')
|
||||
)
|
||||
);
|
||||
|
||||
const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet(
|
||||
mockSavedObjectsClient,
|
||||
mockEndpointAppContext,
|
||||
mockEsClient
|
||||
);
|
||||
expect(endpointUsage).toEqual({
|
||||
total_installed: 1,
|
||||
active_within_last_24_hours: 1,
|
||||
os: [
|
||||
{
|
||||
full_name: MockOSFullName,
|
||||
platform: MockOSPlatform,
|
||||
version: MockOSVersion,
|
||||
count: 1,
|
||||
},
|
||||
],
|
||||
policies: {
|
||||
malware: {
|
||||
failure: 0,
|
||||
active: 0,
|
||||
inactive: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* 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 {
|
||||
ElasticsearchClient,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindResponse,
|
||||
} from 'src/core/server';
|
||||
import { AgentService } from '../../../../fleet/server';
|
||||
import { FLEET_ENDPOINT_PACKAGE } from '../../../../fleet/common';
|
||||
|
||||
export const AGENT_EVENT_SAVED_OBJECT_TYPE = 'donotexistsanymore-since-7.13';
|
||||
|
||||
export const getEndpointIntegratedFleetMetadata = async (
|
||||
agentService: AgentService | undefined,
|
||||
esClient: ElasticsearchClient
|
||||
) => {
|
||||
return agentService?.listAgents(esClient, {
|
||||
kuery: `(packages : ${FLEET_ENDPOINT_PACKAGE})`,
|
||||
perPage: 10000,
|
||||
showInactive: false,
|
||||
sortField: 'enrolled_at',
|
||||
sortOrder: 'desc',
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
TODO: AS OF 7.13, this access will no longer work due to the enabling of fleet server. An alternative route will have
|
||||
to be discussed to retrieve the policy data we need, as well as when the endpoint was last active, which is obtained
|
||||
via the last endpoint 'check in' event that was sent to fleet. Also, the only policy currently tracked is `malware`,
|
||||
but the hope is to add more, so a better/more scalable solution would be desirable.
|
||||
*/
|
||||
|
||||
export const getLatestFleetEndpointEvent = async (
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
agentId: string
|
||||
): Promise<SavedObjectsFindResponse> =>
|
||||
// Agent events saved object do not exists in Fleet anymore
|
||||
({ total: 0, saved_objects: [], page: 0, per_page: 0 });
|
|
@ -1,284 +0,0 @@
|
|||
/*
|
||||
* 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 { cloneDeep } from 'lodash';
|
||||
import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server';
|
||||
import { SavedObject } from './../../../../../../src/core/types/saved_objects';
|
||||
import { Agent } from './../../../../fleet/common/types/models/agent';
|
||||
import { AgentMetadata } from '../../../../fleet/common/types/models/agent';
|
||||
import {
|
||||
getEndpointIntegratedFleetMetadata,
|
||||
getLatestFleetEndpointEvent,
|
||||
} from './fleet_saved_objects';
|
||||
import { EndpointAppContext } from '../../endpoint/types';
|
||||
|
||||
export interface AgentOSMetadataTelemetry {
|
||||
full_name: string;
|
||||
platform: string;
|
||||
version: string;
|
||||
count: number;
|
||||
}
|
||||
export interface PolicyTelemetry {
|
||||
active: number;
|
||||
inactive: number;
|
||||
failure: number;
|
||||
}
|
||||
|
||||
export interface PoliciesTelemetry {
|
||||
malware: PolicyTelemetry;
|
||||
}
|
||||
|
||||
export interface EndpointUsage {
|
||||
total_installed: number;
|
||||
active_within_last_24_hours: number;
|
||||
os: AgentOSMetadataTelemetry[];
|
||||
policies: PoliciesTelemetry;
|
||||
}
|
||||
|
||||
type EndpointOSNames = 'Linux' | 'Windows' | 'macOS';
|
||||
|
||||
export interface AgentLocalMetadata extends AgentMetadata {
|
||||
elastic: {
|
||||
agent: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
host: {
|
||||
hostname: string;
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
os: {
|
||||
name: string;
|
||||
platform: string;
|
||||
version: string;
|
||||
full: string;
|
||||
};
|
||||
}
|
||||
|
||||
type OSTracker = Record<string, AgentOSMetadataTelemetry>;
|
||||
|
||||
/**
|
||||
* @description returns an empty telemetry object to be incrmented and updated within the `getEndpointTelemetryFromFleet` fn
|
||||
*/
|
||||
export const getDefaultEndpointTelemetry = (): EndpointUsage => ({
|
||||
total_installed: 0,
|
||||
active_within_last_24_hours: 0,
|
||||
os: [],
|
||||
policies: {
|
||||
malware: {
|
||||
active: 0,
|
||||
inactive: 0,
|
||||
failure: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @description this function updates the os telemetry. We use the fullName field as the key as it contains the name and version details.
|
||||
* If it has already been tracked, the count will be updated, otherwise a tracker will be initialized for that fullName.
|
||||
*/
|
||||
export const updateEndpointOSTelemetry = (
|
||||
os: AgentLocalMetadata['os'],
|
||||
osTracker: OSTracker
|
||||
): OSTracker => {
|
||||
let updatedOSTracker = osTracker;
|
||||
if (os && typeof os === 'object') {
|
||||
updatedOSTracker = cloneDeep(osTracker);
|
||||
const { version: osVersion, platform: osPlatform, full: osFullName } = os;
|
||||
if (osFullName && osVersion) {
|
||||
if (updatedOSTracker[osFullName]) updatedOSTracker[osFullName].count += 1;
|
||||
else {
|
||||
updatedOSTracker[osFullName] = {
|
||||
full_name: osFullName,
|
||||
platform: osPlatform,
|
||||
version: osVersion,
|
||||
count: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedOSTracker;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description we take the latest endpoint specific agent event, get the status of the endpoint, and if it is running
|
||||
* and the agent itself has been active within the last 24 hours, we can safely assume the endpoint has been active within
|
||||
* the same time span.
|
||||
*/
|
||||
export const updateEndpointDailyActiveCount = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
latestEndpointEvent: SavedObject<any>, // TODO: This information will be lost in 7.13, need to find an alternative route.
|
||||
lastAgentCheckin: Agent['last_checkin'],
|
||||
currentCount: number
|
||||
) => {
|
||||
const aDayAgo = new Date();
|
||||
aDayAgo.setDate(aDayAgo.getDate() - 1);
|
||||
|
||||
const agentWasActiveOverLastDay = !!lastAgentCheckin && new Date(lastAgentCheckin) > aDayAgo;
|
||||
return agentWasActiveOverLastDay && latestEndpointEvent.attributes.subtype === 'RUNNING'
|
||||
? currentCount + 1
|
||||
: currentCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description We take the latest endpoint specific agent event, and as long as it provides the payload with policy details, we will parse that policy
|
||||
* to populate the success of it's application. The policy is provided in the agent health checks.
|
||||
*/
|
||||
export const updateEndpointPolicyTelemetry = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
latestEndpointEvent: SavedObject<any>,
|
||||
policiesTracker: PoliciesTelemetry
|
||||
): PoliciesTelemetry => {
|
||||
const policyHostTypeToPolicyType = {
|
||||
Linux: 'linux',
|
||||
macOS: 'mac',
|
||||
Windows: 'windows',
|
||||
};
|
||||
const enabledMalwarePolicyTypes = ['prevent', 'detect'];
|
||||
|
||||
// The policy details are sent as a string on the 'payload' attribute of the agent event
|
||||
const { payload } = latestEndpointEvent.attributes;
|
||||
|
||||
if (!payload) {
|
||||
// This payload may not always be provided depending on the state of the endpoint. Guard again situations where it is not sent
|
||||
return policiesTracker;
|
||||
}
|
||||
|
||||
let endpointPolicyPayload;
|
||||
try {
|
||||
endpointPolicyPayload = JSON.parse(latestEndpointEvent.attributes.payload);
|
||||
} catch (error) {
|
||||
return policiesTracker;
|
||||
}
|
||||
|
||||
// Get the platform: windows, mac, or linux
|
||||
const hostType =
|
||||
policyHostTypeToPolicyType[
|
||||
endpointPolicyPayload['endpoint-security']?.host?.os?.name as EndpointOSNames
|
||||
];
|
||||
// Get whether the malware setting for the platform on the most recently provided config is active (prevent or detect is on) or off
|
||||
const userDesiredMalwareState =
|
||||
endpointPolicyPayload['endpoint-security'].Endpoint?.configuration?.inputs[0]?.policy[hostType]
|
||||
?.malware?.mode;
|
||||
|
||||
// Get the status of the application of the malware protection
|
||||
const malwareStatus =
|
||||
endpointPolicyPayload['endpoint-security'].Endpoint?.policy?.applied?.response?.configurations
|
||||
?.malware?.status;
|
||||
|
||||
if (!userDesiredMalwareState || !malwareStatus) {
|
||||
// If we get policy information without the mode or status, then nothing to track or update
|
||||
return policiesTracker;
|
||||
}
|
||||
|
||||
const updatedPoliciesTracker = {
|
||||
malware: { ...policiesTracker.malware },
|
||||
};
|
||||
|
||||
const isAnActiveMalwareState = enabledMalwarePolicyTypes.includes(userDesiredMalwareState);
|
||||
|
||||
// we only check for 'not failure' as the 'warning' state for malware is still technically actively enabled (with warnings)
|
||||
const successfullyEnabled = !!malwareStatus && malwareStatus !== 'failure';
|
||||
const failedToEnable = !!malwareStatus && malwareStatus === 'failure';
|
||||
|
||||
if (isAnActiveMalwareState && successfullyEnabled) {
|
||||
updatedPoliciesTracker.malware.active += 1;
|
||||
} else if (!isAnActiveMalwareState && successfullyEnabled) {
|
||||
updatedPoliciesTracker.malware.inactive += 1;
|
||||
} else if (isAnActiveMalwareState && failedToEnable) {
|
||||
updatedPoliciesTracker.malware.failure += 1;
|
||||
}
|
||||
|
||||
return updatedPoliciesTracker;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This aggregates the telemetry details from the fleet agent service `listAgents` and the fleet saved object `fleet-agent-events` to populate
|
||||
* the telemetry details for endpoint. Since we cannot access our own indices due to `kibana_system` not having access, this is the best alternative.
|
||||
* Once the data is requested, we iterate over all agents with endpoints registered, and then request the events for each active agent (within last 24 hours)
|
||||
* to confirm whether or not the endpoint is still active
|
||||
*/
|
||||
export const getEndpointTelemetryFromFleet = async (
|
||||
soClient: SavedObjectsClientContract,
|
||||
endpointAppContext: EndpointAppContext,
|
||||
esClient: ElasticsearchClient
|
||||
): Promise<EndpointUsage | {}> => {
|
||||
// Retrieve every agent (max 10000) that references the endpoint as an installed package. It will not be listed if it was never installed
|
||||
let endpointAgents;
|
||||
const agentService = endpointAppContext.service.getAgentService();
|
||||
try {
|
||||
const response = await getEndpointIntegratedFleetMetadata(agentService, esClient);
|
||||
endpointAgents = response?.agents ?? [];
|
||||
} catch (error) {
|
||||
// Better to provide an empty object rather than default telemetry as this better informs us of an error
|
||||
return {};
|
||||
}
|
||||
|
||||
const endpointAgentsCount = endpointAgents.length;
|
||||
const endpointTelemetry = getDefaultEndpointTelemetry();
|
||||
|
||||
// If there are no installed endpoints return the default telemetry object
|
||||
if (!endpointAgents || endpointAgentsCount < 1) return endpointTelemetry;
|
||||
|
||||
// Use unique hosts to prevent any potential duplicates
|
||||
const uniqueHosts: Set<string> = new Set();
|
||||
let osTracker: OSTracker = {};
|
||||
let dailyActiveCount = 0;
|
||||
let policyTracker: PoliciesTelemetry = { malware: { active: 0, inactive: 0, failure: 0 } };
|
||||
|
||||
for (let i = 0; i < endpointAgentsCount; i += 1) {
|
||||
try {
|
||||
const { last_checkin: lastCheckin, local_metadata: localMetadata } = endpointAgents[i];
|
||||
const { host, os, elastic } = localMetadata as AgentLocalMetadata;
|
||||
|
||||
// Although not perfect, the goal is to dedupe hosts to get the most recent data for a host
|
||||
// An agent re-installed on the same host will have the same id and hostname
|
||||
// A cloned VM will have the same id, but "may" have the same hostname, but it's really up to the user.
|
||||
const compoundUniqueId = `${host?.id}-${host?.hostname}`;
|
||||
if (!uniqueHosts.has(compoundUniqueId)) {
|
||||
uniqueHosts.add(compoundUniqueId);
|
||||
const agentId = elastic?.agent?.id;
|
||||
osTracker = updateEndpointOSTelemetry(os, osTracker);
|
||||
|
||||
if (agentId) {
|
||||
const { saved_objects: agentEvents } = await getLatestFleetEndpointEvent(
|
||||
soClient,
|
||||
agentId
|
||||
);
|
||||
|
||||
// AgentEvents will have a max length of 1
|
||||
if (agentEvents && agentEvents.length > 0) {
|
||||
const latestEndpointEvent = agentEvents[0];
|
||||
dailyActiveCount = updateEndpointDailyActiveCount(
|
||||
latestEndpointEvent,
|
||||
lastCheckin,
|
||||
dailyActiveCount
|
||||
);
|
||||
policyTracker = updateEndpointPolicyTelemetry(latestEndpointEvent, policyTracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// All errors thrown in the loop would be handled here
|
||||
// Not logging any errors to avoid leaking any potential PII
|
||||
// Depending on when the error is thrown in the loop some specifics may be missing, but it allows the loop to continue
|
||||
}
|
||||
}
|
||||
|
||||
// All unique hosts with an endpoint installed, thus all unique endpoint installs
|
||||
endpointTelemetry.total_installed = uniqueHosts.size;
|
||||
// Set the daily active count for the endpoints
|
||||
endpointTelemetry.active_within_last_24_hours = dailyActiveCount;
|
||||
// Get the objects to populate our OS Telemetry
|
||||
endpointTelemetry.os = Object.values(osTracker);
|
||||
// Provide the updated policy information
|
||||
endpointTelemetry.policies = policyTracker;
|
||||
|
||||
return endpointTelemetry;
|
||||
};
|
|
@ -6,12 +6,10 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup } from 'src/core/server';
|
||||
import { EndpointAppContext } from '../endpoint/types';
|
||||
import { SetupPlugins } from '../plugin';
|
||||
|
||||
export type CollectorDependencies = {
|
||||
kibanaIndex: string;
|
||||
signalsIndex: string;
|
||||
core: CoreSetup;
|
||||
endpointAppContext: EndpointAppContext;
|
||||
} & Pick<SetupPlugins, 'ml' | 'usageCollection'>;
|
||||
|
|
|
@ -2192,7 +2192,9 @@
|
|||
},
|
||||
"open_formula_popover": {
|
||||
"type": "long",
|
||||
"_meta": { "description": "Number of times the user opened the in-product formula help popover." }
|
||||
"_meta": {
|
||||
"description": "Number of times the user opened the in-product formula help popover."
|
||||
}
|
||||
},
|
||||
"toggle_fullscreen_formula": {
|
||||
"type": "long",
|
||||
|
@ -2431,7 +2433,9 @@
|
|||
},
|
||||
"open_formula_popover": {
|
||||
"type": "long",
|
||||
"_meta": { "description": "Number of times the user opened the in-product formula help popover." }
|
||||
"_meta": {
|
||||
"description": "Number of times the user opened the in-product formula help popover."
|
||||
}
|
||||
},
|
||||
"toggle_fullscreen_formula": {
|
||||
"type": "long",
|
||||
|
@ -5429,79 +5433,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"properties": {
|
||||
"total_installed": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of installed endpoints"
|
||||
}
|
||||
},
|
||||
"active_within_last_24_hours": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The number of active endpoints"
|
||||
}
|
||||
},
|
||||
"os": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"full_name": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "Full name of the operating system"
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "OS Platform. eg Centos, Ubuntu"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
"description": "The version of the operating system, eg 16.04.7 LTS (Xenial Xerus), 8 (Core)"
|
||||
}
|
||||
},
|
||||
"count": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The total number of endpoints from that platform"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"policies": {
|
||||
"properties": {
|
||||
"malware": {
|
||||
"properties": {
|
||||
"active": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The total number of active malware policies"
|
||||
}
|
||||
},
|
||||
"inactive": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The total number of inactive malware policies"
|
||||
}
|
||||
},
|
||||
"failure": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "The total number of failing malware policies"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue