[Security solution] Add additional properties to attack discovery telemetry (#182249)

This commit is contained in:
Steph Milovic 2024-05-01 22:48:36 -06:00 committed by GitHub
parent 7d97184065
commit e306074aa9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 106 additions and 11 deletions

3
.github/CODEOWNERS vendored
View file

@ -1348,8 +1348,9 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution_ess/ @elastic/security-solution
/x-pack/plugins/security_solution_serverless/ @elastic/security-solution
# AI Assistant in Security Solution
# GenAI in Security Solution
/x-pack/plugins/security_solution/public/assistant @elastic/security-generative-ai
/x-pack/plugins/security_solution/public/attack_discovery @elastic/security-generative-ai
/x-pack/test/security_solution_cypress/cypress/e2e/ai_assistant @elastic/security-generative-ai
# Security Solution cross teams ownership

View file

@ -30,16 +30,19 @@ interface GenAiConfig {
export const getGenAiConfig = (connector: ActionConnector | undefined): GenAiConfig | undefined => {
if (!connector?.isPreconfigured) {
const config = (connector as ActionConnectorProps<GenAiConfig, unknown>)?.config;
if (config?.apiProvider === OpenAiProviderType.AzureAi) {
return {
...config,
defaultModel: getAzureApiVersionParameter(config.apiUrl ?? ''),
};
}
const { apiProvider, apiUrl, defaultModel } = config ?? {};
return (connector as ActionConnectorProps<GenAiConfig, unknown>)?.config;
return {
apiProvider,
apiUrl,
defaultModel:
apiProvider === OpenAiProviderType.AzureAi
? getAzureApiVersionParameter(apiUrl ?? '')
: defaultModel,
};
}
return undefined;
return undefined; // the connector is neither available nor editable
};
export const getActionTypeTitle = (actionType: ActionTypeModel): string => {

View file

@ -42,10 +42,16 @@ describe('useAttackDiscoveryTelemetry', () => {
await result.current.reportAttackDiscoveriesGenerated({
actionTypeId: '.gen-ai',
model: 'gpt-4',
durationMs: 8000,
alertsCount: 20,
configuredAlertsCount: 30,
});
expect(reportAttackDiscoveriesGenerated).toHaveBeenCalledWith({
actionTypeId: '.gen-ai',
model: 'gpt-4',
durationMs: 8000,
alertsCount: 20,
configuredAlertsCount: 30,
});
});
});

View file

@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
import type { ActionConnectorProps } from '@kbn/triggers-actions-ui-plugin/public/types';
// aligns with OpenAiProviderType from '@kbn/stack-connectors-plugin/common/openai/types'
enum OpenAiProviderType {
OpenAi = 'OpenAI',
AzureAi = 'Azure OpenAI',
}
interface GenAiConfig {
apiProvider?: OpenAiProviderType;
apiUrl?: string;
defaultModel?: string;
}
/**
* Returns the GenAiConfig for a given ActionConnector. Note that if the connector is preconfigured,
* the config will be undefined as the connector is neither available nor editable.
*
* @param connector
*/
export const getGenAiConfig = (connector: ActionConnector | undefined): GenAiConfig | undefined => {
if (!connector?.isPreconfigured) {
const config = (connector as ActionConnectorProps<GenAiConfig, unknown>)?.config;
const { apiProvider, apiUrl, defaultModel } = config ?? {};
return {
apiProvider,
apiUrl,
defaultModel:
apiProvider === OpenAiProviderType.AzureAi
? getAzureApiVersionParameter(apiUrl ?? '')
: defaultModel,
};
}
return undefined; // the connector is neither available nor editable
};
const getAzureApiVersionParameter = (url: string): string | undefined => {
const urlSearchParams = new URLSearchParams(new URL(url).search);
return urlSearchParams.get('api-version') ?? undefined;
};

View file

@ -16,7 +16,7 @@ import {
AttackDiscoveryPostResponse,
ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION,
} from '@kbn/elastic-assistant-common';
import { isEmpty } from 'lodash/fp';
import { isEmpty, uniq } from 'lodash/fp';
import moment from 'moment';
import React, { useCallback, useMemo, useState } from 'react';
import { useLocalStorage, useSessionStorage } from 'react-use';
@ -41,6 +41,7 @@ import {
} from '../pages/session_storage';
import { ERROR_GENERATING_ATTACK_DISCOVERIES } from '../pages/translations';
import type { AttackDiscovery, GenerationInterval } from '../types';
import { getGenAiConfig } from './helpers';
const MAX_GENERATION_INTERVALS = 5;
@ -230,7 +231,17 @@ export const useAttackDiscovery = ({
setAttackDiscoveries(newAttackDiscoveries);
setLastUpdated(newLastUpdated);
setConnectorId?.(connectorId);
reportAttackDiscoveriesGenerated({ actionTypeId });
const connectorConfig = getGenAiConfig(selectedConnector);
reportAttackDiscoveriesGenerated({
actionTypeId,
durationMs,
alertsCount: uniq(
newAttackDiscoveries.flatMap((attackDiscovery) => attackDiscovery.alertIds)
).length,
configuredAlertsCount: knowledgeBase.latestAlerts,
provider: connectorConfig?.apiProvider,
model: connectorConfig?.defaultModel,
});
} catch (error) {
toasts?.addDanger(error, {
title: ERROR_GENERATING_ATTACK_DISCOVERIES,

View file

@ -18,6 +18,27 @@ export const insightsGeneratedEvent: TelemetryEvent = {
optional: false,
},
},
durationMs: {
type: 'integer',
_meta: {
description: 'Duration of request in ms',
optional: false,
},
},
alertsCount: {
type: 'integer',
_meta: {
description: 'Number of unique alerts referenced in the attack discoveries',
optional: false,
},
},
configuredAlertsCount: {
type: 'integer',
_meta: {
description: 'Number of alerts configured by the user',
optional: false,
},
},
model: {
type: 'keyword',
_meta: {

View file

@ -12,6 +12,9 @@ export interface ReportAttackDiscoveriesGeneratedParams {
actionTypeId: string;
provider?: string;
model?: string;
durationMs: number;
alertsCount: number;
configuredAlertsCount: number;
}
export type ReportAttackDiscoveryTelemetryEventParams = ReportAttackDiscoveriesGeneratedParams;