diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts index e379d81ffb0b7..1541cb128f60c 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts @@ -25,7 +25,7 @@ describe('test security solution endpoint telemetry', () => { let mockSavedObjectsClient: jest.Mocked; let mockEndpointAppContext: EndpointAppContext; let mockEsClient: ReturnType; - let getFleetSavedObjectsMetadataSpy: jest.SpyInstance< + let getEndpointIntegratedFleetMetadataSpy: jest.SpyInstance< Promise<{ agents: Agent[]; total: number; page: number; perPage: number } | undefined> >; let getLatestFleetEndpointEventSpy: jest.SpyInstance< @@ -34,7 +34,10 @@ describe('test security solution endpoint telemetry', () => { beforeAll(() => { getLatestFleetEndpointEventSpy = jest.spyOn(fleetSavedObjects, 'getLatestFleetEndpointEvent'); - getFleetSavedObjectsMetadataSpy = jest.spyOn(fleetSavedObjects, 'getFleetSavedObjectsMetadata'); + getEndpointIntegratedFleetMetadataSpy = jest.spyOn( + fleetSavedObjects, + 'getEndpointIntegratedFleetMetadata' + ); mockSavedObjectsClient = savedObjectsClientMock.create(); mockEndpointAppContext = createMockEndpointAppContext(); mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); @@ -63,7 +66,7 @@ describe('test security solution endpoint telemetry', () => { describe('when a request for endpoint agents fails', () => { it('should return an empty object', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.reject(Error('No agents for you')) ); @@ -72,14 +75,14 @@ describe('test security solution endpoint telemetry', () => { mockEndpointAppContext, mockEsClient ); - expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); + 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 () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve({ agents: [], total: 0, perPage: 0, page: 0 }) ); @@ -88,7 +91,7 @@ describe('test security solution endpoint telemetry', () => { mockEndpointAppContext, mockEsClient ); - expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); + expect(getEndpointIntegratedFleetMetadataSpy).toHaveBeenCalled(); expect(endpointUsage).toEqual({ total_installed: 0, active_within_last_24_hours: 0, @@ -107,7 +110,7 @@ describe('test security solution endpoint telemetry', () => { 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 () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -143,7 +146,7 @@ describe('test security solution endpoint telemetry', () => { describe('when a request for events is successful', () => { it('should show one endpoint installed but endpoint has failed to run', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -177,7 +180,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should show two endpoints installed but both endpoints have failed to run', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse(false)) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -215,7 +218,7 @@ describe('test security solution endpoint telemetry', () => { twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); const twoDaysAgoISOString = twoDaysAgo.toISOString(); - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse(false, twoDaysAgoISOString)) ); getLatestFleetEndpointEventSpy.mockImplementation( @@ -249,7 +252,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should show one endpoint installed and endpoint is running', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -284,7 +287,7 @@ describe('test security solution endpoint telemetry', () => { describe('malware policy', () => { it('should have failed to enable', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -320,7 +323,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should be enabled successfully', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => @@ -354,7 +357,7 @@ describe('test security solution endpoint telemetry', () => { }); it('should be disabled successfully', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => + getEndpointIntegratedFleetMetadataSpy.mockImplementation(() => Promise.resolve(mockFleetObjectsResponse()) ); getLatestFleetEndpointEventSpy.mockImplementation(() => diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts index 20e0414fcc7a6..7e3620ec0ae04 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts @@ -8,33 +8,29 @@ import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { AgentService } from '../../../../fleet/server'; import { AgentEventSOAttributes } from './../../../../fleet/common/types/models/agent'; -import { - AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, -} from './../../../../fleet/common/constants/agent'; +import { AGENT_EVENT_SAVED_OBJECT_TYPE } from './../../../../fleet/common/constants/agent'; import { defaultPackages as FleetDefaultPackages } from '../../../../fleet/common'; export const FLEET_ENDPOINT_PACKAGE_CONSTANT = FleetDefaultPackages.Endpoint; -export const getFleetSavedObjectsMetadata = async ( - savedObjectsClient: SavedObjectsClientContract, +export const getEndpointIntegratedFleetMetadata = async ( agentService: AgentService | undefined, esClient: ElasticsearchClient ) => { - const agentData = await agentService?.listAgents(savedObjectsClient, esClient, { - showInactive: true, + return agentService?.listAgents(esClient, { + kuery: `(packages : ${FLEET_ENDPOINT_PACKAGE_CONSTANT})`, perPage: 10000, + showInactive: false, sortField: 'enrolled_at', sortOrder: 'desc', - kuery: `${AGENT_SAVED_OBJECT_TYPE}.attributes.packages: ${FLEET_ENDPOINT_PACKAGE_CONSTANT}`, }); - return agentData; }; /* 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. Currently it's only `malware`, but the hope is to add more, - so a more scalable solution will be desirable. + 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 ( diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts index 5538117e063b8..94ff168ffffc8 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts @@ -10,7 +10,10 @@ import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server import { SavedObject } from './../../../../../../src/core/types/saved_objects'; import { Agent, NewAgentEvent } from './../../../../fleet/common/types/models/agent'; import { AgentMetadata } from '../../../../fleet/common/types/models/agent'; -import { getFleetSavedObjectsMetadata, getLatestFleetEndpointEvent } from './fleet_saved_objects'; +import { + getEndpointIntegratedFleetMetadata, + getLatestFleetEndpointEvent, +} from './fleet_saved_objects'; import { EndpointAppContext } from '../../endpoint/types'; export interface AgentOSMetadataTelemetry { @@ -208,7 +211,7 @@ export const getEndpointTelemetryFromFleet = async ( let endpointAgents; const agentService = endpointAppContext.service.getAgentService(); try { - const response = await getFleetSavedObjectsMetadata(soClient, agentService, esClient); + 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