Skip to content

Commit

Permalink
[SecuritySolution][Endpoint][ResponseActions] Response action telemet…
Browse files Browse the repository at this point in the history
…ry (endpoint/third party) (elastic#192685)

## Summary

Adds server-side telemetry collection for response action creation and
responses.
part of elastic/security-team/issues/7466

<details><summary>Events from telemetry staging</summary>
<img
src="https://github.com/user-attachments/assets/2e9f37f1-c5b5-46e9-be34-c3bdcff4015b"
/>
<img
src="https://github.com/user-attachments/assets/85a5a75d-f9f1-4d76-a782-272d9d7da0cb"
/>
</details> 

<details><summary>Dashboard on staging</summary>
<img
src="https://github.com/user-attachments/assets/9faa96a2-a553-4def-b5da-6b66b5728ca4">
</details> 

This PR adds  Server Side EBTs (event-based telemetry) for:
### Action creation event
```json5
"event_type": [
    "endpoint_response_action_sent"
  ],
  "properties": [
    {
      "responseActions": {
        "actionId": "696608a5-1908-457d-9072-5f555c740ffc",
        "agentType": "sentinel_one",
        "command": "unisolate",
        "isAutomated": false
      }
    }
  ],
```
### Action response event
```json5
{
"event_type": [
    "endpoint_response_action_status_change_event"
  ],
  "properties": [
    {
      "responseActions": {
        "actionId": "696608a5-1908-457d-9072-5f555c740ffc",
        "agentType": "sentinel_one",
        "actionStatus": "successful",
        "command": "unisolate",
      }
    }
  ],
}
```

### Action creation error event
```json5
"event_type": [
    "endpoint_response_action_sent_error"
  ],
  "properties": [
    {
      "responseActions": {
        "command": "execute",
        "error": "error message",
        "agentType": "endpoint"
      }
    }
  ],
```


**Note:** This PR does not add response completion telemetry for
`endpoint` agent type. There would be follow up PRs to add that and some
usage/snapshot telemetry.

### Checklist
- [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
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] If a plugin configuration key changed, check if it needs to be
allow-listed in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
  • Loading branch information
ashokaditya authored and seanrathier committed Oct 2, 2024
1 parent db6c5d3 commit 36852cf
Show file tree
Hide file tree
Showing 19 changed files with 668 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ export const allowedExperimentalValues = Object.freeze({
*/
crowdstrikeDataInAnalyzerEnabled: true,

/**
* Enables Response actions telemetry collection
* Should be enabled in 8.17.0
*/
responseActionsTelemetryEnabled: false,

/**
* Enables experimental JAMF integration data to be available in Analyzer
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ export const ExecuteActionResult = memo<
>(({ command, setStore, store, status, setStatus, ResultComponent }) => {
const actionCreator = useSendExecuteEndpoint();
const actionRequestBody = useMemo<undefined | ExecuteActionRequestBody>(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { endpointId, agentType } = command.commandDefinition?.meta ?? {};

if (!endpointId) {
return;
}
return {
agent_type: agentType,
endpoint_ids: [endpointId],
parameters: {
command: command.args.args.command[0],
Expand All @@ -46,7 +47,7 @@ export const ExecuteActionResult = memo<
comment: command.args.args?.comment?.[0],
};
}, [
command.commandDefinition?.meta?.endpointId,
command.commandDefinition?.meta,
command.args.args.command,
command.args.args.timeout,
command.args.args?.comment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ export const GetFileActionResult = memo<
const actionCreator = useSendGetFileRequest();

const actionRequestBody = useMemo<undefined | ResponseActionGetFileRequestBody>(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { agentType, endpointId } = command.commandDefinition?.meta ?? {};
const { path, comment } = command.args.args;
const agentType = command.commandDefinition?.meta?.agentType;

return endpointId
? {
Expand All @@ -37,11 +36,7 @@ export const GetFileActionResult = memo<
},
}
: undefined;
}, [
command.args.args,
command.commandDefinition?.meta?.agentType,
command.commandDefinition?.meta?.endpointId,
]);
}, [command.args.args, command.commandDefinition?.meta]);

const { result, actionDetails } = useConsoleActionSubmitter<ResponseActionGetFileRequestBody>({
ResultComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('When using execute action from response actions console', () => {

await waitFor(() => {
expect(apiMocks.responseProvider.execute).toHaveBeenCalledWith({
body: '{"endpoint_ids":["a.b.c"],"parameters":{"command":"ls -al"}}',
body: '{"agent_type":"endpoint","endpoint_ids":["a.b.c"],"parameters":{"command":"ls -al"}}',
path: EXECUTE_ROUTE,
version: '2023-10-31',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ export const IsolateActionResult = memo<ActionRequestComponentProps>(
const isolateHostApi = useSendIsolateEndpointRequest();

const actionRequestBody = useMemo(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { agentType, endpointId } = command.commandDefinition?.meta ?? {};
const comment = command.args.args?.comment?.[0];
const agentType = command.commandDefinition?.meta?.agentType;

return endpointId
? {
Expand All @@ -30,12 +29,7 @@ export const IsolateActionResult = memo<ActionRequestComponentProps>(
comment,
}
: undefined;
}, [
command.args.args?.comment,
command.commandDefinition?.meta?.agentType,
command.commandDefinition?.meta?.endpointId,
isSentinelOneV1Enabled,
]);
}, [command.args.args?.comment, command.commandDefinition?.meta, isSentinelOneV1Enabled]);

return useConsoleActionSubmitter({
ResultComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ export const KillProcessActionResult = memo<
const actionCreator = useSendKillProcessRequest();

const actionRequestBody = useMemo<undefined | KillProcessRequestBody>(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const agentType = command.commandDefinition?.meta?.agentType;
const { endpointId, agentType } = command.commandDefinition?.meta ?? {};
const parameters = parsedKillOrSuspendParameter(command.args.args);

return endpointId
Expand All @@ -30,11 +29,7 @@ export const KillProcessActionResult = memo<
parameters,
}
: undefined;
}, [
command.args.args,
command.commandDefinition?.meta?.agentType,
command.commandDefinition?.meta?.endpointId,
]);
}, [command.args.args, command.commandDefinition?.meta]);

return useConsoleActionSubmitter<KillProcessRequestBody>({
ResultComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ export const ReleaseActionResult = memo<ActionRequestComponentProps>(
const releaseHostApi = useSendReleaseEndpointRequest();

const actionRequestBody = useMemo(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { endpointId, agentType } = command.commandDefinition?.meta ?? {};
const comment = command.args.args?.comment?.[0];
const agentType = command.commandDefinition?.meta?.agentType;

return endpointId
? {
Expand All @@ -30,12 +29,7 @@ export const ReleaseActionResult = memo<ActionRequestComponentProps>(
comment,
}
: undefined;
}, [
command.args.args?.comment,
command.commandDefinition?.meta?.agentType,
command.commandDefinition?.meta?.endpointId,
isSentinelOneV1Enabled,
]);
}, [command.args.args?.comment, command.commandDefinition?.meta, isSentinelOneV1Enabled]);

return useConsoleActionSubmitter({
ResultComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@ export const SuspendProcessActionResult = memo<
const actionCreator = useSendSuspendProcessRequest();

const actionRequestBody = useMemo<undefined | SuspendProcessRequestBody>(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { agentType, endpointId } = command.commandDefinition?.meta ?? {};
const parameters = parsedKillOrSuspendParameter(command.args.args) as
| ResponseActionParametersWithPid
| ResponseActionParametersWithEntityId;

return endpointId
? {
agent_type: agentType,
endpoint_ids: [endpointId],
comment: command.args.args?.comment?.[0],
parameters,
}
: undefined;
}, [command.args.args, command.commandDefinition?.meta?.endpointId]);
}, [command.args.args, command.commandDefinition?.meta]);

return useConsoleActionSubmitter<SuspendProcessRequestBody, SuspendProcessActionOutputContent>({
ResultComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ export const UploadActionResult = memo<
const actionCreator = useSendUploadEndpointRequest();

const actionRequestBody = useMemo<undefined | UploadActionUIRequestBody>(() => {
const endpointId = command.commandDefinition?.meta?.endpointId;
const { agentType, endpointId } = command.commandDefinition?.meta ?? {};
const { comment, overwrite, file } = command.args.args;

if (!endpointId) {
return;
}

const reqBody: UploadActionUIRequestBody = {
agent_type: agentType,
endpoint_ids: [endpointId],
...(comment?.[0] ? { comment: comment?.[0] } : {}),
parameters:
Expand All @@ -49,7 +50,7 @@ export const UploadActionResult = memo<
};

return reqBody;
}, [command.args.args, command.commandDefinition?.meta?.endpointId]);
}, [command.args.args, command.commandDefinition?.meta]);

const { result, actionDetails } = useConsoleActionSubmitter<
UploadActionUIRequestBody,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{"attributes":{"allowHidden":false,"fieldAttrs":"{\"properties.model\":{},\"properties.resourceAccessed\":{},\"properties.resultCount\":{},\"properties.responseTime\":{},\"properties.errorMessage\":{},\"properties.isEnabledKnowledgeBase\":{},\"properties.isEnabledRAGAlerts\":{},\"properties.assistantStreamingEnabled\":{},\"properties.actionTypeId\":{},\"properties.message\":{},\"properties.productTier\":{},\"properties.failedToDeleteCount\":{},\"properties.totalInstalledCount\":{},\"properties.scoresWritten\":{},\"properties.taskDurationInSeconds\":{},\"properties.interval\":{},\"properties.alertSampleSizePerShard\":{},\"properties.status\":{},\"properties.processing.startTime\":{},\"properties.processing.endTime\":{},\"properties.processing.tookMs\":{},\"properties.result.successful\":{},\"properties.result.failed\":{},\"properties.result.total\":{},\"properties.alertsContextCount\":{},\"properties.alertsCount\":{},\"properties.configuredAlertsCount\":{},\"properties.discoveriesGenerated\":{},\"properties.durationMs\":{},\"properties.provider\":{},\"properties.total_tokens\":{},\"properties.prompt_tokens\":{},\"properties.completion_tokens\":{},\"properties.suppressionRuleType\":{},\"properties.suppressionMissingFields\":{},\"properties.suppressionAlertsCreated\":{},\"properties.suppressionAlertsSuppressed\":{},\"properties.suppressionRuleName\":{},\"properties.suppressionDuration\":{},\"properties.suppressionFieldsNumber\":{},\"properties.suppressionGroupByFieldsNumber\":{},\"properties.suppressionGroupByFields\":{},\"properties.suppressionRuleId\":{}}","fieldFormatMap":"{}","fields":"[]","name":"security-solution-ebt-kibana-server","runtimeFieldMap":"{\"properties.message\":{\"type\":\"keyword\"},\"properties.productTier\":{\"type\":\"keyword\"},\"properties.failedToDeleteCount\":{\"type\":\"long\"},\"properties.totalInstalledCount\":{\"type\":\"long\"},\"properties.model\":{\"type\":\"keyword\"},\"properties.resourceAccessed\":{\"type\":\"keyword\"},\"properties.resultCount\":{\"type\":\"long\"},\"properties.responseTime\":{\"type\":\"long\"},\"properties.errorMessage\":{\"type\":\"keyword\"},\"properties.isEnabledKnowledgeBase\":{\"type\":\"boolean\"},\"properties.isEnabledRAGAlerts\":{\"type\":\"boolean\"},\"properties.assistantStreamingEnabled\":{\"type\":\"boolean\"},\"properties.actionTypeId\":{\"type\":\"keyword\"},\"properties.alertsContextCount\":{\"type\":\"long\"},\"properties.alertsCount\":{\"type\":\"long\"},\"properties.configuredAlertsCount\":{\"type\":\"long\"},\"properties.discoveriesGenerated\":{\"type\":\"long\"},\"properties.durationMs\":{\"type\":\"long\"},\"properties.provider\":{\"type\":\"keyword\"},\"properties.scoresWritten\":{\"type\":\"long\"},\"properties.taskDurationInSeconds\":{\"type\":\"long\"},\"properties.interval\":{\"type\":\"keyword\"},\"properties.alertSampleSizePerShard\":{\"type\":\"long\"},\"properties.status\":{\"type\":\"keyword\"},\"properties.processing.startTime\":{\"type\":\"date\"},\"properties.processing.endTime\":{\"type\":\"date\"},\"properties.processing.tookMs\":{\"type\":\"long\"},\"properties.result.successful\":{\"type\":\"long\"},\"properties.result.failed\":{\"type\":\"long\"},\"properties.result.total\":{\"type\":\"long\"},\"properties.total_tokens\":{\"type\":\"long\"},\"properties.prompt_tokens\":{\"type\":\"long\"},\"properties.completion_tokens\":{\"type\":\"keyword\"},\"properties.suppressionMissingFields\":{\"type\":\"boolean\"},\"properties.suppressionAlertsCreated\":{\"type\":\"long\"},\"properties.suppressionAlertsSuppressed\":{\"type\":\"long\"},\"properties.suppressionRuleName\":{\"type\":\"keyword\"},\"properties.suppressionDuration\":{\"type\":\"long\"},\"properties.suppressionRuleType\":{\"type\":\"keyword\"},\"properties.suppressionGroupByFieldsNumber\":{\"type\":\"long\"},\"properties.suppressionGroupByFields\":{\"type\":\"keyword\"},\"properties.suppressionRuleId\":{\"type\":\"keyword\"}}","sourceFilters":"[]","timeFieldName":"timestamp","title":"ebt-kibana-server"},"coreMigrationVersion":"8.8.0","created_at":"2024-05-30T16:12:44.874Z","id":"security-solution-ebt-kibana-server","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-07-30T11:12:43.928Z","version":"WzM4ODczLDVd"}
{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":1,"missingRefCount":0,"missingReferences":[]}
{"attributes":{"allowHidden":false,"fieldAttrs":"{\"properties.model\":{},\"properties.resourceAccessed\":{},\"properties.resultCount\":{},\"properties.responseTime\":{},\"properties.errorMessage\":{},\"properties.isEnabledKnowledgeBase\":{},\"properties.isEnabledRAGAlerts\":{},\"properties.assistantStreamingEnabled\":{},\"properties.actionTypeId\":{},\"properties.message\":{},\"properties.productTier\":{},\"properties.failedToDeleteCount\":{},\"properties.totalInstalledCount\":{},\"properties.scoresWritten\":{},\"properties.taskDurationInSeconds\":{},\"properties.interval\":{},\"properties.alertSampleSizePerShard\":{},\"properties.status\":{},\"properties.processing.startTime\":{},\"properties.processing.endTime\":{},\"properties.processing.tookMs\":{},\"properties.result.successful\":{},\"properties.result.failed\":{},\"properties.result.total\":{},\"properties.alertsContextCount\":{},\"properties.alertsCount\":{},\"properties.configuredAlertsCount\":{},\"properties.discoveriesGenerated\":{},\"properties.durationMs\":{},\"properties.provider\":{},\"properties.total_tokens\":{},\"properties.prompt_tokens\":{},\"properties.completion_tokens\":{},\"properties.suppressionRuleType\":{},\"properties.suppressionMissingFields\":{},\"properties.suppressionAlertsCreated\":{},\"properties.suppressionAlertsSuppressed\":{},\"properties.suppressionRuleName\":{},\"properties.suppressionDuration\":{},\"properties.suppressionFieldsNumber\":{},\"properties.suppressionGroupByFieldsNumber\":{},\"properties.suppressionGroupByFields\":{},\"properties.suppressionRuleId\":{},\"properties.responseActions.actionId\":{},\"properties.responseActions.agentType\":{},\"properties.responseActions.command\":{},\"properties.responseActions.endpointIds\":{},\"properties.responseActions.isAutomated\":{},\"properties.responseActions.actionStatus\":{}}","fieldFormatMap":"{}","fields":"[]","name":"security-solution-ebt-kibana-server","runtimeFieldMap":"{\"properties.message\":{\"type\":\"keyword\"},\"properties.productTier\":{\"type\":\"keyword\"},\"properties.failedToDeleteCount\":{\"type\":\"long\"},\"properties.totalInstalledCount\":{\"type\":\"long\"},\"properties.isEnabledKnowledgeBase\":{\"type\":\"boolean\"},\"properties.isEnabledRAGAlerts\":{\"type\":\"boolean\"},\"properties.total_tokens\":{\"type\":\"long\"},\"properties.prompt_tokens\":{\"type\":\"long\"},\"properties.completion_tokens\":{\"type\":\"keyword\"},\"properties.suppressionGroupByFields\":{\"type\":\"keyword\"},\"properties.model\":{\"type\":\"keyword\"},\"properties.resourceAccessed\":{\"type\":\"keyword\"},\"properties.resultCount\":{\"type\":\"long\"},\"properties.responseTime\":{\"type\":\"long\"},\"properties.errorMessage\":{\"type\":\"keyword\"},\"properties.assistantStreamingEnabled\":{\"type\":\"boolean\"},\"properties.actionTypeId\":{\"type\":\"keyword\"},\"properties.alertsContextCount\":{\"type\":\"long\"},\"properties.alertsCount\":{\"type\":\"long\"},\"properties.configuredAlertsCount\":{\"type\":\"long\"},\"properties.discoveriesGenerated\":{\"type\":\"long\"},\"properties.durationMs\":{\"type\":\"long\"},\"properties.provider\":{\"type\":\"keyword\"},\"properties.scoresWritten\":{\"type\":\"long\"},\"properties.taskDurationInSeconds\":{\"type\":\"long\"},\"properties.interval\":{\"type\":\"keyword\"},\"properties.alertSampleSizePerShard\":{\"type\":\"long\"},\"properties.status\":{\"type\":\"keyword\"},\"properties.processing.startTime\":{\"type\":\"date\"},\"properties.processing.endTime\":{\"type\":\"date\"},\"properties.processing.tookMs\":{\"type\":\"long\"},\"properties.result.successful\":{\"type\":\"long\"},\"properties.result.failed\":{\"type\":\"long\"},\"properties.result.total\":{\"type\":\"long\"},\"properties.suppressionAlertsCreated\":{\"type\":\"long\"},\"properties.suppressionAlertsSuppressed\":{\"type\":\"long\"},\"properties.suppressionRuleName\":{\"type\":\"keyword\"},\"properties.suppressionDuration\":{\"type\":\"long\"},\"properties.suppressionGroupByFieldsNumber\":{\"type\":\"long\"},\"properties.suppressionRuleType\":{\"type\":\"keyword\"},\"properties.suppressionMissingFields\":{\"type\":\"boolean\"},\"properties.suppressionRuleId\":{\"type\":\"keyword\"},\"properties.responseActions.actionId\":{\"type\":\"keyword\"},\"properties.responseActions.agentType\":{\"type\":\"keyword\"},\"properties.responseActions.command\":{\"type\":\"keyword\"},\"properties.responseActions.endpointIds\":{\"type\":\"keyword\"},\"properties.responseActions.isAutomated\":{\"type\":\"boolean\"},\"properties.responseActions.actionStatus\":{\"type\":\"keyword\"}}","sourceFilters":"[]","timeFieldName":"timestamp","title":"ebt-kibana-server"},"coreMigrationVersion":"8.8.0","created_at":"2024-05-30T16:12:44.874Z","id":"security-solution-ebt-kibana-server","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-09-16T11:22:09.683Z","version":"WzQ2MDU0LDdd"}
{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":1,"missingRefCount":0,"missingReferences":[]}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import type {
AnalyticsServiceSetup,
ElasticsearchClient,
KibanaRequest,
Logger,
Expand Down Expand Up @@ -58,6 +59,7 @@ export interface EndpointAppContextServiceSetupContract {
securitySolutionRequestContextFactory: IRequestContextFactory;
cloud: CloudSetup;
loggerFactory: LoggerFactory;
telemetry: AnalyticsServiceSetup;
}

export interface EndpointAppContextServiceStartContract {
Expand Down Expand Up @@ -339,4 +341,11 @@ export class EndpointAppContextService {

return this.startDependencies.createFleetActionsClient('endpoint');
}

public getTelemetryService(): AnalyticsServiceSetup {
if (!this.setupDependencies?.telemetry) {
throw new EndpointAppContentServicesNotSetUpError();
}
return this.setupDependencies.telemetry;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import type { ScopedClusterClientMock } from '@kbn/core/server/mocks';
import {
analyticsServiceMock,
elasticsearchServiceMock,
httpServerMock,
httpServiceMock,
Expand Down Expand Up @@ -128,6 +129,7 @@ export const createMockEndpointAppContextService = (
getExceptionListsClient: jest.fn(),
getMessageSigningService: jest.fn().mockReturnValue(messageSigningService),
getFleetActionsClient: jest.fn(async (_) => fleetActionsClientMock),
getTelemetryService: jest.fn(),
getInternalResponseActionsClient: jest.fn(() => {
return responseActionsClientMock.create();
}),
Expand All @@ -143,6 +145,7 @@ export const createMockEndpointAppContextServiceSetupContract =
securitySolutionRequestContextFactory: requestContextFactoryMock.create(),
cloud: cloudMock.createSetup(),
loggerFactory: loggingSystemMock.create(),
telemetry: analyticsServiceMock.createAnalyticsServiceSetup(),
};
};

Expand Down
Loading

0 comments on commit 36852cf

Please sign in to comment.