Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -91,6 +100,7 @@ describe('Trial companion NBA detectors', () => {
[
'installed one default package',
[createPackageListItem('endpoint', 'installed')],
buildCountResponse(1),
Milestone.M1,
],
[
Expand All @@ -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,
],
[
Expand All @@ -115,6 +127,7 @@ describe('Trial companion NBA detectors', () => {
createPackageListItem('elastic_agent', 'installed'),
createPackageListItem('fleet_server', 'installed'),
],
buildCountResponse(1),
Milestone.M1,
],
[
Expand All @@ -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();
});
});

Expand Down Expand Up @@ -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);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Milestone | undefined> => {
try {
logger.debug('verifyNonDefaultPackagesInstalled: Fetching Fleet packages');
Expand Down Expand Up @@ -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;
}
};
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -122,12 +131,13 @@ export const savedDiscoverySessionsM2 = (deps: UsageCollectorDeps): DetectorF =>

export const aiFeaturesM5 = (esClient: ElasticsearchClient): DetectorF => {
return async (): Promise<Milestone | undefined> => {
const deltaT = 'now-30d';
const attackDiscoveryResponse = await esClient.count({
index: '.alerts-security.attack.discovery.alerts-*',
query: {
range: {
'@timestamp': {
gte: 'now-14d',
gte: deltaT,
},
},
},
Expand All @@ -140,14 +150,27 @@ export const aiFeaturesM5 = (esClient: ElasticsearchClient): DetectorF => {
query: {
range: {
'@timestamp': {
gte: 'now-14d',
gte: deltaT,
},
},
},
});
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;
};
};
Expand All @@ -170,7 +193,7 @@ async function fetchCollectorResults<T>(

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;
}
}
Loading