Skip to content

Commit

Permalink
Add package version to prebuilt rule alert telemetry (#148954)
Browse files Browse the repository at this point in the history
## Summary

This pull request adds the users installed detection rules package to
the alert telemetry.
This enables Threat Researchers / Detection Engineers to develop these
rules for more fine-grained filtering and tuning.

### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
pjhampton authored Jan 27, 2023
1 parent d1e0c27 commit 0133fdc
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const createMockTelemetryReceiver = (
fetchEndpointMetrics: jest.fn().mockReturnValue(stubEndpointMetricsResponse),
fetchEndpointPolicyResponses: jest.fn(),
fetchPrebuiltRuleAlerts: jest.fn().mockReturnValue(prebuiltRuleAlertsResponse),
fetchDetectionRulesPackageVersion: jest.fn(),
fetchTrustedApplications: jest.fn(),
fetchEndpointList: jest.fn(),
fetchDetectionRules: jest.fn().mockReturnValue({ body: null }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,6 @@ export const endpointAllowlistFields: AllowlistFields = {
host: {
os: true,
},
package_version: true,
...allowlistBaseEventFields,
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('Security Telemetry filters', () => {
'event.outcome': true,
'event.provider': true,
'event.type': true,
package_version: true,
};

it('filters top level', () => {
Expand Down Expand Up @@ -160,6 +161,7 @@ describe('Security Telemetry filters', () => {
'event.outcome': 'success',
'event.provider': 'iam.amazonaws.com',
'event.type': ['user', 'creation'],
package_version: '3.4.1',
};
expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({
'event.id': '36857486973080746231799376445175633955031786243637182487',
Expand All @@ -169,6 +171,7 @@ describe('Security Telemetry filters', () => {
'event.outcome': 'success',
'event.provider': 'iam.amazonaws.com',
'event.type': ['user', 'creation'],
package_version: '3.4.1',
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { AllowlistFields } from './types';
export const prebuiltRuleAllowlistFields: AllowlistFields = {
_id: true,
id: true,
package_version: true,
'@timestamp': true,
// Base alert fields
'kibana.alert.ancestors': true,
Expand Down
22 changes: 18 additions & 4 deletions x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ import {
THRESHOLD_RULE_TYPE_ID,
} from '@kbn/securitysolution-rules';
import type { TransportResult } from '@elastic/elasticsearch';
import type { Agent, AgentPolicy } from '@kbn/fleet-plugin/common';
import type { AgentClient, AgentPolicyServiceInterface } from '@kbn/fleet-plugin/server';
import type { Agent, AgentPolicy, Installation } from '@kbn/fleet-plugin/common';
import type {
AgentClient,
AgentPolicyServiceInterface,
PackageService,
} from '@kbn/fleet-plugin/server';
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
import type { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services';
import {
Expand Down Expand Up @@ -66,11 +70,14 @@ export interface ITelemetryReceiver {
kibanaIndex?: string,
alertsIndex?: string,
endpointContextService?: EndpointAppContextService,
exceptionListClient?: ExceptionListClient
exceptionListClient?: ExceptionListClient,
packageService?: PackageService
): Promise<void>;

getClusterInfo(): ESClusterInfo | undefined;

fetchDetectionRulesPackageVersion(): Promise<Installation | undefined>;

fetchFleetAgents(): Promise<
| {
agents: Agent[];
Expand Down Expand Up @@ -178,6 +185,7 @@ export class TelemetryReceiver implements ITelemetryReceiver {
private alertsIndex?: string;
private clusterInfo?: ESClusterInfo;
private processTreeFetcher?: Fetcher;
private packageService?: PackageService;
private readonly maxRecords = 10_000 as const;

constructor(logger: Logger) {
Expand All @@ -189,14 +197,16 @@ export class TelemetryReceiver implements ITelemetryReceiver {
kibanaIndex?: string,
alertsIndex?: string,
endpointContextService?: EndpointAppContextService,
exceptionListClient?: ExceptionListClient
exceptionListClient?: ExceptionListClient,
packageService?: PackageService
) {
this.kibanaIndex = kibanaIndex;
this.alertsIndex = alertsIndex;
this.agentClient = endpointContextService?.getInternalFleetServices().agent;
this.agentPolicyService = endpointContextService?.getInternalFleetServices().agentPolicy;
this.esClient = core?.elasticsearch.client.asInternalUser;
this.exceptionListClient = exceptionListClient;
this.packageService = packageService;
this.soClient =
core?.savedObjects.createInternalRepository() as unknown as SavedObjectsClientContract;
this.clusterInfo = await this.fetchClusterInfo();
Expand All @@ -209,6 +219,10 @@ export class TelemetryReceiver implements ITelemetryReceiver {
return this.clusterInfo;
}

public async fetchDetectionRulesPackageVersion(): Promise<Installation | undefined> {
return this.packageService?.asInternalUser.getInstallation('security_detection_engine');
}

public async fetchFleetAgents() {
if (this.esClient === undefined || this.esClient === null) {
throw Error('elasticsearch client is unavailable: cannot retrieve fleet agents');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('security telemetry - detection rule alerts task test', () => {
mockTelemetryEventsSender,
testTaskExecutionPeriod
);
expect(mockTelemetryReceiver.fetchDetectionRulesPackageVersion).toHaveBeenCalled();
expect(mockTelemetryReceiver.fetchPrebuiltRuleAlerts).toHaveBeenCalled();
expect(mockTelemetryEventsSender.getTelemetryUsageCluster).toHaveBeenCalled();
expect(mockTelemetryEventsSender.getTelemetryUsageCluster()?.incrementCounter).toBeCalledTimes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: n
const startTime = Date.now();
const taskName = 'Security Solution - Prebuilt Rule and Elastic ML Alerts Telemetry';
try {
const [clusterInfoPromise, licenseInfoPromise] = await Promise.allSettled([
const [clusterInfoPromise, licenseInfoPromise, packageVersion] = await Promise.allSettled([
receiver.fetchClusterInfo(),
receiver.fetchLicenseInfo(),
receiver.fetchDetectionRulesPackageVersion(),
]);

const clusterInfo =
Expand All @@ -44,6 +45,8 @@ export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: n
licenseInfoPromise.status === 'fulfilled'
? licenseInfoPromise.value
: ({} as ESLicense | undefined);
const packageInfo =
packageVersion.status === 'fulfilled' ? packageVersion.value : undefined;

const { events: telemetryEvents, count: totalPrebuiltAlertCount } =
await receiver.fetchPrebuiltRuleAlerts();
Expand Down Expand Up @@ -73,6 +76,7 @@ export function createTelemetryPrebuiltRuleAlertsTaskConfig(maxTelemetryBatch: n
licence_id: licenseInfo?.uid,
cluster_uuid: clusterInfo?.cluster_uuid,
cluster_name: clusterInfo?.cluster_name,
package_version: packageInfo?.version,
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface TelemetryEvent {
};
cluster_name?: string;
cluster_uuid?: string;
package_version?: string;
file?: {
[key: string]: SearchTypes;
Ext?: {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/security_solution/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@ export class Plugin implements ISecuritySolutionPlugin {
this.kibanaIndex!,
DEFAULT_ALERTS_INDEX,
this.endpointAppContextService,
exceptionListClient
exceptionListClient,
packageService
);

artifactService.start(this.telemetryReceiver);
Expand Down

0 comments on commit 0133fdc

Please sign in to comment.