From f5ba018fa20c2ebd66c5fee394f6bdd8887a28bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Zaffarano?= Date: Mon, 22 Jul 2024 12:56:27 +0200 Subject: [PATCH] [Telemetry][Security Solution] Enrich endpoint alerts with license info (#188760) (cherry picked from commit aa6aa2686641b428730f64c65a30003031d39c98) --- .../server/integration_tests/lib/helpers.ts | 4 ++-- .../integration_tests/telemetry.test.ts | 24 ++++++++++++++++--- .../server/lib/telemetry/async_sender.ts | 10 +++++++- .../server/lib/telemetry/receiver.ts | 8 +++++++ .../server/lib/telemetry/tasks/diagnostic.ts | 5 ++-- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/server/integration_tests/lib/helpers.ts b/x-pack/plugins/security_solution/server/integration_tests/lib/helpers.ts index 4cf1b69e0873a..ccc73435636e9 100644 --- a/x-pack/plugins/security_solution/server/integration_tests/lib/helpers.ts +++ b/x-pack/plugins/security_solution/server/integration_tests/lib/helpers.ts @@ -22,8 +22,8 @@ const asyncUnlink = Util.promisify(Fs.unlink); */ export async function eventually( cb: () => Promise, - duration: number = 60000, - interval: number = 1000 + duration: number = 120000, + interval: number = 3000 ) { let elapsed = 0; diff --git a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts index fd91986cb30e5..7a38948c0c46d 100644 --- a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts +++ b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts @@ -262,7 +262,7 @@ describe('telemetry tasks', () => { // wait until the events are sent to the telemetry server const body = await eventually(async () => { const found = mockedAxiosPost.mock.calls.find(([url]) => { - return url.startsWith(ENDPOINT_STAGING) && url.endsWith('alerts-endpoint'); + return url.startsWith(ENDPOINT_STAGING) && url.endsWith(TelemetryChannel.ENDPOINT_ALERTS); }); expect(found).not.toBeFalsy(); @@ -273,6 +273,25 @@ describe('telemetry tasks', () => { expect(body).not.toBeFalsy(); expect(body.Endpoint).not.toBeFalsy(); }); + + it('should enrich with license info', async () => { + await mockAndScheduleEndpointDiagnosticsTask(); + + // wait until the events are sent to the telemetry server + const body = await eventually(async () => { + const found = mockedAxiosPost.mock.calls.find(([url]) => { + return url.startsWith(ENDPOINT_STAGING) && url.endsWith(TelemetryChannel.ENDPOINT_ALERTS); + }); + + expect(found).not.toBeFalsy(); + + return JSON.parse((found ? found[1] : '{}') as string); + }); + + expect(body).not.toBeFalsy(); + expect(body.license).not.toBeFalsy(); + expect(body.license.status).not.toBeFalsy(); + }); }); describe('endpoint-meta-telemetry', () => { @@ -679,8 +698,7 @@ describe('telemetry tasks', () => { expect(body.file).toStrictEqual(alertsDetectionsRequest.file); }); - // Flaky: https://github.com/elastic/kibana/issues/188234 - it.skip('should manage runtime errors searching endpoint metrics', async () => { + it('should manage runtime errors searching endpoint metrics', async () => { const errorMessage = 'Something went wront'; async function* mockedGenerator( diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/async_sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/async_sender.ts index 07e098b1cc979..6c2def2abb61d 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/async_sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/async_sender.ts @@ -21,7 +21,7 @@ import { TelemetryChannel, TelemetryCounter } from './types'; import * as collections from './collections_helpers'; import { CachedSubject, retryOnError$ } from './rxjs_helpers'; import { SenderUtils } from './sender_helpers'; -import { newTelemetryLogger } from './helpers'; +import { copyLicenseFields, newTelemetryLogger } from './helpers'; import { type TelemetryLogger } from './telemetry_logger'; export const DEFAULT_QUEUE_CONFIG: QueueConfig = { @@ -291,6 +291,14 @@ export class AsyncTelemetryEventsSender implements IAsyncTelemetryEventsSender { }; } + if (event.channel === TelemetryChannel.ENDPOINT_ALERTS) { + const licenseInfo = this.telemetryReceiver?.getLicenseInfo(); + additional = { + ...additional, + ...(licenseInfo ? { license: copyLicenseFields(licenseInfo) } : {}), + }; + } + event.payload = { ...event.payload, ...additional, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index ebff5655d99e0..22f85d19c83d8 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -102,6 +102,8 @@ export interface ITelemetryReceiver { fetchClusterInfo(): Promise; + getLicenseInfo(): Nullable; + fetchLicenseInfo(): Promise>; closePointInTime(pitId: string): Promise; @@ -248,6 +250,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { private getIndexForType?: (type: string) => string; private alertsIndex?: string; private clusterInfo?: ESClusterInfo; + private licenseInfo?: Nullable; private processTreeFetcher?: Fetcher; private packageService?: PackageService; private experimentalFeatures: ExperimentalFeatures | undefined; @@ -280,6 +283,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { this.soClient = core?.savedObjects.createInternalRepository() as unknown as SavedObjectsClientContract; this.clusterInfo = await this.fetchClusterInfo(); + this.licenseInfo = await this.fetchLicenseInfo(); this.experimentalFeatures = endpointContextService?.experimentalFeatures; const elasticsearch = core?.elasticsearch.client as unknown as IScopedClusterClient; this.processTreeFetcher = new Fetcher(elasticsearch); @@ -291,6 +295,10 @@ export class TelemetryReceiver implements ITelemetryReceiver { return this.clusterInfo; } + public getLicenseInfo(): Nullable { + return this.licenseInfo; + } + public getAlertsIndex(): string | undefined { return this.alertsIndex; } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/diagnostic.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/diagnostic.ts index 8148a81e8f915..a6825a7517b4f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/diagnostic.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/diagnostic.ts @@ -8,11 +8,10 @@ import type { Logger } from '@kbn/core/server'; import { newTelemetryLogger, getPreviousDiagTaskTimestamp } from '../helpers'; import type { ITelemetryEventsSender } from '../sender'; -import type { TelemetryEvent } from '../types'; +import { TelemetryChannel, type TelemetryEvent } from '../types'; import type { ITelemetryReceiver } from '../receiver'; import type { TaskExecutionPeriod } from '../task'; import type { ITaskMetricsService } from '../task_metrics.types'; -import { TELEMETRY_CHANNEL_ENDPOINT_ALERTS } from '../constants'; import { copyAllowlistedFields, filterList } from '../filterlists'; export function createTelemetryDiagnosticsTaskConfig() { @@ -65,7 +64,7 @@ export function createTelemetryDiagnosticsTaskConfig() { log.l('Sending diagnostic alerts', { alerts_count: alerts.length, }); - await sender.sendOnDemand(TELEMETRY_CHANNEL_ENDPOINT_ALERTS, processedAlerts); + sender.sendAsync(TelemetryChannel.ENDPOINT_ALERTS, processedAlerts); } await taskMetricsService.end(trace);