mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[EDR Workflows] Extend Defend Insights telemetry events to include new fields. (#216967)
This PR introduces a new event type, `endpoint_workflow_insights_remediated_event`, and extends some of the existing ones. The goal is to enable better monitoring of the **Defend Insights** feature usage. ### Event Types - **`defend_insight_success`** – Sent when the Scan button triggers an API call and an insight is successfully created. This carries most of the valuable data, such as result contents, duration, etc. - **`endpoint_workflow_insights_remediated_event`** – Sent when a Trusted App is added as a result of an insight, and that insight is marked as remediated. - **`defend_insight_error`** – Sent when insight generation fails and no results are returned. ### Data sent to telemetry **`defend_insight_error`** ``` actionTypeId – Kibana connector type errorMessage – Error message from ES/LLM model – LLM model provider – Model provider ``` **`endpoint_workflow_insights_remediated_event`** ``` insightId – The ID of the action that was sent to the endpoint (currently unused) ``` **`defend_insight_success`** ``` actionTypeId – Kibana connector type eventsContextCount – Number of events sent as context to the LLM insightsGenerated – Number of Defend insights generated durationMs – Duration of the request in milliseconds model – LLM model provider – Model provider insightType – Type of Defend insight (e.g., incompatible-antivirus) insightsDetails – Details of the generated insights (e.g., ["ClamAV", "Avast"]) ```
This commit is contained in:
parent
4ca5e062f7
commit
387e2d95ec
7 changed files with 78 additions and 8 deletions
|
@ -456,6 +456,8 @@ export const DEFEND_INSIGHT_SUCCESS_EVENT: EventTypeOpts<{
|
|||
durationMs: number;
|
||||
model?: string;
|
||||
provider?: string;
|
||||
insightType: string;
|
||||
insightsDetails: string[];
|
||||
}> = {
|
||||
eventType: 'defend_insight_success',
|
||||
schema: {
|
||||
|
@ -501,6 +503,25 @@ export const DEFEND_INSIGHT_SUCCESS_EVENT: EventTypeOpts<{
|
|||
optional: true,
|
||||
},
|
||||
},
|
||||
insightType: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'Defend insight type',
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
insightsDetails: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'Details of the generated Defend insights',
|
||||
},
|
||||
},
|
||||
_meta: {
|
||||
description: 'Details of the generated Defend insights',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ describe('defend insights route helpers', () => {
|
|||
rawDefendInsights: '{"eventsContextCount": 5, "insights": ["insight1", "insight2"]}',
|
||||
startTime: moment(),
|
||||
telemetry: { reportEvent: jest.fn() } as any,
|
||||
insightType: DefendInsightType.Enum.incompatible_antivirus,
|
||||
};
|
||||
await updateDefendInsights(params);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
SavedObjectsClientContract,
|
||||
} from '@kbn/core/server';
|
||||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import type {
|
||||
import {
|
||||
ApiConfig,
|
||||
ContentReferencesStore,
|
||||
DefendInsightGenerationInterval,
|
||||
|
@ -22,6 +22,10 @@ import type {
|
|||
DefendInsightsPostRequestBody,
|
||||
DefendInsightsResponse,
|
||||
Replacements,
|
||||
DEFEND_INSIGHTS_ID,
|
||||
DefendInsightStatus,
|
||||
DefendInsightType,
|
||||
DefendInsightsGetRequestQuery,
|
||||
} from '@kbn/elastic-assistant-common';
|
||||
import type { AnonymizationFieldResponse } from '@kbn/elastic-assistant-common/impl/schemas/anonymization_fields/bulk_crud_anonymization_fields_route.gen';
|
||||
import type { ActionsClient } from '@kbn/actions-plugin/server';
|
||||
|
@ -30,12 +34,6 @@ import { ActionsClientLlm } from '@kbn/langchain/server';
|
|||
import { getLangSmithTracer } from '@kbn/langchain/server/tracers/langsmith';
|
||||
import { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
import {
|
||||
DEFEND_INSIGHTS_ID,
|
||||
DefendInsightStatus,
|
||||
DefendInsightType,
|
||||
DefendInsightsGetRequestQuery,
|
||||
} from '@kbn/elastic-assistant-common';
|
||||
|
||||
import { getDefendInsightsPrompt } from '../../lib/defend_insights/graphs/default_defend_insights_graph/nodes/helpers/prompts';
|
||||
import type { GraphState } from '../../lib/defend_insights/graphs/default_defend_insights_graph/types';
|
||||
|
@ -269,6 +267,18 @@ export async function createDefendInsight(
|
|||
};
|
||||
}
|
||||
|
||||
const extractInsightsForTelemetryReporting = (
|
||||
insightType: DefendInsightType,
|
||||
insights: DefendInsights
|
||||
): string[] => {
|
||||
switch (insightType) {
|
||||
case DefendInsightType.Enum.incompatible_antivirus:
|
||||
return insights.map((insight) => insight.group);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export async function updateDefendInsights({
|
||||
anonymizedEvents,
|
||||
apiConfig,
|
||||
|
@ -280,6 +290,7 @@ export async function updateDefendInsights({
|
|||
logger,
|
||||
startTime,
|
||||
telemetry,
|
||||
insightType,
|
||||
}: {
|
||||
anonymizedEvents: Document[];
|
||||
apiConfig: ApiConfig;
|
||||
|
@ -291,6 +302,7 @@ export async function updateDefendInsights({
|
|||
logger: Logger;
|
||||
startTime: Moment;
|
||||
telemetry: AnalyticsServiceSetup;
|
||||
insightType: DefendInsightType;
|
||||
}) {
|
||||
try {
|
||||
const currentInsight = await dataClient.getDefendInsight({
|
||||
|
@ -324,13 +336,19 @@ export async function updateDefendInsights({
|
|||
defendInsightUpdateProps: updateProps,
|
||||
authenticatedUser,
|
||||
});
|
||||
|
||||
telemetry.reportEvent(DEFEND_INSIGHT_SUCCESS_EVENT.eventType, {
|
||||
actionTypeId: apiConfig.actionTypeId,
|
||||
eventsContextCount: updateProps.eventsContextCount,
|
||||
insightsGenerated: updateProps.insights?.length ?? 0,
|
||||
insightsDetails: extractInsightsForTelemetryReporting(
|
||||
insightType,
|
||||
updateProps.insights || []
|
||||
),
|
||||
durationMs,
|
||||
model: apiConfig.model,
|
||||
provider: apiConfig.provider,
|
||||
insightType,
|
||||
});
|
||||
} catch (updateErr) {
|
||||
logger.error(updateErr);
|
||||
|
|
|
@ -162,6 +162,7 @@ export const postDefendInsightsRoute = (router: IRouter<ElasticAssistantRequestH
|
|||
logger,
|
||||
startTime,
|
||||
telemetry,
|
||||
insightType,
|
||||
}).then(() => insights)
|
||||
)
|
||||
.then((insights) =>
|
||||
|
|
|
@ -41,6 +41,9 @@ describe('Update Insights Route Handler', () => {
|
|||
service: {
|
||||
...mockEndpointContext.service,
|
||||
getEndpointAuthz: jest.fn().mockResolvedValue(authz),
|
||||
getTelemetryService: jest.fn().mockReturnValue({
|
||||
reportEvent: jest.fn(),
|
||||
}),
|
||||
},
|
||||
securitySolution: {
|
||||
getEndpointAuthz: jest.fn().mockResolvedValue(authz),
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { RequestHandler } from '@kbn/core/server';
|
||||
import { ENDPOINT_WORKFLOW_INSIGHTS_REMEDIATED_EVENT } from '../../../lib/telemetry/event_based/events';
|
||||
import type {
|
||||
UpdateWorkflowInsightsRequestBody,
|
||||
UpdateWorkflowInsightsRequestParams,
|
||||
|
@ -73,9 +74,18 @@ const updateInsightsRouteHandler = (
|
|||
return async (_, request, response) => {
|
||||
const { insightId } = request.params;
|
||||
const { canWriteWorkflowInsights } = await endpointContext.service.getEndpointAuthz(request);
|
||||
if (!canWriteWorkflowInsights && !isOnlyActionTypeUpdate(request.body)) {
|
||||
const onlyActionTypeUpdate = isOnlyActionTypeUpdate(request.body);
|
||||
if (!canWriteWorkflowInsights && !onlyActionTypeUpdate) {
|
||||
return response.forbidden({ body: 'Unauthorized to update workflow insights' });
|
||||
}
|
||||
if (onlyActionTypeUpdate) {
|
||||
if (request.body.action?.type === 'remediated') {
|
||||
const telemetry = endpointContext.service.getTelemetryService();
|
||||
telemetry.reportEvent(ENDPOINT_WORKFLOW_INSIGHTS_REMEDIATED_EVENT.eventType, {
|
||||
insightId,
|
||||
});
|
||||
}
|
||||
}
|
||||
logger.debug(`Updating insight ${insightId}`);
|
||||
try {
|
||||
const body = await securityWorkflowInsightsService.update(
|
||||
|
|
|
@ -1241,6 +1241,21 @@ export const SIEM_MIGRATIONS_RULE_TRANSLATION_FAILURE: EventTypeOpts<{
|
|||
},
|
||||
};
|
||||
|
||||
export const ENDPOINT_WORKFLOW_INSIGHTS_REMEDIATED_EVENT: EventTypeOpts<{
|
||||
insightId: string;
|
||||
}> = {
|
||||
eventType: 'endpoint_workflow_insights_remediated_event',
|
||||
schema: {
|
||||
insightId: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'The ID of the action that was sent to the endpoint',
|
||||
optional: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const events = [
|
||||
RISK_SCORE_EXECUTION_SUCCESS_EVENT,
|
||||
RISK_SCORE_EXECUTION_ERROR_EVENT,
|
||||
|
@ -1250,6 +1265,7 @@ export const events = [
|
|||
ENDPOINT_RESPONSE_ACTION_SENT_EVENT,
|
||||
ENDPOINT_RESPONSE_ACTION_SENT_ERROR_EVENT,
|
||||
ENDPOINT_RESPONSE_ACTION_STATUS_CHANGE_EVENT,
|
||||
ENDPOINT_WORKFLOW_INSIGHTS_REMEDIATED_EVENT,
|
||||
FIELD_RETENTION_ENRICH_POLICY_EXECUTION_EVENT,
|
||||
ENTITY_STORE_DATA_VIEW_REFRESH_EXECUTION_EVENT,
|
||||
ENTITY_ENGINE_RESOURCE_INIT_FAILURE_EVENT,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue