diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_milestone_service.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_milestone_service.ts index ef75ec78dfc3c..4afccc343ff3f 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_milestone_service.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_milestone_service.ts @@ -81,7 +81,7 @@ export const createTrialCompanionMilestoneServiceDeps: TrialCompanionMilestoneSe : undefined; // order doesn't matter - detectors.push(installedPackagesM1(detectorsLogger, packageService)); + detectors.push(installedPackagesM1(detectorsLogger, packageService, esClient)); if (usageCollectorDeps) { detectors.push( savedDiscoverySessionsM2(usageCollectorDeps), diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.test.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.test.ts index ab0ab414bbb19..ad3fd4d2815cd 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.test.ts @@ -59,6 +59,15 @@ describe('Trial companion NBA detectors', () => { jest.clearAllMocks(); }); + const buildCountResponse = (count: number): CountResponse => ({ + count, + _shards: { + failed: 0, + successful: 0, + total: 0, + }, + }); + it.each([ [10, undefined], [1, undefined], @@ -91,6 +100,7 @@ describe('Trial companion NBA detectors', () => { [ 'installed one default package', [createPackageListItem('endpoint', 'installed')], + buildCountResponse(1), Milestone.M1, ], [ @@ -99,11 +109,13 @@ describe('Trial companion NBA detectors', () => { createPackageListItem('trial-companion', 'installed'), createPackageListItem('trial-test', 'installed'), ], + buildCountResponse(1), undefined, ], [ 'not installed', [createPackageListItem('endpoint', ''), createPackageListItem('trial-companion', '')], + buildCountResponse(1), Milestone.M1, ], [ @@ -115,6 +127,7 @@ describe('Trial companion NBA detectors', () => { createPackageListItem('elastic_agent', 'installed'), createPackageListItem('fleet_server', 'installed'), ], + buildCountResponse(1), Milestone.M1, ], [ @@ -123,15 +136,33 @@ describe('Trial companion NBA detectors', () => { createPackageListItem('trial-test', 'installed'), createPackageListItem('fleet_server', 'installed'), ], + buildCountResponse(1), undefined, ], - ])('returns milestone based on packages in %s', async (_tcName, packageList, expected) => { - packageClient.getPackages.mockResolvedValueOnce(packageList); - await expect(installedPackagesM1(deps.logger, packageService)()).resolves.toEqual(expected); - }); + [ + 'all installed no agent', + [ + createPackageListItem('trial-test', 'installed'), + createPackageListItem('fleet_server', 'installed'), + ], + buildCountResponse(0), + Milestone.M1, + ], + ])( + 'returns milestone based on packages in %s', + async (_tcName, packageList, esResponse, expected) => { + packageClient.getPackages.mockResolvedValueOnce(packageList); + esClient.count.mockResolvedValueOnce(esResponse); + await expect(installedPackagesM1(deps.logger, packageService, esClient)()).resolves.toEqual( + expected + ); + } + ); it('propagates error from package service', async () => { packageClient.getPackages.mockRejectedValueOnce(new Error('test error')); - await expect(installedPackagesM1(deps.logger, packageService)()).rejects.toThrowError(); + await expect( + installedPackagesM1(deps.logger, packageService, esClient)() + ).rejects.toThrowError(); }); }); @@ -184,25 +215,19 @@ describe('Trial companion NBA detectors', () => { }); describe('aiFeaturesM5', () => { - const buildCountResponse = (count: number): CountResponse => ({ - count, - _shards: { - failed: 0, - successful: 0, - total: 0, - }, - }); - it.each([ - ['0 alerts, 0 conversations', 0, 0, Milestone.M5], - ['0 alerts, 3 conversations', 0, 3, undefined], - ['2 alerts, 0 conversations', 2, 0, undefined], - ['5 alerts, 12 conversations', 5, 12, undefined], + ['0 alerts, 0 conversations, 0 charts', 0, 0, 0, Milestone.M5], + ['0 alerts, 3 conversations, 0 charts', 0, 3, 0, undefined], + ['2 alerts, 0 conversations, 0 charts', 2, 0, 0, undefined], + ['5 alerts, 12 conversations, 0 charts', 5, 12, 0, undefined], + ['5 alerts, 12 conversations, 1 charts', 5, 12, 1, undefined], + ['0 alerts, 0 conversations, 1 charts', 0, 0, 7, undefined], ])( 'compares count of attack discovery alerts and assistant conversations - %s', - async (_tcName, alerts, assistant, expected) => { + async (_tcName, alerts, assistant, chart, expected) => { (esClient.count as jest.Mock).mockResolvedValueOnce(buildCountResponse(alerts)); (esClient.count as jest.Mock).mockResolvedValueOnce(buildCountResponse(assistant)); + (esClient.count as jest.Mock).mockResolvedValueOnce(buildCountResponse(chart)); await expect(aiFeaturesM5(esClient)()).resolves.toEqual(expected); } ); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.ts index c55f048ac1c96..577af2f584600 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/trial_companion/services/trial_companion_nba_detectors.ts @@ -19,7 +19,11 @@ export interface UsageCollectorDeps { usageCollection: ICollectorSet; } -export const installedPackagesM1 = (logger: Logger, packageService: PackageService): DetectorF => { +export const installedPackagesM1 = ( + logger: Logger, + packageService: PackageService, + esClient: ElasticsearchClient +): DetectorF => { return async (): Promise => { try { logger.debug('verifyNonDefaultPackagesInstalled: Fetching Fleet packages'); @@ -47,12 +51,17 @@ export const installedPackagesM1 = (logger: Logger, packageService: PackageServi }, installed package names: ${nonDefaultPackages.join(', ')}` ); - if (nonDefaultPackages.length === 0) { + const agentLogs = await esClient.count({ + index: 'logs-elastic_agent*', + }); + logger.debug(`agentLogs: ${JSON.stringify(agentLogs)}`); + + if (nonDefaultPackages.length === 0 || !agentLogs.count || agentLogs.count === 0) { return Milestone.M1; } return undefined; } catch (error) { - logger.error('verifyNonDefaultPackagesInstalled: Error fetching Fleet packages', error); + logger.error('installedPackagesM1: Error fetching installed packages or agent logs', error); throw error; } }; @@ -85,7 +94,7 @@ export const detectionRulesInstalledM3 = (deps: UsageCollectorDeps): DetectorF = const rulesCount = customEnabled + elasticEnabled; deps.logger.debug( - `verifyEnabledSecurityRulesCount: Rules count - custom: ${customEnabled}, elastic: ${elasticEnabled}, total: ${rulesCount}` + `detectionRulesInstalledM3: Rules count - custom: ${customEnabled}, elastic: ${elasticEnabled}, total: ${rulesCount}` ); return rulesCount > 0 ? undefined : Milestone.M3; }; @@ -122,12 +131,13 @@ export const savedDiscoverySessionsM2 = (deps: UsageCollectorDeps): DetectorF => export const aiFeaturesM5 = (esClient: ElasticsearchClient): DetectorF => { return async (): Promise => { + const deltaT = 'now-30d'; const attackDiscoveryResponse = await esClient.count({ index: '.alerts-security.attack.discovery.alerts-*', query: { range: { '@timestamp': { - gte: 'now-14d', + gte: deltaT, }, }, }, @@ -140,7 +150,7 @@ export const aiFeaturesM5 = (esClient: ElasticsearchClient): DetectorF => { query: { range: { '@timestamp': { - gte: 'now-14d', + gte: deltaT, }, }, }, @@ -148,6 +158,19 @@ export const aiFeaturesM5 = (esClient: ElasticsearchClient): DetectorF => { if (aiAssistantResponse.count > 0) { return undefined; } + const aiChatsResponse = await esClient.count({ + index: '.chat-conversations*', + query: { + range: { + updated_at: { + gte: deltaT, + }, + }, + }, + }); + if (aiChatsResponse.count > 0) { + return undefined; + } return Milestone.M5; }; }; @@ -170,7 +193,7 @@ async function fetchCollectorResults( return result as T; } catch (error) { - logger.error(`cases: Error fetching security solution telemetry: ${error}`); + logger.error(`fetchCollectorResults: Error fetching security solution telemetry: ${error}`); throw error; } }