diff --git a/packages/deeplinks/security/deep_links.ts b/packages/deeplinks/security/deep_links.ts index 7cf870a6ecdffe..ea1d818cf8010e 100644 --- a/packages/deeplinks/security/deep_links.ts +++ b/packages/deeplinks/security/deep_links.ts @@ -8,9 +8,9 @@ export enum SecurityPageName { administration = 'administration', - aiInsights = 'ai_insights', alerts = 'alerts', assets = 'assets', + attackDiscovery = 'attack_discovery', blocklist = 'blocklist', /* * Warning: Computed values are not permitted in an enum with string valued members diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/capabilities/index.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/capabilities/index.ts index 348ac6188514da..86c3b26f200e68 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/capabilities/index.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/capabilities/index.ts @@ -14,6 +14,6 @@ export type AssistantFeatures = { [K in keyof typeof defaultAssistantFeatures]: * Default features available to the elastic assistant */ export const defaultAssistantFeatures = Object.freeze({ - assistantAlertsInsights: false, assistantModelEvaluation: false, + attackDiscoveryEnabled: false, }); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.gen.ts new file mode 100644 index 00000000000000..b492dfe2dc9f28 --- /dev/null +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.gen.ts @@ -0,0 +1,75 @@ +/* + * 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 { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Attack discovery API endpoint + * version: 1 + */ + +import { AnonymizationFieldResponse } from '../anonymization_fields/bulk_crud_anonymization_fields_route.gen'; +import { Replacements, TraceData } from '../conversations/common_attributes.gen'; + +/** + * An attack discovery generated from one or more alerts + */ +export type AttackDiscovery = z.infer; +export const AttackDiscovery = z.object({ + /** + * The alert IDs that the attack discovery is based on + */ + alertIds: z.array(z.string()), + /** + * Details of the attack with bulleted markdown that always uses special syntax for field names and values from the source data. + */ + detailsMarkdown: z.string(), + /** + * A short (no more than a sentence) summary of the attack discovery featuring only the host.name and user.name fields (when they are applicable), using the same syntax + */ + entitySummaryMarkdown: z.string(), + /** + * An array of MITRE ATT&CK tactic for the attack discovery + */ + mitreAttackTactics: z.array(z.string()).optional(), + /** + * A markdown summary of attack discovery, using the same syntax + */ + summaryMarkdown: z.string(), + /** + * A title for the attack discovery, in plain text + */ + title: z.string(), +}); + +export type AttackDiscoveryPostRequestBody = z.infer; +export const AttackDiscoveryPostRequestBody = z.object({ + alertsIndexPattern: z.string(), + anonymizationFields: z.array(AnonymizationFieldResponse), + connectorId: z.string(), + actionTypeId: z.string(), + langSmithProject: z.string().optional(), + langSmithApiKey: z.string().optional(), + model: z.string().optional(), + replacements: Replacements.optional(), + size: z.number(), + subAction: z.enum(['invokeAI', 'invokeStream']), +}); +export type AttackDiscoveryPostRequestBodyInput = z.input; + +export type AttackDiscoveryPostResponse = z.infer; +export const AttackDiscoveryPostResponse = z.object({ + connector_id: z.string().optional(), + attackDiscoveries: z.array(AttackDiscovery).optional(), + replacements: Replacements.optional(), + status: z.string().optional(), + trace_data: TraceData.optional(), +}); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.schema.yaml similarity index 63% rename from x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.schema.yaml rename to x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.schema.yaml index a4a647784ee29b..b32708536f9689 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.schema.yaml +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/attack_discovery/post_attack_discovery_route.schema.yaml @@ -1,13 +1,13 @@ openapi: 3.0.0 info: - title: Alerts insights API endpoint + title: Attack discovery API endpoint version: '1' components: x-codegen-enabled: true schemas: - AlertsInsight: + AttackDiscovery: type: object - description: An insight generated from one or more alerts + description: An attack discovery generated from one or more alerts required: - 'alertIds' - 'detailsMarkdown' @@ -16,38 +16,38 @@ components: - 'title' properties: alertIds: - description: The alert IDs that the insight is based on + description: The alert IDs that the attack discovery is based on items: type: string type: array detailsMarkdown: - description: A detailed insight with bulleted markdown that always uses special syntax for field names and values from the source data. + description: Details of the attack with bulleted markdown that always uses special syntax for field names and values from the source data. type: string entitySummaryMarkdown: - description: A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same syntax + description: A short (no more than a sentence) summary of the attack discovery featuring only the host.name and user.name fields (when they are applicable), using the same syntax type: string mitreAttackTactics: - description: An array of MITRE ATT&CK tactic for the insight + description: An array of MITRE ATT&CK tactic for the attack discovery items: type: string type: array summaryMarkdown: - description: A markdown summary of insight, using the same syntax + description: A markdown summary of attack discovery, using the same syntax type: string title: - description: A title for the insight, in plain text + description: A title for the attack discovery, in plain text type: string paths: - /internal/elastic_assistant/insights/alerts: + /internal/elastic_assistant/attack_discovery: post: - operationId: AlertsInsightsPost + operationId: AttackDiscoveryPost x-codegen-enabled: true - description: Generate insights from alerts - summary: Generate insights from alerts via the Elastic Assistant + description: Generate attack discoveries from alerts + summary: Generate attack discoveries from alerts via the Elastic Assistant tags: - - insights + - attack_discovery - alerts requestBody: required: true @@ -67,7 +67,7 @@ paths: type: string anonymizationFields: items: - $ref: '../../anonymization_fields/bulk_crud_anonymization_fields_route.schema.yaml#/components/schemas/AnonymizationFieldResponse' + $ref: '../anonymization_fields/bulk_crud_anonymization_fields_route.schema.yaml#/components/schemas/AnonymizationFieldResponse' type: array connectorId: type: string @@ -80,7 +80,7 @@ paths: model: type: string replacements: - $ref: '../../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' + $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' size: type: number subAction: @@ -98,16 +98,16 @@ paths: properties: connector_id: type: string - insights: + attackDiscoveries: type: array items: - $ref: '#/components/schemas/AlertsInsight' + $ref: '#/components/schemas/AttackDiscovery' replacements: - $ref: '../../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' + $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/Replacements' status: type: string trace_data: - $ref: '../../conversations/common_attributes.schema.yaml#/components/schemas/TraceData' + $ref: '../conversations/common_attributes.schema.yaml#/components/schemas/TraceData' '400': description: Bad request content: diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.gen.ts index 5a5eaac37b3a68..6e90724097fc8a 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.gen.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.gen.ts @@ -18,6 +18,6 @@ import { z } from 'zod'; export type GetCapabilitiesResponse = z.infer; export const GetCapabilitiesResponse = z.object({ - assistantAlertsInsights: z.boolean(), assistantModelEvaluation: z.boolean(), + attackDiscoveryEnabled: z.boolean(), }); diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.schema.yaml b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.schema.yaml index 65e9be9b2eb9cd..8d28a6b95d55a2 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.schema.yaml +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/capabilities/get_capabilities_route.schema.yaml @@ -19,13 +19,13 @@ paths: schema: type: object properties: - assistantAlertsInsights: - type: boolean assistantModelEvaluation: type: boolean + attackDiscoveryEnabled: + type: boolean required: - - assistantAlertsInsights - assistantModelEvaluation + - attackDiscoveryEnabled '400': description: Generic Error content: diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/index.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/index.ts index c3d635fdf94a71..24d484bdd06c6b 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/index.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/index.ts @@ -18,8 +18,8 @@ export const API_VERSIONS = { export const PUBLIC_API_ACCESS = 'public'; export const INTERNAL_API_ACCESS = 'internal'; -// Alerts Insights Schemas -export * from './insights/alerts/post_alerts_insights_route.gen'; +// Attack discovery Schemas +export * from './attack_discovery/post_attack_discovery_route.gen'; // Evaluation Schemas export * from './evaluation/post_evaluate_route.gen'; diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.gen.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.gen.ts deleted file mode 100644 index cfae1a004a54da..00000000000000 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/insights/alerts/post_alerts_insights_route.gen.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 { z } from 'zod'; - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Alerts insights API endpoint - * version: 1 - */ - -import { AnonymizationFieldResponse } from '../../anonymization_fields/bulk_crud_anonymization_fields_route.gen'; -import { Replacements, TraceData } from '../../conversations/common_attributes.gen'; - -/** - * An insight generated from one or more alerts - */ -export type AlertsInsight = z.infer; -export const AlertsInsight = z.object({ - /** - * The alert IDs that the insight is based on - */ - alertIds: z.array(z.string()), - /** - * A detailed insight with bulleted markdown that always uses special syntax for field names and values from the source data. - */ - detailsMarkdown: z.string(), - /** - * A short (no more than a sentence) summary of the insight featuring only the host.name and user.name fields (when they are applicable), using the same syntax - */ - entitySummaryMarkdown: z.string(), - /** - * An array of MITRE ATT&CK tactic for the insight - */ - mitreAttackTactics: z.array(z.string()).optional(), - /** - * A markdown summary of insight, using the same syntax - */ - summaryMarkdown: z.string(), - /** - * A title for the insight, in plain text - */ - title: z.string(), -}); - -export type AlertsInsightsPostRequestBody = z.infer; -export const AlertsInsightsPostRequestBody = z.object({ - alertsIndexPattern: z.string(), - anonymizationFields: z.array(AnonymizationFieldResponse), - connectorId: z.string(), - actionTypeId: z.string(), - langSmithProject: z.string().optional(), - langSmithApiKey: z.string().optional(), - model: z.string().optional(), - replacements: Replacements.optional(), - size: z.number(), - subAction: z.enum(['invokeAI', 'invokeStream']), -}); -export type AlertsInsightsPostRequestBodyInput = z.input; - -export type AlertsInsightsPostResponse = z.infer; -export const AlertsInsightsPostResponse = z.object({ - connector_id: z.string().optional(), - insights: z.array(AlertsInsight).optional(), - replacements: Replacements.optional(), - status: z.string().optional(), - trace_data: TraceData.optional(), -}); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/capabilities/use_capabilities.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/capabilities/use_capabilities.test.tsx index 258788a09304ff..df1f399dadcc31 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/capabilities/use_capabilities.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api/capabilities/use_capabilities.test.tsx @@ -14,9 +14,9 @@ import { useCapabilities, UseCapabilitiesParams } from './use_capabilities'; import { API_VERSIONS } from '@kbn/elastic-assistant-common'; const statusResponse = { - assistantAlertsInsights: false, assistantModelEvaluation: true, assistantStreamingEnabled: false, + attackDiscoveryEnabled: false, }; const http = { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_context/types.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_context/types.ts index ad1954c0b3b23d..233e45049333cd 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_context/types.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_context/types.ts @@ -57,7 +57,7 @@ export interface PromptContext { id: string; /** - * Replacements associated with the context, i.e. replacements for an insight provided as context + * Replacements associated with the context, i.e. replacements for an attack discovery provided as context */ replacements?: Replacements; @@ -82,7 +82,7 @@ export interface SelectedPromptContext { promptContextId: string; /** this data is not anonymized */ rawData: string | Record; - /** replacements associated with the context, i.e. replacements for an insight provided as context */ + /** replacements associated with the context, i.e. replacements for an attack discovery provided as context */ replacements?: Replacements; } diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx index be23d4951c62b8..3e32bc5d7bb600 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx @@ -73,7 +73,7 @@ export const useAssistantOverlay = ( tooltip: PromptContext['tooltip'], /** - * Optionally provide a map of replacements associated with the context, i.e. replacements for an insight that's provided as context + * Optionally provide a map of replacements associated with the context, i.e. replacements for an attack discovery that's provided as context */ replacements?: Replacements | null ): UseAssistantOverlay => { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx index 61cf86eae97189..d54f6f8b4d28dc 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx @@ -7,7 +7,7 @@ import { KnowledgeBaseConfig } from '../assistant/types'; -export const AI_INSIGHTS_STORAGE_KEY = 'aiInsights'; +export const ATTACK_DISCOVERY_STORAGE_KEY = 'attackDiscovery'; export const DEFAULT_ASSISTANT_NAMESPACE = 'elasticAssistantDefault'; export const QUICK_PROMPT_LOCAL_STORAGE_KEY = 'quickPrompts'; export const SYSTEM_PROMPT_LOCAL_STORAGE_KEY = 'systemPrompts'; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx index f51a72e3149642..7726f54f4637a4 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx @@ -277,16 +277,16 @@ export const AssistantProvider: React.FC = ({ // Fetch assistant capabilities const { data: capabilities } = useCapabilities({ http, toasts }); - const { assistantAlertsInsights, assistantModelEvaluation: modelEvaluatorEnabled } = + const { assistantModelEvaluation: modelEvaluatorEnabled, attackDiscoveryEnabled } = capabilities ?? defaultAssistantFeatures; const value = useMemo( () => ({ actionTypeRegistry, alertsIndexPattern, - assistantAlertsInsights, assistantAvailability, assistantTelemetry, + attackDiscoveryEnabled, augmentMessageCodeBlocks, allQuickPrompts: localStorageQuickPrompts ?? [], allSystemPrompts: localStorageSystemPrompts ?? [], @@ -324,9 +324,9 @@ export const AssistantProvider: React.FC = ({ [ actionTypeRegistry, alertsIndexPattern, - assistantAlertsInsights, assistantAvailability, assistantTelemetry, + attackDiscoveryEnabled, augmentMessageCodeBlocks, localStorageQuickPrompts, localStorageSystemPrompts, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx index a1c0fa45e6a7da..65e388862bd53f 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector_inline/connector_selector_inline.tsx @@ -187,7 +187,6 @@ export const ConnectorSelectorInline: React.FC = React.memo( ): boolean => { const { replacements } = request?.body ?? {}; diff --git a/x-pack/plugins/elastic_assistant/server/routes/insights/alerts/helpers.ts b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers.ts similarity index 78% rename from x-pack/plugins/elastic_assistant/server/routes/insights/alerts/helpers.ts rename to x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers.ts index 97167ea76bd4ac..7fd11c5d525828 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/insights/alerts/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers.ts @@ -8,7 +8,7 @@ import { KibanaRequest } from '@kbn/core/server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { - AlertsInsightsPostRequestBody, + AttackDiscoveryPostRequestBody, ExecuteConnectorRequestBody, Replacements, } from '@kbn/elastic-assistant-common'; @@ -16,9 +16,9 @@ import { ActionsClientLlm } from '@kbn/elastic-assistant-common/impl/language_mo import { AnonymizationFieldResponse } from '@kbn/elastic-assistant-common/impl/schemas/anonymization_fields/bulk_crud_anonymization_fields_route.gen'; import { v4 as uuidv4 } from 'uuid'; -import { AssistantToolParams, ElasticAssistantApiRequestHandlerContext } from '../../../types'; +import { AssistantToolParams, ElasticAssistantApiRequestHandlerContext } from '../../types'; -export const REQUIRED_FOR_INSIGHTS: AnonymizationFieldResponse[] = [ +export const REQUIRED_FOR_ATTACK_DISCOVERY: AnonymizationFieldResponse[] = [ { id: uuidv4(), field: '_id', @@ -52,24 +52,24 @@ export const getAssistantToolParams = ({ request: KibanaRequest< unknown, unknown, - ExecuteConnectorRequestBody | AlertsInsightsPostRequestBody + ExecuteConnectorRequestBody | AttackDiscoveryPostRequestBody >; size: number; }): AssistantToolParams => ({ alertsIndexPattern, - anonymizationFields: [...(anonymizationFields ?? []), ...REQUIRED_FOR_INSIGHTS], - isEnabledKnowledgeBase: false, // not required for insights - chain: undefined, // not required for insights + anonymizationFields: [...(anonymizationFields ?? []), ...REQUIRED_FOR_ATTACK_DISCOVERY], + isEnabledKnowledgeBase: false, // not required for attack discovery + chain: undefined, // not required for attack discovery esClient, llm, - modelExists: false, // not required for insights + modelExists: false, // not required for attack discovery onNewReplacements, replacements: latestReplacements, request, size, }); -export const isInsightsFeatureEnabled = ({ +export const isAttackDiscoveryFeatureEnabled = ({ assistantContext, pluginName, }: { @@ -78,5 +78,5 @@ export const isInsightsFeatureEnabled = ({ }): boolean => { const registeredFeatures = assistantContext.getRegisteredFeatures(pluginName); - return registeredFeatures.assistantAlertsInsights === true; + return registeredFeatures.attackDiscoveryEnabled === true; }; diff --git a/x-pack/plugins/elastic_assistant/server/routes/insights/alerts/post_alerts_insights.ts b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post_attack_discovery.ts similarity index 73% rename from x-pack/plugins/elastic_assistant/server/routes/insights/alerts/post_alerts_insights.ts rename to x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post_attack_discovery.ts index 248e366646a8e4..3c92bee4d6a739 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/insights/alerts/post_alerts_insights.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post_attack_discovery.ts @@ -9,26 +9,28 @@ import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/ import { ActionsClientLlm } from '@kbn/elastic-assistant-common/impl/language_models'; import { type IKibanaResponse, IRouter, Logger } from '@kbn/core/server'; import { - AlertsInsightsPostRequestBody, - AlertsInsightsPostResponse, + AttackDiscoveryPostRequestBody, + AttackDiscoveryPostResponse, ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, Replacements, } from '@kbn/elastic-assistant-common'; import { transformError } from '@kbn/securitysolution-es-utils'; -import { INSIGHTS_ALERTS } from '../../../../common/constants'; -import { getAssistantToolParams, isInsightsFeatureEnabled } from './helpers'; -import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from '../../helpers'; -import { getLangSmithTracer } from '../../evaluate/utils'; -import { buildResponse } from '../../../lib/build_response'; -import { ElasticAssistantRequestHandlerContext } from '../../../types'; -import { getLlmType } from '../../utils'; - -export const postAlertsInsightsRoute = (router: IRouter) => { +import { ATTACK_DISCOVERY } from '../../../common/constants'; +import { getAssistantToolParams, isAttackDiscoveryFeatureEnabled } from './helpers'; +import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from '../helpers'; +import { getLangSmithTracer } from '../evaluate/utils'; +import { buildResponse } from '../../lib/build_response'; +import { ElasticAssistantRequestHandlerContext } from '../../types'; +import { getLlmType } from '../utils'; + +export const postAttackDiscoveryRoute = ( + router: IRouter +) => { router.versioned .post({ access: 'internal', - path: INSIGHTS_ALERTS, + path: ATTACK_DISCOVERY, options: { tags: ['access:elasticAssistant'], }, @@ -38,16 +40,16 @@ export const postAlertsInsightsRoute = (router: IRouter> => { + async (context, request, response): Promise> => { const resp = buildResponse(response); const assistantContext = await context.elasticAssistant; const logger: Logger = assistantContext.logger; @@ -62,12 +64,12 @@ export const postAlertsInsightsRoute = (router: IRouter tool.id === 'insights-tool'); + const assistantTool = assistantTools.find((tool) => tool.id === 'attack-discovery'); if (!assistantTool) { - return response.notFound(); // insights tool not found + return response.notFound(); // attack discovery tool not found } const traceOptions = { @@ -116,7 +118,7 @@ export const postAlertsInsightsRoute = (router: IRouter { describe('Capabilities', () => { it('returns a 404 if evaluate feature is not registered', async () => { context.elasticAssistant.getRegisteredFeatures.mockReturnValueOnce({ - assistantAlertsInsights: false, assistantModelEvaluation: false, + attackDiscoveryEnabled: false, }); const response = await server.inject( diff --git a/x-pack/plugins/elastic_assistant/server/routes/index.ts b/x-pack/plugins/elastic_assistant/server/routes/index.ts index 32886fe150ee96..352b91624f7fb1 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/index.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/index.ts @@ -8,8 +8,8 @@ // Actions Connector Execute (LLM Wrapper) export { postActionsConnectorExecuteRoute } from './post_actions_connector_execute'; -// Alerts Insights -export { postAlertsInsightsRoute } from './insights/alerts/post_alerts_insights'; +// Attack Discovery +export { postAttackDiscoveryRoute } from './attack_discovery/post_attack_discovery'; // Knowledge Base export { deleteKnowledgeBaseRoute } from './knowledge_base/delete_knowledge_base'; diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts index a82d20047aa1f0..32f5fc47ae7706 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -304,7 +304,7 @@ export const postActionsConnectorExecuteRoute = ( }); const assistantTools = (await context.elasticAssistant) .getRegisteredTools(pluginName) - .filter((x) => x.id !== 'insights-tool'); // we don't (yet) support asking the assistant for NEW insights from a conversation + .filter((x) => x.id !== 'attack-discovery'); // We don't (yet) support asking the assistant for NEW attack discoveries from a conversation // get a scoped esClient for assistant memory const esClient = (await context.core).elasticsearch.client.asCurrentUser; diff --git a/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts b/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts index 7c0f970c7f6f91..325f4a84ab8c7a 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/register_routes.ts @@ -8,7 +8,7 @@ import type { KibanaRequest, Logger, SavedObjectsClientContract } from '@kbn/core/server'; import { once } from 'lodash/fp'; -import { postAlertsInsightsRoute } from './insights/alerts/post_alerts_insights'; +import { postAttackDiscoveryRoute } from './attack_discovery/post_attack_discovery'; import { ElasticAssistantPluginRouter, ElasticAssistantPluginSetupDependencies, @@ -80,6 +80,6 @@ export const registerRoutes = ( bulkActionAnonymizationFieldsRoute(router, logger); findAnonymizationFieldsRoute(router, logger); - // Alerts Insights - postAlertsInsightsRoute(router); + // Attack Discovery + postAttackDiscoveryRoute(router); }; diff --git a/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts b/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts index 395710931f5ec1..aac4e53dfae22f 100644 --- a/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts +++ b/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts @@ -53,8 +53,8 @@ describe('AppContextService', () => { it('should return default registered features when stopped ', () => { appContextService.start(mockAppContext); appContextService.registerFeatures('super', { - assistantAlertsInsights: false, assistantModelEvaluation: true, + attackDiscoveryEnabled: false, }); appContextService.stop(); @@ -103,8 +103,8 @@ describe('AppContextService', () => { it('should register and get features for a single plugin', () => { const pluginName = 'pluginName'; const features: AssistantFeatures = { - assistantAlertsInsights: false, assistantModelEvaluation: true, + attackDiscoveryEnabled: false, }; appContextService.start(mockAppContext); @@ -118,13 +118,13 @@ describe('AppContextService', () => { it('should register and get features for multiple plugins', () => { const pluginOne = 'plugin1'; const featuresOne: AssistantFeatures = { - assistantAlertsInsights: false, assistantModelEvaluation: true, + attackDiscoveryEnabled: false, }; const pluginTwo = 'plugin2'; const featuresTwo: AssistantFeatures = { - assistantAlertsInsights: false, assistantModelEvaluation: false, + attackDiscoveryEnabled: false, }; appContextService.start(mockAppContext); @@ -138,12 +138,12 @@ describe('AppContextService', () => { it('should update features if registered again', () => { const pluginName = 'pluginName'; const featuresOne: AssistantFeatures = { - assistantAlertsInsights: false, assistantModelEvaluation: true, + attackDiscoveryEnabled: false, }; const featuresTwo: AssistantFeatures = { - assistantAlertsInsights: false, assistantModelEvaluation: false, + attackDiscoveryEnabled: false, }; appContextService.start(mockAppContext); diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts index 9fe2be7ae1fc6c..74b976fd516f72 100755 --- a/x-pack/plugins/elastic_assistant/server/types.ts +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -27,7 +27,7 @@ import { AuthenticatedUser, SecurityPluginStart } from '@kbn/security-plugin/ser import { RetrievalQAChain } from 'langchain/chains'; import { ElasticsearchClient } from '@kbn/core/server'; import { - AlertsInsightsPostRequestBody, + AttackDiscoveryPostRequestBody, AssistantFeatures, ExecuteConnectorRequestBody, Replacements, @@ -218,7 +218,7 @@ export interface AssistantToolParams { request: KibanaRequest< unknown, unknown, - ExecuteConnectorRequestBody | AlertsInsightsPostRequestBody + ExecuteConnectorRequestBody | AttackDiscoveryPostRequestBody >; size?: number; } diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index a897c108d6f625..0a470b0d234f36 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -98,7 +98,7 @@ export const RULES_CREATE_PATH = `${RULES_PATH}/create` as const; export const EXCEPTIONS_PATH = '/exceptions' as const; export const EXCEPTION_LIST_DETAIL_PATH = `${EXCEPTIONS_PATH}/details/:detailName` as const; export const HOSTS_PATH = '/hosts' as const; -export const AI_INSIGHTS_PATH = '/ai_insights' as const; +export const ATTACK_DISCOVERY_PATH = '/attack_discovery' as const; export const USERS_PATH = '/users' as const; export const KUBERNETES_PATH = '/kubernetes' as const; export const NETWORK_PATH = '/network' as const; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 55568ac8fe69ac..18b13eb3dd193c 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -134,9 +134,9 @@ export const allowedExperimentalValues = Object.freeze({ alertsPageFiltersEnabled: true, /** - * Enables the Assistant Alerts Insights feature and API endpoint + * Enables the Attack discovery feature and API endpoint */ - assistantAlertsInsights: false, + attackDiscoveryEnabled: false, /** * Enables the Assistant Model Evaluation advanced setting and API endpoint, introduced in `8.11.0`. diff --git a/x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.tsx b/x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.tsx deleted file mode 100644 index 864daf9164e891..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 { ReportInsightsGeneratedParams } from '../../../common/lib/telemetry/events/insights/types'; -import { useKibana } from '../../../common/lib/kibana'; - -interface InsightsTelemetry { - reportInsightsGenerated: (params: ReportInsightsGeneratedParams) => void; -} - -// TODO @andrew implement this hook and call the reportInsightsGenerated function wherever insights are generated -export const useInsightsTelemetry = (): InsightsTelemetry => { - const { - services: { telemetry }, - } = useKibana(); - - return { - reportInsightsGenerated: telemetry.reportInsightsGenerated, - }; -}; diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/translations.ts deleted file mode 100644 index faed5f8d69fbb7..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/translations.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const ADD_TO_CASE_SUCCESS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.addToCaseSuccessLabel', - { - defaultMessage: 'Successfully added insight to the case', - } -); - -export const ADD_TO_NEW_CASE = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.addToNewCaseButtonLabel', - { - defaultMessage: 'Add to new case', - } -); - -export const CREATE_A_CASE_FOR_INSIGHT = (title: string) => - i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.createACaseForInsightHeaderText', - { - values: { title }, - defaultMessage: 'Create a case for insight {title}', - } - ); - -export const CASE_DESCRIPTION = (insightTitle: string) => - i18n.translate('xpack.securitySolution.aiInsights.insight.actions.useAddToCase.caseDescription', { - values: { insightTitle }, - defaultMessage: 'This case was opened for insight: _{insightTitle}_', - }); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_existing_case/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_existing_case/translations.ts deleted file mode 100644 index 0d46653de0435b..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_existing_case/translations.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const ADD_TO_CASE_SUCCESS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.addToCaseSuccessLabel', - { - defaultMessage: 'Successfully added insight to the case', - } -); - -export const ADD_TO_NEW_CASE = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.addToNewCaseButtonLabel', - { - defaultMessage: 'Add to new case', - } -); - -export const CREATE_A_CASE_FOR_INSIGHT = (title: string) => - i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.useAddToCase.createACaseForInsightHeaderText', - { - values: { title }, - defaultMessage: 'Create a case for insight {title}', - } - ); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/translations.ts deleted file mode 100644 index e4026d917db723..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/translations.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const ALERTS_WILL_BE_ANALYZED = (alertsCount: number) => - i18n.translate('xpack.securitySolution.aiInsights.pages.emptyPrompt.alertsWillBeAnalyzedTitle', { - defaultMessage: '{alertsCount, plural, one {alert} other {alerts}} will be analyzed', - values: { alertsCount }, - }); - -export const BASED_ON_SELECTED_FILTERS = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.emptyPrompt.basedOnSelectedFiltersLabel', - { - defaultMessage: 'Based on the selected filters above.', - } -); - -export const GENERATE = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.emptyPrompt.generateLabel', - { - defaultMessage: 'Generate', - } -); - -export const LEARN_MORE = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.emptyPrompt.learnMoreLabel', - { - defaultMessage: 'Learn more', - } -); - -export const SELECT_A_CONNECTOR = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.emptyPrompt.selectAConnectorLabel', - { - defaultMessage: 'Select a connector', - } -); - -export const START_GENERATING_INSIGHTS = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.emptyPrompt.startGeneratingInsightsLabel', - { - defaultMessage: 'Start generating insights via Elastic AI Assistant.', - } -); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/translations.ts deleted file mode 100644 index 35f8cdd027dff1..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/translations.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const AI_IS_CURRENTLY_ANALYZING = (alertsCount: number) => - i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.aiIsCurrentlyAnalyzing', - { - defaultMessage: `AI is currently analyzing up to {alertsCount} {alertsCount, plural, =1 {alert} other {alerts}} in the last 24 hours to generate insights`, - values: { alertsCount }, - } - ); - -export const ABOVE_THE_AVERAGE_TIME = i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.aboveTheAverageTimeLabel', - { - defaultMessage: 'Above the average time:', - } -); - -export const APPROXIMATE_TIME_REMAINING = i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.approximateTimeRemainingLabel', - { - defaultMessage: 'Approximate time remaining:', - } -); - -export const AVERAGE_TIME = i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.averageTimeLabel', - { - defaultMessage: 'Average time', - } -); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/translations.ts deleted file mode 100644 index b7c8015f90ad33..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/translations.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const AI_IS_CURRENTLY_ANALYZING = (alertsCount: number) => - i18n.translate('xpack.securitySolution.aiInsights.pages.loadingCallout.aiIsCurrentlyAnalyzing', { - defaultMessage: `AI is currently analyzing up to {alertsCount} {alertsCount, plural, =1 {alert} other {alerts}} in the last 24 hours to generate insights`, - values: { alertsCount }, - }); - -export const INSIGHTS_GENERATION_IN_PROGRESS = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.loadingCallout.hangTightLabel', - { - defaultMessage: 'Insights generation in progress', - } -); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/page_title/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/pages/page_title/translations.ts deleted file mode 100644 index 5b34047faa3608..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/page_title/translations.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const AI_INSIGHTS_PAGE_TITLE = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.pageTitle.pageTitle', - { - defaultMessage: 'AI insights', - } -); - -export const BETA = i18n.translate('xpack.securitySolution.aiInsights.pages.pageTitle.betaBadge', { - defaultMessage: 'Beta', -}); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/translations.ts b/x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/translations.ts deleted file mode 100644 index 0c03b9d6f7a6ad..00000000000000 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/translations.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; - -export const AI_INSIGHTS = i18n.translate( - 'xpack.securitySolution.aiInsights.upgrade.aiInsightsTitle', - { - defaultMessage: 'AI Insights', - } -); - -export const AI_INSIGHTS_ARE_AVAILABLE = i18n.translate( - 'xpack.securitySolution.aiInsights.upgrade.aiInsightsAreAvailable', - { - defaultMessage: 'AI Insights are available to Enterprise users only.', - } -); - -export const PLEASE_UPGRADE = i18n.translate( - 'xpack.securitySolution.aiInsights.upgrade.pleaseUpgradeMessage', - { - defaultMessage: 'Please upgrade your license to use this feature.', - } -); - -export const UPGRADE = i18n.translate('xpack.securitySolution.aiInsights.upgrade.upgradeButton', { - defaultMessage: 'Upgrade', -}); diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts index b9072ba43701e9..0449881d6a636a 100644 --- a/x-pack/plugins/security_solution/public/app/translations.ts +++ b/x-pack/plugins/security_solution/public/app/translations.ts @@ -97,9 +97,12 @@ export const ALERTS = i18n.translate('xpack.securitySolution.navigation.alerts', defaultMessage: 'Alerts', }); -export const AI_INSIGHTS = i18n.translate('xpack.securitySolution.navigation.aiInsights', { - defaultMessage: 'AI Insights', -}); +export const ATTACK_DISCOVERY = i18n.translate( + 'xpack.securitySolution.navigation.attackDiscovery', + { + defaultMessage: 'Attack discovery', + } +); export const TIMELINES = i18n.translate('xpack.securitySolution.navigation.timelines', { defaultMessage: 'Timelines', diff --git a/x-pack/plugins/security_solution/public/app_links.ts b/x-pack/plugins/security_solution/public/app_links.ts index aef2997d8905d6..4140f6bfcd3222 100644 --- a/x-pack/plugins/security_solution/public/app_links.ts +++ b/x-pack/plugins/security_solution/public/app_links.ts @@ -6,7 +6,7 @@ */ import type { CoreStart } from '@kbn/core/public'; -import { links as aiInsightsLinks } from './ai_insights/links'; +import { links as attackDiscoveryLinks } from './attack_discovery/links'; import type { AppLinkItems } from './common/links/types'; import { indicatorsLinks } from './threat_intelligence/links'; import { links as alertsLinks } from './detections/links'; @@ -26,7 +26,7 @@ export { solutionAppLinksSwitcher } from './app/solution_navigation/links/app_li export const appLinks: AppLinkItems = Object.freeze([ dashboardsLinks, alertsLinks, - aiInsightsLinks, + attackDiscoveryLinks, findingsLinks, casesLinks, timelinesLinks, @@ -46,7 +46,7 @@ export const getFilteredLinks = async ( return Object.freeze([ dashboardsLinks, alertsLinks, - aiInsightsLinks, + attackDiscoveryLinks, findingsLinks, casesLinks, timelinesLinks, diff --git a/x-pack/plugins/security_solution/public/ai_insights/attack/attack_chain/axis_tick/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack/attack_chain/axis_tick/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/attack/attack_chain/axis_tick/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack/attack_chain/axis_tick/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/attack/attack_chain/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack/attack_chain/index.tsx similarity index 83% rename from x-pack/plugins/security_solution/public/ai_insights/attack/attack_chain/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack/attack_chain/index.tsx index ab1a0422424519..18df24e4420726 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/attack/attack_chain/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack/attack_chain/index.tsx @@ -11,14 +11,14 @@ import React, { useMemo } from 'react'; import { Tactic } from './tactic'; import { getTacticMetadata } from '../../helpers'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; } -const AttackChainComponent: React.FC = ({ insight }) => { - const tacticMetadata = useMemo(() => getTacticMetadata(insight), [insight]); +const AttackChainComponent: React.FC = ({ attackDiscovery }) => { + const tacticMetadata = useMemo(() => getTacticMetadata(attackDiscovery), [attackDiscovery]); return ( = ({ insight }) => { +const MiniAttackChainComponent: React.FC = ({ attackDiscovery }) => { const { euiTheme } = useEuiTheme(); - const tactics = useMemo(() => getTacticMetadata(insight), [insight]); + const tactics = useMemo(() => getTacticMetadata(attackDiscovery), [attackDiscovery]); const detectedTactics = useMemo(() => tactics.filter((tactic) => tactic.detected), [tactics]); const detectedTacticsList = useMemo( diff --git a/x-pack/plugins/security_solution/public/ai_insights/attack/mini_attack_chain/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/translations.ts similarity index 85% rename from x-pack/plugins/security_solution/public/ai_insights/attack/mini_attack_chain/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/translations.ts index a0bc3e0b94cfe3..2332ab5a0c5ef6 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/attack/mini_attack_chain/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/translations.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; export const ATTACK_CHAIN_TOOLTIP = (tacticsCount: number) => - i18n.translate('xpack.securitySolution.aiInsights.miniAttackChain.attackChainTooltip', { + i18n.translate('xpack.securitySolution.attackDiscovery.miniAttackChain.attackChainTooltip', { defaultMessage: '{tacticsCount} {tacticsCount, plural, one {tactic was} other {tactics were}} identified in the analysis, providing insight into the nature of the detected violations:', values: { tacticsCount }, diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/insight_markdown_parser/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/attack_discovery_markdown_parser/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/insight_markdown_parser/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/attack_discovery_markdown_parser/helpers.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/insight_markdown_parser/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/attack_discovery_markdown_parser/index.tsx similarity index 96% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/insight_markdown_parser/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/attack_discovery_markdown_parser/index.tsx index 11b9d383709351..6812ebb749602f 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/insight_markdown_parser/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/attack_discovery_markdown_parser/index.tsx @@ -11,7 +11,7 @@ import type { RemarkTokenizer } from '@elastic/eui'; import { getIconFromFieldName } from './helpers'; import type { ParsedField } from '../types'; -export const InsightMarkdownParser: Plugin = function () { +export const AttackDiscoveryMarkdownParser: Plugin = function () { // NOTE: the use of `this.Parse` and the other idioms below required by the Remark `Plugin` should NOT be replicated outside this file const Parser = this.Parser; const tokenizers = Parser.prototype.inlineTokenizers; diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/get_host_flyout_panel_props.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/get_host_flyout_panel_props.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/get_host_flyout_panel_props.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/get_host_flyout_panel_props.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/get_user_flyout_panel_props.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/get_user_flyout_panel_props.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/get_user_flyout_panel_props.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/get_user_flyout_panel_props.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/helpers.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/field_markdown_renderer/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/field_markdown_renderer/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/index.tsx similarity index 57% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/index.tsx index 6e45064e23a6a8..bc6a19aecf741d 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/index.tsx @@ -12,7 +12,7 @@ import { } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { InsightMarkdownParser } from './insight_markdown_parser'; +import { AttackDiscoveryMarkdownParser } from './attack_discovery_markdown_parser'; import { getFieldMarkdownRenderer } from './field_markdown_renderer'; interface Props { @@ -20,16 +20,16 @@ interface Props { markdown: string; } -const InsightMarkdownFormatterComponent: React.FC = ({ +const AttackDiscoveryMarkdownFormatterComponent: React.FC = ({ disableActions = false, markdown, }) => { - const insightParsingPluginList = useMemo( - () => [...getDefaultEuiMarkdownParsingPlugins(), InsightMarkdownParser], + const attackDiscoveryParsingPluginList = useMemo( + () => [...getDefaultEuiMarkdownParsingPlugins(), AttackDiscoveryMarkdownParser], [] ); - const insightProcessingPluginList = useMemo(() => { + const attackDiscoveryProcessingPluginList = useMemo(() => { const processingPluginList = getDefaultEuiMarkdownProcessingPlugins(); processingPluginList[1][1].components.fieldPlugin = getFieldMarkdownRenderer(disableActions); @@ -39,15 +39,17 @@ const InsightMarkdownFormatterComponent: React.FC = ({ return ( {markdown} ); }; -InsightMarkdownFormatterComponent.displayName = 'InsightMarkdownFormatter'; +AttackDiscoveryMarkdownFormatterComponent.displayName = 'AttackDiscoveryMarkdownFormatter'; -export const InsightMarkdownFormatter = React.memo(InsightMarkdownFormatterComponent); +export const AttackDiscoveryMarkdownFormatter = React.memo( + AttackDiscoveryMarkdownFormatterComponent +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/types.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/types.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight_markdown_formatter/types.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_markdown_formatter/types.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actionable_summary/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actionable_summary/index.tsx similarity index 69% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actionable_summary/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actionable_summary/index.tsx index cde5325c92c3dd..7f1dbe7be3b2e7 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actionable_summary/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actionable_summary/index.tsx @@ -9,18 +9,18 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import type { Replacements } from '@kbn/elastic-assistant-common'; import React, { useMemo } from 'react'; -import { InsightMarkdownFormatter } from '../../insight_markdown_formatter'; -import type { AlertsInsight } from '../../types'; +import { AttackDiscoveryMarkdownFormatter } from '../../attack_discovery_markdown_formatter'; +import type { AttackDiscovery } from '../../types'; import { ViewInAiAssistant } from '../view_in_ai_assistant'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; showAnonymized?: boolean; } const ActionableSummaryComponent: React.FC = ({ - insight, + attackDiscovery, replacements, showAnonymized = false, }) => { @@ -28,25 +28,31 @@ const ActionableSummaryComponent: React.FC = ({ () => Object.entries(replacements ?? {}).reduce( (acc, [key, value]) => acc.replace(key, value), - insight.entitySummaryMarkdown + attackDiscovery.entitySummaryMarkdown ), - [insight.entitySummaryMarkdown, replacements] + [attackDiscovery.entitySummaryMarkdown, replacements] ); return ( - - + diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/actions_placeholder/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/actions_placeholder/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/actions_placeholder/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/actions_placeholder/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/alerts_badge/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/alerts_badge/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/alerts_badge/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/alerts_badge/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/index.tsx similarity index 86% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/index.tsx index a818ab2c392b8e..3aeba84bee02f8 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/index.tsx @@ -14,14 +14,14 @@ import { AlertsBadge } from './alerts_badge'; import { MiniAttackChain } from '../../attack/mini_attack_chain'; import { TakeAction } from './take_action'; import * as i18n from './translations'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; } -const ActionsComponent: React.FC = ({ insight, replacements }) => { +const ActionsComponent: React.FC = ({ attackDiscovery, replacements }) => { const { euiTheme } = useEuiTheme(); return ( @@ -40,7 +40,7 @@ const ActionsComponent: React.FC = ({ insight, replacements }) => { - + @@ -70,7 +70,7 @@ const ActionsComponent: React.FC = ({ insight, replacements }) => { - + @@ -87,7 +87,7 @@ const ActionsComponent: React.FC = ({ insight, replacements }) => { - + ); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/helpers.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/index.tsx similarity index 84% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/index.tsx index 713a452b0e4502..5e019f6af6653d 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/index.tsx @@ -17,19 +17,19 @@ import React, { useCallback, useMemo, useState } from 'react'; import { useKibana } from '../../../../common/lib/kibana'; import { APP_ID } from '../../../../../common'; -import { getAlertsInsightMarkdown } from '../../../get_alerts_insight_markdown/get_alerts_insight_markdown'; +import { getAttackDiscoveryMarkdown } from '../../../get_attack_discovery_markdown/get_attack_discovery_markdown'; import * as i18n from './translations'; -import type { AlertsInsight } from '../../../types'; +import type { AttackDiscovery } from '../../../types'; import { useAddToNewCase } from '../use_add_to_case'; import { useAddToExistingCase } from '../use_add_to_existing_case'; import { useViewInAiAssistant } from '../../view_in_ai_assistant/use_view_in_ai_assistant'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; } -const TakeActionComponent: React.FC = ({ insight, replacements }) => { +const TakeActionComponent: React.FC = ({ attackDiscovery, replacements }) => { // get dependencies for creating / adding to cases: const { cases } = useKibana().services; const userCasesPermissions = cases.helpers.canUseCases([APP_ID]); @@ -39,7 +39,7 @@ const TakeActionComponent: React.FC = ({ insight, replacements }) => { ); const { disabled: addToCaseDisabled, onAddToNewCase } = useAddToNewCase({ canUserCreateAndReadCases, - title: insight.title, + title: attackDiscovery.title, }); const { onAddToExistingCase } = useAddToExistingCase({ canUserCreateAndReadCases, @@ -53,14 +53,14 @@ const TakeActionComponent: React.FC = ({ insight, replacements }) => { const onButtonClick = useCallback(() => setPopover(!isPopoverOpen), [isPopoverOpen]); const closePopover = useCallback(() => setPopover(false), []); - // markdown for the alert insight, which will be exported to the case, or to the assistant: + // markdown for the attack discovery, which will be exported to the case, or to the assistant: const markdown = useMemo( () => - getAlertsInsightMarkdown({ - insight, + getAttackDiscoveryMarkdown({ + attackDiscovery, replacements, }), - [insight, replacements] + [attackDiscovery, replacements] ); // click handlers for the popover actions: @@ -68,24 +68,24 @@ const TakeActionComponent: React.FC = ({ insight, replacements }) => { closePopover(); onAddToNewCase({ - alertIds: insight.alertIds, + alertIds: attackDiscovery.alertIds, markdownComments: [markdown], replacements, }); - }, [closePopover, insight.alertIds, markdown, onAddToNewCase, replacements]); + }, [closePopover, attackDiscovery.alertIds, markdown, onAddToNewCase, replacements]); const onClickAddToExistingCase = useCallback(() => { closePopover(); onAddToExistingCase({ - alertIds: insight.alertIds, + alertIds: attackDiscovery.alertIds, markdownComments: [markdown], replacements, }); - }, [closePopover, insight.alertIds, markdown, onAddToExistingCase, replacements]); + }, [closePopover, attackDiscovery.alertIds, markdown, onAddToExistingCase, replacements]); const { showAssistantOverlay, disabled: viewInAiAssistantDisabled } = useViewInAiAssistant({ - insight, + attackDiscovery, replacements, }); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/translations.ts similarity index 62% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/translations.ts index 14bd7dd3341421..3ca19eac855edf 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/take_action/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/take_action/translations.ts @@ -8,28 +8,28 @@ import { i18n } from '@kbn/i18n'; export const ADD_TO_NEW_CASE = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.takeAction.addToNewCaseButtonLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.takeAction.addToNewCaseButtonLabel', { defaultMessage: 'Add to new case', } ); export const ADD_TO_EXISTING_CASE = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.takeAction.addToExistingCaseButtonLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.takeAction.addToExistingCaseButtonLabel', { defaultMessage: 'Add to existing case', } ); export const TAKE_ACTION = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.takeAction.title', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.takeAction.title', { defaultMessage: 'Take action', } ); export const VIEW_IN_AI_ASSISTANT = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.takeAction.viewInAiAssistantButtonLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.takeAction.viewInAiAssistantButtonLabel', { defaultMessage: 'View in AI Assistant', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/translations.ts similarity index 72% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/translations.ts index 605b78711b5679..7f5bb5606ac06e 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/translations.ts @@ -8,14 +8,14 @@ import { i18n } from '@kbn/i18n'; export const ATTACK_CHAIN = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.attackChainLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.attackChainLabel', { defaultMessage: 'Attack chain:', } ); export const ALERTS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.actions.alertsLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.alertsLabel', { defaultMessage: 'Alerts:', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/index.tsx similarity index 95% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/index.tsx index 95053d4356f27a..d2418c43ef66bd 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_case/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/index.tsx @@ -83,7 +83,10 @@ export const useAddToNewCase = ({ [alertsIndexPattern, createCaseFlyout] ); - const headerContent = useMemo(() =>
{i18n.CREATE_A_CASE_FOR_INSIGHT(title)}
, [title]); + const headerContent = useMemo( + () =>
{i18n.CREATE_A_CASE_FOR_ATTACK_DISCOVERY(title)}
, + [title] + ); const onAddToNewCase = useCallback( ({ diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/translations.ts new file mode 100644 index 00000000000000..9303bf09902054 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_case/translations.ts @@ -0,0 +1,40 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ADD_TO_CASE_SUCCESS = i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.addToCaseSuccessLabel', + { + defaultMessage: 'Successfully added attack discovery to the case', + } +); + +export const ADD_TO_NEW_CASE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.addToNewCaseButtonLabel', + { + defaultMessage: 'Add to new case', + } +); + +export const CREATE_A_CASE_FOR_ATTACK_DISCOVERY = (title: string) => + i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.createACaseForAttackDiscoveryHeaderText', + { + values: { title }, + defaultMessage: 'Create a case for attack discovery {title}', + } + ); + +export const CASE_DESCRIPTION = (attackDiscoveryTitle: string) => + i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.caseDescription', + { + values: { attackDiscoveryTitle }, + defaultMessage: 'This case was opened for attack discovery: _{attackDiscoveryTitle}_', + } + ); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_existing_case/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/actions/use_add_to_existing_case/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/index.tsx diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts new file mode 100644 index 00000000000000..5c5fbcdd4f6e4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts @@ -0,0 +1,31 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ADD_TO_CASE_SUCCESS = i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.addToCaseSuccessLabel', + { + defaultMessage: 'Successfully added attack discovery to the case', + } +); + +export const ADD_TO_NEW_CASE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.addToNewCaseButtonLabel', + { + defaultMessage: 'Add to new case', + } +); + +export const CREATE_A_CASE_FOR_ATTACK_DISCOVERY = (title: string) => + i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.createACaseForAttackDiscoveryHeaderText', + { + values: { title }, + defaultMessage: 'Create a case for attack discovery {title}', + } + ); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/index.tsx similarity index 69% rename from x-pack/plugins/security_solution/public/ai_insights/insight/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/index.tsx index ebe66bae893471..daa5abd264598b 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/index.tsx @@ -14,19 +14,19 @@ import { ActionableSummary } from './actionable_summary'; import { Actions } from './actions'; import { Tabs } from './tabs'; import { Title } from './title'; -import type { AlertsInsight } from '../types'; +import type { AttackDiscovery } from '../types'; interface Props { + attackDiscovery: AttackDiscovery; initialIsOpen?: boolean; - insight: AlertsInsight; onToggle?: (newState: 'open' | 'closed') => void; replacements?: Replacements; showAnonymized?: boolean; } -const InsightComponent: React.FC = ({ +const AttackDiscoveryPanelComponent: React.FC = ({ + attackDiscovery, initialIsOpen, - insight, onToggle, replacements, showAnonymized = false, @@ -34,7 +34,7 @@ const InsightComponent: React.FC = ({ const { euiTheme } = useEuiTheme(); const htmlId = useGeneratedHtmlId({ - prefix: 'insightAccordion', + prefix: 'attackDiscoveryAccordion', }); const [isOpen, setIsOpen] = useState<'open' | 'closed'>(initialIsOpen ? 'open' : 'closed'); const updateIsOpen = useCallback(() => { @@ -45,21 +45,21 @@ const InsightComponent: React.FC = ({ }, [isOpen, onToggle]); const actions = useMemo( - () => , - [insight, replacements] + () => , + [attackDiscovery, replacements] ); const buttonContent = useMemo( - () => , - [insight.title] + () => <Title isLoading={false} title={attackDiscovery.title} />, + [attackDiscovery.title] ); return ( <> - <EuiPanel data-test-subj="insight" hasBorder={true}> + <EuiPanel data-test-subj="attackDiscovery" hasBorder={true}> <EuiAccordion buttonContent={buttonContent} - data-test-subj="insightAccordion" + data-test-subj="attackDiscoveryAccordion" extraAction={actions} forceState={isOpen} id={htmlId} @@ -71,7 +71,7 @@ const InsightComponent: React.FC<Props> = ({ <EuiSpacer size="m" /> <ActionableSummary - insight={insight} + attackDiscovery={attackDiscovery} replacements={replacements} showAnonymized={showAnonymized} /> @@ -84,16 +84,20 @@ const InsightComponent: React.FC<Props> = ({ border-radius: 0 0 6px 6px; margin: 0 ${euiTheme.size.m} 0 ${euiTheme.size.m}; `} - data-test-subj="insightTabsPanel" + data-test-subj="attackDiscoveryTabsPanel" hasBorder={true} > - <Tabs insight={insight} replacements={replacements} showAnonymized={showAnonymized} /> + <Tabs + attackDiscovery={attackDiscovery} + replacements={replacements} + showAnonymized={showAnonymized} + /> </EuiPanel> )} </> ); }; -InsightComponent.displayName = 'Insight'; +AttackDiscoveryPanelComponent.displayName = 'AttackDiscoveryPanel'; -export const Insight = React.memo(InsightComponent); +export const AttackDiscoveryPanel = React.memo(AttackDiscoveryPanelComponent); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/interval/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/interval/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/interval/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/interval/helpers.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/alerts/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/alerts_tab/index.tsx similarity index 72% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/alerts/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/alerts_tab/index.tsx index 169192848d4ccd..d7caf0a7528c93 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/alerts/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/alerts_tab/index.tsx @@ -11,22 +11,22 @@ import React, { useMemo } from 'react'; import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../../common/constants'; import { useKibana } from '../../../../common/lib/kibana'; -import type { AlertsInsight } from '../../../types'; +import type { AttackDiscovery } from '../../../types'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; } -const AlertsComponent: React.FC<Props> = ({ insight, replacements }) => { +const AlertsTabComponent: React.FC<Props> = ({ attackDiscovery, replacements }) => { const { triggersActionsUi } = useKibana().services; const originalAlertIds = useMemo( () => - insight.alertIds.map((alertId) => + attackDiscovery.alertIds.map((alertId) => replacements != null ? replacements[alertId] ?? alertId : alertId ), - [insight.alertIds, replacements] + [attackDiscovery.alertIds, replacements] ); const alertIdsQuery = useMemo( @@ -44,12 +44,17 @@ const AlertsComponent: React.FC<Props> = ({ insight, replacements }) => { () => ({ alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry, configurationId: configId, - id: `ai-insights-alerts-${insight.id}`, + id: `attack-discovery-alerts-${attackDiscovery.id}`, featureIds: [AlertConsumers.SIEM], query: alertIdsQuery, showAlertStatusWithFlapping: false, }), - [triggersActionsUi.alertsTableConfigurationRegistry, configId, insight.id, alertIdsQuery] + [ + alertIdsQuery, + attackDiscovery.id, + configId, + triggersActionsUi.alertsTableConfigurationRegistry, + ] ); return ( @@ -57,4 +62,6 @@ const AlertsComponent: React.FC<Props> = ({ insight, replacements }) => { ); }; -export const Alerts = React.memo(AlertsComponent); +AlertsTabComponent.displayName = 'AlertsTab'; + +export const AlertsTab = React.memo(AlertsTabComponent); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx similarity index 78% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx index 9155024acabfff..e80be849de08e3 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/index.tsx @@ -14,24 +14,24 @@ import { AttackChain } from '../../../attack/attack_chain'; import { InvestigateInTimelineButton } from '../../../../common/components/event_details/table/investigate_in_timeline_button'; import { buildAlertsKqlFilter } from '../../../../detections/components/alerts_table/actions'; import { getTacticMetadata } from '../../../helpers'; -import { InsightMarkdownFormatter } from '../../../insight_markdown_formatter'; +import { AttackDiscoveryMarkdownFormatter } from '../../../attack_discovery_markdown_formatter'; import * as i18n from './translations'; -import type { AlertsInsight } from '../../../types'; +import type { AttackDiscovery } from '../../../types'; import { ViewInAiAssistant } from '../../view_in_ai_assistant'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; showAnonymized?: boolean; } -const AiInsightsComponent: React.FC<Props> = ({ - insight, +const AttackDiscoveryTabComponent: React.FC<Props> = ({ + attackDiscovery, replacements, showAnonymized = false, }) => { const { euiTheme } = useEuiTheme(); - const { detailsMarkdown, summaryMarkdown } = useMemo(() => insight, [insight]); + const { detailsMarkdown, summaryMarkdown } = useMemo(() => attackDiscovery, [attackDiscovery]); const summaryMarkdownWithReplacements = useMemo( () => @@ -51,22 +51,22 @@ const AiInsightsComponent: React.FC<Props> = ({ [detailsMarkdown, replacements] ); - const tacticMetadata = useMemo(() => getTacticMetadata(insight), [insight]); + const tacticMetadata = useMemo(() => getTacticMetadata(attackDiscovery), [attackDiscovery]); const originalAlertIds = useMemo( - () => insight.alertIds.map((id) => replacements?.[id] ?? id), - [insight.alertIds, replacements] + () => attackDiscovery.alertIds.map((id) => replacements?.[id] ?? id), + [attackDiscovery.alertIds, replacements] ); const filters = useMemo(() => buildAlertsKqlFilter('_id', originalAlertIds), [originalAlertIds]); return ( - <div data-test-subj="aiInsightsTab"> + <div data-test-subj="attackDiscoveryTab"> <EuiTitle data-test-subj="summaryTitle" size="xs"> <h2>{i18n.SUMMARY}</h2> </EuiTitle> <EuiSpacer size="s" /> - <InsightMarkdownFormatter + <AttackDiscoveryMarkdownFormatter disableActions={showAnonymized} markdown={showAnonymized ? summaryMarkdown : summaryMarkdownWithReplacements} /> @@ -77,7 +77,7 @@ const AiInsightsComponent: React.FC<Props> = ({ <h2>{i18n.DETAILS}</h2> </EuiTitle> <EuiSpacer size="s" /> - <InsightMarkdownFormatter + <AttackDiscoveryMarkdownFormatter disableActions={showAnonymized} markdown={showAnonymized ? detailsMarkdown : detailsMarkdownWithReplacements} /> @@ -90,14 +90,14 @@ const AiInsightsComponent: React.FC<Props> = ({ <h2>{i18n.ATTACK_CHAIN}</h2> </EuiTitle> <EuiSpacer size="s" /> - <AttackChain insight={insight} /> + <AttackChain attackDiscovery={attackDiscovery} /> <EuiSpacer size="l" /> </> )} <EuiFlexGroup alignItems="center" gutterSize="none"> <EuiFlexItem grow={false}> - <ViewInAiAssistant insight={insight} /> + <ViewInAiAssistant attackDiscovery={attackDiscovery} replacements={replacements} /> </EuiFlexItem> <EuiFlexItem css={css` @@ -128,6 +128,6 @@ const AiInsightsComponent: React.FC<Props> = ({ ); }; -AiInsightsComponent.displayName = 'AiInsights'; +AttackDiscoveryTabComponent.displayName = 'AttackDiscoveryTab'; -export const AiInsights = React.memo(AiInsightsComponent); +export const AttackDiscoveryTab = React.memo(AttackDiscoveryTabComponent); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/translations.ts similarity index 55% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/translations.ts index f3ab02bf262ad3..a83495b2fcc62f 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/ai_insights/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/attack_discovery_tab/translations.ts @@ -8,35 +8,28 @@ import { i18n } from '@kbn/i18n'; export const ATTACK_CHAIN = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsights.attackChainLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.attackDiscovery.attackChainLabel', { defaultMessage: 'Attack Chain', } ); -export const ALERTS_FROM_INSIGHT = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsights.alertsFromInsightQueryTitle', - { - defaultMessage: 'Alerts from insight', - } -); - export const DETAILS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsights.detailsTitle', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.attackDiscovery.detailsTitle', { defaultMessage: 'Details', } ); export const INVESTIGATE_IN_TIMELINE = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsights.investigateInTimelineButtonLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.attackDiscovery.investigateInTimelineButtonLabel', { defaultMessage: 'Investigate in Timeline', } ); export const SUMMARY = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsights.summaryTitle', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.attackDiscovery.summaryTitle', { defaultMessage: 'Summary', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/get_tabs.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/get_tabs.tsx similarity index 62% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/get_tabs.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/get_tabs.tsx index 64808adc1d6292..8f74a52bdb6507 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/get_tabs.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/get_tabs.tsx @@ -9,10 +9,10 @@ import { EuiSpacer } from '@elastic/eui'; import type { Replacements } from '@kbn/elastic-assistant-common'; import React from 'react'; -import { AiInsights } from './ai_insights'; -import { Alerts } from './alerts'; +import { AttackDiscoveryTab } from './attack_discovery_tab'; +import { AlertsTab } from './alerts_tab'; import * as i18n from './translations'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; interface TabInfo { content: JSX.Element; @@ -21,21 +21,25 @@ interface TabInfo { } export const getTabs = ({ - insight, + attackDiscovery, replacements, showAnonymized = false, }: { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; showAnonymized?: boolean; }): TabInfo[] => [ { - id: 'aiInsights--id', - name: i18n.AI_INSIGHTS, + id: 'attackDiscovery--id', + name: i18n.ATTACK_DISCOVERY, content: ( <> <EuiSpacer /> - <AiInsights insight={insight} replacements={replacements} showAnonymized={showAnonymized} /> + <AttackDiscoveryTab + attackDiscovery={attackDiscovery} + replacements={replacements} + showAnonymized={showAnonymized} + /> </> ), }, @@ -45,7 +49,7 @@ export const getTabs = ({ content: ( <> <EuiSpacer /> - <Alerts insight={insight} replacements={replacements} /> + <AlertsTab attackDiscovery={attackDiscovery} replacements={replacements} /> </> ), }, diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/index.tsx similarity index 80% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/index.tsx index 469af3c40d3e17..a11d63acb83bb4 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/index.tsx @@ -10,18 +10,22 @@ import { EuiTabs, EuiTab } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import { getTabs } from './get_tabs'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; showAnonymized?: boolean; } -const TabsComponent: React.FC<Props> = ({ insight, replacements, showAnonymized = false }) => { +const TabsComponent: React.FC<Props> = ({ + attackDiscovery, + replacements, + showAnonymized = false, +}) => { const tabs = useMemo( - () => getTabs({ insight, replacements, showAnonymized }), - [insight, replacements, showAnonymized] + () => getTabs({ attackDiscovery, replacements, showAnonymized }), + [attackDiscovery, replacements, showAnonymized] ); const [selectedTabId, setSelectedTabId] = useState(tabs[0].id); diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/translations.ts similarity index 58% rename from x-pack/plugins/security_solution/public/ai_insights/insight/tabs/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/translations.ts index 28846d3f0db9e9..ef873da2fd17ee 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/tabs/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/tabs/translations.ts @@ -7,15 +7,15 @@ import { i18n } from '@kbn/i18n'; -export const AI_INSIGHTS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.aiInsightsTabLabel', +export const ATTACK_DISCOVERY = i18n.translate( + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.attackDiscoveryTabLabel', { - defaultMessage: 'AI Insights', + defaultMessage: 'Attack discovery', } ); export const ALERTS = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.tabs.alertsTabLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.tabs.alertsTabLabel', { defaultMessage: 'Alerts', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/title/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/title/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/insight/title/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/title/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/index.tsx similarity index 91% rename from x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/index.tsx index dbbe3c7e01c890..b3ad4590f0363c 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/index.tsx @@ -11,21 +11,24 @@ import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/e import React from 'react'; import * as i18n from './translations'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; import { useViewInAiAssistant } from './use_view_in_ai_assistant'; interface Props { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; compact?: boolean; replacements?: Replacements; } const ViewInAiAssistantComponent: React.FC<Props> = ({ + attackDiscovery, compact = false, - insight, replacements, }) => { - const { showAssistantOverlay, disabled } = useViewInAiAssistant({ insight, replacements }); + const { showAssistantOverlay, disabled } = useViewInAiAssistant({ + attackDiscovery, + replacements, + }); return compact ? ( <EuiButtonEmpty diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/translations.ts similarity index 77% rename from x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/translations.ts index 92d95f659ba9b8..8adbaf682dec6c 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/translations.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; export const VIEW_IN_AI_ASSISTANT = i18n.translate( - 'xpack.securitySolution.aiInsights.insight.viewInAiAssistant.viewInAiAssistantButtonLabel', + 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.viewInAiAssistant.viewInAiAssistantButtonLabel', { defaultMessage: 'View in AI Assistant', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/use_view_in_ai_assistant.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts similarity index 78% rename from x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/use_view_in_ai_assistant.ts rename to x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts index 5f30dd24a20719..228f8828637779 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/insight/view_in_ai_assistant/use_view_in_ai_assistant.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts @@ -9,8 +9,8 @@ import { useMemo, useCallback } from 'react'; import { useAssistantOverlay } from '@kbn/elastic-assistant'; import type { Replacements } from '@kbn/elastic-assistant-common'; import { useAssistantAvailability } from '../../../assistant/use_assistant_availability'; -import { getAlertsInsightMarkdown } from '../../get_alerts_insight_markdown/get_alerts_insight_markdown'; -import type { AlertsInsight } from '../../types'; +import { getAttackDiscoveryMarkdown } from '../../get_attack_discovery_markdown/get_attack_discovery_markdown'; +import type { AttackDiscovery } from '../../types'; const useAssistantNoop = () => ({ promptContextId: undefined, showAssistantOverlay: () => {} }); @@ -19,10 +19,10 @@ const useAssistantNoop = () => ({ promptContextId: undefined, showAssistantOverl */ const category = 'insight'; export const useViewInAiAssistant = ({ - insight, + attackDiscovery, replacements, }: { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; }) => { const { hasAssistantPrivilege } = useAssistantAvailability(); @@ -35,18 +35,18 @@ export const useViewInAiAssistant = ({ // the prompt context for this insight: const getPromptContext = useCallback( async () => - getAlertsInsightMarkdown({ - insight, + getAttackDiscoveryMarkdown({ + attackDiscovery, // note: we do NOT want to replace the replacements here }), - [insight] + [attackDiscovery] ); const { promptContextId, showAssistantOverlay: showOverlay } = useAssistantHook( category, - insight.title, // conversation title - insight.title, // description used in context pill + attackDiscovery.title, // conversation title + attackDiscovery.title, // description used in context pill getPromptContext, - insight.id, // accept the UUID default for this prompt context + attackDiscovery.id, // accept the UUID default for this prompt context null, // suggestedUserPrompt null, // tooltip replacements ?? null diff --git a/x-pack/plugins/security_solution/public/ai_insights/get_alerts_insight_markdown/get_alerts_insight_markdown.ts b/x-pack/plugins/security_solution/public/attack_discovery/get_attack_discovery_markdown/get_attack_discovery_markdown.ts similarity index 68% rename from x-pack/plugins/security_solution/public/ai_insights/get_alerts_insight_markdown/get_alerts_insight_markdown.ts rename to x-pack/plugins/security_solution/public/attack_discovery/get_attack_discovery_markdown/get_attack_discovery_markdown.ts index 1ab375208281e5..e79470f670d8d2 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/get_alerts_insight_markdown/get_alerts_insight_markdown.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/get_attack_discovery_markdown/get_attack_discovery_markdown.ts @@ -8,7 +8,7 @@ import type { Replacements } from '@kbn/elastic-assistant-common'; import { getTacticLabel, getTacticMetadata } from '../helpers'; -import type { AlertsInsight } from '../types'; +import type { AttackDiscovery } from '../types'; export const getMarkdownFields = (markdown: string): string => { const regex = new RegExp('{{\\s*(\\S+)\\s+(\\S+)\\s*}}', 'gm'); @@ -16,8 +16,8 @@ export const getMarkdownFields = (markdown: string): string => { return markdown.replace(regex, (_, field, value) => `\`${value}\``); }; -export const getAttackChainMarkdown = (insight: AlertsInsight): string => { - const tacticMetadata = getTacticMetadata(insight).filter((tactic) => tactic.detected); +export const getAttackChainMarkdown = (attackDiscovery: AttackDiscovery): string => { + const tacticMetadata = getTacticMetadata(attackDiscovery).filter((tactic) => tactic.detected); if (tacticMetadata.length === 0) { return ''; @@ -49,17 +49,17 @@ export const getMarkdownWithOriginalValues = ({ ); }; -export const getAlertsInsightMarkdown = ({ - insight, +export const getAttackDiscoveryMarkdown = ({ + attackDiscovery, replacements, }: { - insight: AlertsInsight; + attackDiscovery: AttackDiscovery; replacements?: Replacements; }): string => { - const title = getMarkdownFields(insight.title); - const entitySummaryMarkdown = getMarkdownFields(insight.entitySummaryMarkdown); - const summaryMarkdown = getMarkdownFields(insight.summaryMarkdown); - const detailsMarkdown = getMarkdownFields(insight.detailsMarkdown); + const title = getMarkdownFields(attackDiscovery.title); + const entitySummaryMarkdown = getMarkdownFields(attackDiscovery.entitySummaryMarkdown); + const summaryMarkdown = getMarkdownFields(attackDiscovery.summaryMarkdown); + const detailsMarkdown = getMarkdownFields(attackDiscovery.detailsMarkdown); const markdown = `## ${title} @@ -71,7 +71,7 @@ ${summaryMarkdown} ### Details ${detailsMarkdown} -${getAttackChainMarkdown(insight)} +${getAttackChainMarkdown(attackDiscovery)} `; if (replacements != null) { return getMarkdownWithOriginalValues({ markdown, replacements }); diff --git a/x-pack/plugins/security_solution/public/ai_insights/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/helpers.ts similarity index 89% rename from x-pack/plugins/security_solution/public/ai_insights/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/helpers.ts index 579f36f581c093..2cdd1354c9b6d9 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/helpers.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/helpers.ts @@ -6,7 +6,7 @@ */ import * as i18n from './translations'; -import type { AlertsInsight } from './types'; +import type { AttackDiscovery } from './types'; export const RECONNAISSANCE = 'Reconnaissance'; export const INITIAL_ACCESS = 'Initial Access'; @@ -62,12 +62,12 @@ interface TacticMetadata { name: string; } -export const getTacticMetadata = (insight: AlertsInsight): TacticMetadata[] => +export const getTacticMetadata = (attackDiscovery: AttackDiscovery): TacticMetadata[] => MITRE_ATTACK_TACTICS_SUBSET.map((tactic, i) => ({ detected: - insight.mitreAttackTactics === undefined + attackDiscovery.mitreAttackTactics === undefined ? false - : insight.mitreAttackTactics.includes(tactic), + : attackDiscovery.mitreAttackTactics.includes(tactic), name: getTacticLabel(tactic), index: i, })); diff --git a/x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.test.tsx b/x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.test.tsx similarity index 56% rename from x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.test.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.test.tsx index dd1592ec0f8c33..423c30bddf2078 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/hooks/use_insights_telemetry/index.test.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.test.tsx @@ -6,13 +6,13 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { useInsightsTelemetry } from '.'; +import { useAttackDiscoveryTelemetry } from '.'; import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock'; -const reportInsightsGenerated = jest.fn(); +const reportAttackDiscoveriesGenerated = jest.fn(); const mockedTelemetry = { ...createTelemetryServiceMock(), - reportInsightsGenerated, + reportAttackDiscoveriesGenerated, }; jest.mock('../../../common/lib/kibana', () => { @@ -28,19 +28,22 @@ jest.mock('../../../common/lib/kibana', () => { }; }); -describe('useInsightsTelemetry', () => { +describe('useAttackDiscoveryTelemetry', () => { beforeEach(() => { jest.clearAllMocks(); }); it('should return the expected telemetry object with tracking functions', () => { - const { result } = renderHook(() => useInsightsTelemetry()); - expect(result.current).toHaveProperty('reportInsightsGenerated'); + const { result } = renderHook(() => useAttackDiscoveryTelemetry()); + expect(result.current).toHaveProperty('reportAttackDiscoveriesGenerated'); }); - it('Should call reportInsightsGenerated with appropriate actionTypeId when tracking is called', async () => { - const { result } = renderHook(() => useInsightsTelemetry()); - await result.current.reportInsightsGenerated({ actionTypeId: '.gen-ai', model: 'gpt-4' }); - expect(reportInsightsGenerated).toHaveBeenCalledWith({ + it('Should call reportAttackDiscoveriesGenerated with appropriate actionTypeId when tracking is called', async () => { + const { result } = renderHook(() => useAttackDiscoveryTelemetry()); + await result.current.reportAttackDiscoveriesGenerated({ + actionTypeId: '.gen-ai', + model: 'gpt-4', + }); + expect(reportAttackDiscoveriesGenerated).toHaveBeenCalledWith({ actionTypeId: '.gen-ai', model: 'gpt-4', }); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.tsx new file mode 100644 index 00000000000000..a0cc331e88017b --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/hooks/use_attack_discovery_telemetry/index.tsx @@ -0,0 +1,23 @@ +/* + * 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 { ReportAttackDiscoveriesGeneratedParams } from '../../../common/lib/telemetry/events/attack_discovery/types'; +import { useKibana } from '../../../common/lib/kibana'; + +interface AttackDiscoveryTelemetry { + reportAttackDiscoveriesGenerated: (params: ReportAttackDiscoveriesGeneratedParams) => void; +} + +export const useAttackDiscoveryTelemetry = (): AttackDiscoveryTelemetry => { + const { + services: { telemetry }, + } = useKibana(); + + return { + reportAttackDiscoveriesGenerated: telemetry.reportAttackDiscoveriesGenerated, + }; +}; diff --git a/x-pack/plugins/security_solution/public/ai_insights/index.ts b/x-pack/plugins/security_solution/public/attack_discovery/index.ts similarity index 94% rename from x-pack/plugins/security_solution/public/ai_insights/index.ts rename to x-pack/plugins/security_solution/public/attack_discovery/index.ts index e9e14376b12bbd..fa7667f60dcfec 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/index.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/index.ts @@ -8,7 +8,7 @@ import type { SecuritySubPlugin } from '../app/types'; import { routes } from './routes'; -export class AiInsights { +export class AttackDiscovery { public setup() {} public start(isEnabled = false): SecuritySubPlugin { diff --git a/x-pack/plugins/security_solution/public/ai_insights/jest.config.js b/x-pack/plugins/security_solution/public/attack_discovery/jest.config.js similarity index 72% rename from x-pack/plugins/security_solution/public/ai_insights/jest.config.js rename to x-pack/plugins/security_solution/public/attack_discovery/jest.config.js index c633ac8ac91e95..bfec7737e42115 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/jest.config.js +++ b/x-pack/plugins/security_solution/public/attack_discovery/jest.config.js @@ -8,12 +8,12 @@ module.exports = { preset: '@kbn/test', rootDir: '../../../../..', - roots: ['<rootDir>/x-pack/plugins/security_solution/public/ai_insights'], + roots: ['<rootDir>/x-pack/plugins/security_solution/public/attack_discovery'], coverageDirectory: - '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/ai_insights', + '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/attack_discovery', coverageReporters: ['text', 'html'], collectCoverageFrom: [ - '<rootDir>/x-pack/plugins/security_solution/public/ai_insights/**/*.{ts,tsx}', + '<rootDir>/x-pack/plugins/security_solution/public/attack_discovery/**/*.{ts,tsx}', ], moduleNameMapper: require('../../server/__mocks__/module_name_map'), }; diff --git a/x-pack/plugins/security_solution/public/ai_insights/links.ts b/x-pack/plugins/security_solution/public/attack_discovery/links.ts similarity index 54% rename from x-pack/plugins/security_solution/public/ai_insights/links.ts rename to x-pack/plugins/security_solution/public/attack_discovery/links.ts index 0f7435656a00ed..e53fdc8fef0538 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/links.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/links.ts @@ -7,20 +7,20 @@ import { i18n } from '@kbn/i18n'; -import { AI_INSIGHTS } from '../app/translations'; -import { SecurityPageName, SERVER_APP_ID, AI_INSIGHTS_PATH } from '../../common/constants'; +import { ATTACK_DISCOVERY } from '../app/translations'; +import { ATTACK_DISCOVERY_PATH, SecurityPageName, SERVER_APP_ID } from '../../common/constants'; import type { LinkItem } from '../common/links/types'; export const links: LinkItem = { capabilities: [`${SERVER_APP_ID}.show`], - experimentalKey: 'assistantAlertsInsights', + experimentalKey: 'attackDiscoveryEnabled', globalNavPosition: 4, globalSearchKeywords: [ - i18n.translate('xpack.securitySolution.appLinks.aiInsights', { - defaultMessage: 'AI Insights', + i18n.translate('xpack.securitySolution.appLinks.attackDiscovery', { + defaultMessage: 'Attack discovery', }), ], - id: SecurityPageName.aiInsights, - path: AI_INSIGHTS_PATH, - title: AI_INSIGHTS, + id: SecurityPageName.attackDiscovery, + path: ATTACK_DISCOVERY_PATH, + title: ATTACK_DISCOVERY, }; diff --git a/x-pack/plugins/security_solution/public/ai_insights/mock/mock_find_anonymization_fields_response.ts b/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_find_anonymization_fields_response.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/mock/mock_find_anonymization_fields_response.ts rename to x-pack/plugins/security_solution/public/attack_discovery/mock/mock_find_anonymization_fields_response.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/mock/mock_use_insights.ts b/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts similarity index 96% rename from x-pack/plugins/security_solution/public/ai_insights/mock/mock_use_insights.ts rename to x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts index be49853c3b3bc3..639f66497583e7 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/mock/mock_use_insights.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts @@ -5,30 +5,16 @@ * 2.0. */ -import type { Replacements } from '@kbn/elastic-assistant-common'; +import type { UseAttackDiscovery } from '../use_attack_discovery'; -import type { CachedInsights } from '../pages/session_storage'; -import type { AlertsInsight, GenerationInterval } from '../types'; - -interface MockUseInsightsResults { - approximateFutureTime: Date | null; - cachedInsights: Record<string, CachedInsights>; - fetchInsights: () => Promise<void>; - generationIntervals: Record<string, GenerationInterval[]> | undefined; - insights: AlertsInsight[]; - isLoading: boolean; - lastUpdated: Date | null; - replacements: Replacements; -} - -export const getMockUseInsightsWithCachedInsights = ( - fetchInsights: () => Promise<void> -): MockUseInsightsResults => ({ +export const getMockUseAttackDiscoveriesWithCachedAttackDiscoveries = ( + fetchAttackDiscoveries: () => Promise<void> +): UseAttackDiscovery => ({ approximateFutureTime: null, - cachedInsights: { + cachedAttackDiscoveries: { claudeV3SonnetUsEast1: { connectorId: 'claudeV3SonnetUsEast1', - insights: [ + attackDiscoveries: [ { alertIds: [ 'e770a817-0e87-4e4b-8e26-1bf504a209d2', @@ -170,7 +156,7 @@ export const getMockUseInsightsWithCachedInsights = ( }, claudeV3SonnetUsWest2: { connectorId: 'claudeV3SonnetUsWest2', - insights: [ + attackDiscoveries: [ { alertIds: [ 'e6b49cac-a5d0-4d22-a7e2-868881aa9d20', @@ -381,8 +367,8 @@ export const getMockUseInsightsWithCachedInsights = ( }, ], }, - fetchInsights, - insights: [ + fetchAttackDiscoveries, + attackDiscoveries: [ { alertIds: [ 'e770a817-0e87-4e4b-8e26-1bf504a209d2', @@ -518,28 +504,28 @@ export const getMockUseInsightsWithCachedInsights = ( isLoading: false, }); -export const getMockUseInsightsWithNoInsights = ( - fetchInsights: () => Promise<void> -): MockUseInsightsResults => ({ +export const getMockUseAttackDiscoveriesWithNoAttackDiscoveries = ( + fetchAttackDiscoveries: () => Promise<void> +): UseAttackDiscovery => ({ approximateFutureTime: null, - cachedInsights: {}, - fetchInsights, + cachedAttackDiscoveries: {}, + fetchAttackDiscoveries, generationIntervals: undefined, - insights: [], + attackDiscoveries: [], lastUpdated: null, replacements: {}, isLoading: false, }); -export const getMockUseInsightsWithNoInsightsLoading = ( - fetchInsights: () => Promise<void> -): MockUseInsightsResults => ({ +export const getMockUseAttackDiscoveriesWithNoAttackDiscoveriesLoading = ( + fetchAttackDiscoveries: () => Promise<void> +): UseAttackDiscovery => ({ approximateFutureTime: new Date('2024-04-15T17:13:29.470Z'), // <-- estimated generation completion time - cachedInsights: {}, - fetchInsights, + cachedAttackDiscoveries: {}, + fetchAttackDiscoveries, generationIntervals: undefined, - insights: [], + attackDiscoveries: [], lastUpdated: null, replacements: {}, - isLoading: true, // <-- insights are being generated + isLoading: true, // <-- attack discoveries are being generated }); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/animated_counter/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/animated_counter/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/animated_counter/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/animated_counter/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/index.tsx similarity index 90% rename from x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/index.tsx index e488c4d0fd896b..d5e8c8cb2bf2e1 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/empty_prompt/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/index.tsx @@ -74,14 +74,8 @@ const EmptyPromptComponent: React.FC<Props> = ({ gutterSize="none" > <EuiFlexItem grow={false}> - <EuiText color="subdued" data-test-subj="basedOnSelectedFiltersLabel"> - {i18n.BASED_ON_SELECTED_FILTERS} - </EuiText> - </EuiFlexItem> - - <EuiFlexItem grow={false}> - <EuiText color="subdued" data-test-subj="startGeneratingInsightsLabel"> - {i18n.START_GENERATING_INSIGHTS} + <EuiText color="subdued" data-test-subj="startGeneratingDiscoveriesLabel"> + {i18n.START_GENERATING_DISCOVERIES} </EuiText> </EuiFlexItem> </EuiFlexGroup> @@ -121,7 +115,11 @@ const EmptyPromptComponent: React.FC<Props> = ({ </EuiFlexItem> <EuiFlexItem grow={false}> - <EuiLink data-test-subj="learnMore" href="#" target="_blank"> + <EuiLink + data-test-subj="learnMore" + href="https://www.elastic.co/guide/en/security/master/attack-discovery.html" + target="_blank" + > {i18n.LEARN_MORE} </EuiLink> </EuiFlexItem> diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/translations.ts new file mode 100644 index 00000000000000..d71b47c3f099d2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/empty_prompt/translations.ts @@ -0,0 +1,45 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ALERTS_WILL_BE_ANALYZED = (alertsCount: number) => + i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.emptyPrompt.alertsWillBeAnalyzedTitle', + { + defaultMessage: '{alertsCount, plural, one {alert} other {alerts}} will be analyzed', + values: { alertsCount }, + } + ); + +export const GENERATE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.emptyPrompt.generateLabel', + { + defaultMessage: 'Generate', + } +); + +export const LEARN_MORE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.emptyPrompt.learnMoreLabel', + { + defaultMessage: 'Learn more', + } +); + +export const SELECT_A_CONNECTOR = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.emptyPrompt.selectAConnectorLabel', + { + defaultMessage: 'Select a connector', + } +); + +export const START_GENERATING_DISCOVERIES = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.emptyPrompt.startGeneratingDiscoveriesLabel', + { + defaultMessage: 'Start generating discoveries via Elastic AI Assistant.', + } +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/header/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/header/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/header/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/header/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/translations.ts similarity index 72% rename from x-pack/plugins/security_solution/public/ai_insights/pages/header/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/header/translations.ts index 8744286cfd3ca1..f155dda9e234f7 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/header/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/translations.ts @@ -8,21 +8,21 @@ import { i18n } from '@kbn/i18n'; export const GENERATE = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.header.generateButton', + 'xpack.securitySolution.attackDiscovery.pages.header.generateButton', { defaultMessage: 'Generate', } ); export const LOADING = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.header.loadingButton', + 'xpack.securitySolution.attackDiscovery.pages.header.loadingButton', { defaultMessage: 'Loading...', } ); export const SELECT_A_CONNECTOR = i18n.translate( - 'xpack.securitySolution.aiInsights.pages.header.selectAConnector', + 'xpack.securitySolution.attackDiscovery.pages.header.selectAConnector', { defaultMessage: 'Select a connector', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts similarity index 85% rename from x-pack/plugins/security_solution/public/ai_insights/pages/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts index f62fb348c668cb..930fb372ca8db1 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/helpers.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts @@ -50,7 +50,7 @@ export function isErrorWithStructuredMessage(error: any): error is ErrorWithStru export const CONNECTOR_ID_LOCAL_STORAGE_KEY = 'connectorId'; -export const CACHED_INSIGHTS_SESSION_STORAGE_KEY = 'cachedInsights'; +export const CACHED_ATTACK_DISCOVERIES_SESSION_STORAGE_KEY = 'cachedAttackDiscoveries'; export const GENERATION_INTERVALS_LOCAL_STORAGE_KEY = 'generationIntervals'; @@ -74,31 +74,31 @@ export const getErrorToastText = ( }; export const showEmptyPrompt = ({ - insightsCount, + attackDiscoveriesCount, isLoading, }: { - insightsCount: number; + attackDiscoveriesCount: number; isLoading: boolean; -}): boolean => !isLoading && insightsCount === 0; +}): boolean => !isLoading && attackDiscoveriesCount === 0; export const showLoading = ({ connectorId, - insightsCount, + attackDiscoveriesCount, isLoading, loadingConnectorId, }: { connectorId: string | undefined; - insightsCount: number; + attackDiscoveriesCount: number; isLoading: boolean; loadingConnectorId: string | null; -}): boolean => isLoading && (loadingConnectorId === connectorId || insightsCount === 0); +}): boolean => isLoading && (loadingConnectorId === connectorId || attackDiscoveriesCount === 0); export const showSummary = ({ connectorId, - insightsCount, + attackDiscoveriesCount, loadingConnectorId, }: { connectorId: string | undefined; - insightsCount: number; + attackDiscoveriesCount: number; loadingConnectorId: string | null; -}): boolean => loadingConnectorId !== connectorId && insightsCount > 0; +}): boolean => loadingConnectorId !== connectorId && attackDiscoveriesCount > 0; diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/index.test.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/index.test.tsx similarity index 79% rename from x-pack/plugins/security_solution/public/ai_insights/pages/index.test.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/index.test.tsx index 9ed35daab79fad..b41b6f7f947aac 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/index.test.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/index.test.tsx @@ -17,18 +17,18 @@ import React from 'react'; import { TestProviders } from '../../common/mock'; import { MockAssistantProvider } from '../../common/mock/mock_assistant_provider'; -import { AI_INSIGHTS_PATH } from '../../../common/constants'; +import { ATTACK_DISCOVERY_PATH } from '../../../common/constants'; import { mockHistory } from '../../common/utils/route/mocks'; -import { AiInsights } from '.'; +import { AttackDiscoveryPage } from '.'; import { mockTimelines } from '../../common/mock/mock_timelines_plugin'; import { UpsellingProvider } from '../../common/components/upselling_provider'; import { mockFindAnonymizationFieldsResponse } from '../mock/mock_find_anonymization_fields_response'; import { - getMockUseInsightsWithCachedInsights, - getMockUseInsightsWithNoInsightsLoading, -} from '../mock/mock_use_insights'; -import { AI_INSIGHTS_PAGE_TITLE } from './page_title/translations'; -import { useInsights } from '../use_insights'; + getMockUseAttackDiscoveriesWithCachedAttackDiscoveries, + getMockUseAttackDiscoveriesWithNoAttackDiscoveriesLoading, +} from '../mock/mock_use_attack_discovery'; +import { ATTACK_DISCOVERY_PAGE_TITLE } from './page_title/translations'; +import { useAttackDiscovery } from '../use_attack_discovery'; jest.mock('react-use', () => { const actual = jest.requireActual('react-use'); @@ -50,25 +50,25 @@ jest.mock( jest.mock('../../common/links', () => ({ useLinkInfo: jest.fn().mockReturnValue({ capabilities: ['siem.show'], - experimentalKey: 'assistantAlertsInsights', + experimentalKey: 'attackDiscoveryEnabled', globalNavPosition: 4, - globalSearchKeywords: ['AI Insights'], - id: 'ai_insights', - path: '/ai_insights', - title: 'AI Insights', + globalSearchKeywords: ['Attack discovery'], + id: 'attack_discovery', + path: '/attack_discovery', + title: 'Attack discovery', }), })); -jest.mock('../use_insights', () => ({ - useInsights: jest.fn().mockReturnValue({ +jest.mock('../use_attack_discovery', () => ({ + useAttackDiscovery: jest.fn().mockReturnValue({ approximateFutureTime: null, - cachedInsights: {}, - fetchInsights: jest.fn(), + attackDiscoveries: [], + cachedAttackDiscoveries: {}, + fetchAttackDiscoveries: jest.fn(), generationIntervals: undefined, - insights: [], + isLoading: false, lastUpdated: null, replacements: {}, - isLoading: false, }), })); @@ -175,13 +175,13 @@ const historyMock = { ...mockHistory, location: { hash: '', - pathname: AI_INSIGHTS_PATH, + pathname: ATTACK_DISCOVERY_PATH, search: '', state: '', }, }; -describe('AiInsights', () => { +describe('AttackDiscovery', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -192,7 +192,7 @@ describe('AiInsights', () => { <TestProviders> <Router history={historyMock}> <UpsellingProvider upsellingService={mockUpselling}> - <AiInsights /> + <AttackDiscoveryPage /> </UpsellingProvider> </Router> </TestProviders> @@ -200,7 +200,9 @@ describe('AiInsights', () => { }); it('renders the expected page title', () => { - expect(screen.getByTestId('aiInsightsPageTitle')).toHaveTextContent(AI_INSIGHTS_PAGE_TITLE); + expect(screen.getByTestId('attackDiscoveryPageTitle')).toHaveTextContent( + ATTACK_DISCOVERY_PAGE_TITLE + ); }); it('renders the header', () => { @@ -208,13 +210,13 @@ describe('AiInsights', () => { }); }); - describe('when there are no insights', () => { + describe('when there are no attack discoveries', () => { beforeEach(() => { render( <TestProviders> <Router history={historyMock}> <UpsellingProvider upsellingService={mockUpselling}> - <AiInsights /> + <AttackDiscoveryPage /> </UpsellingProvider> </Router> </TestProviders> @@ -233,8 +235,8 @@ describe('AiInsights', () => { expect(screen.getByTestId('emptyPrompt')).toBeInTheDocument(); }); - it('does NOT render insights', () => { - expect(screen.queryAllByTestId('insight')).toHaveLength(0); + it('does NOT render attack discoveries', () => { + expect(screen.queryAllByTestId('attackDiscovery')).toHaveLength(0); }); it('does NOT render the upgrade call to action', () => { @@ -242,18 +244,20 @@ describe('AiInsights', () => { }); }); - describe('when there are insights', () => { - const mockUseInsightsResults = getMockUseInsightsWithCachedInsights(jest.fn()); - const { insights } = mockUseInsightsResults; + describe('when there are attack discoveries', () => { + const mockUseAttackDiscoveriesResults = getMockUseAttackDiscoveriesWithCachedAttackDiscoveries( + jest.fn() + ); + const { attackDiscoveries } = mockUseAttackDiscoveriesResults; beforeEach(() => { - (useInsights as jest.Mock).mockReturnValue(mockUseInsightsResults); + (useAttackDiscovery as jest.Mock).mockReturnValue(mockUseAttackDiscoveriesResults); render( <TestProviders> <Router history={historyMock}> <UpsellingProvider upsellingService={mockUpselling}> - <AiInsights /> + <AttackDiscoveryPage /> </UpsellingProvider> </Router> </TestProviders> @@ -268,8 +272,8 @@ describe('AiInsights', () => { expect(screen.queryByTestId('loadingCallout')).toBeNull(); }); - it('renders the expected number of insights', () => { - expect(screen.queryAllByTestId('insight')).toHaveLength(insights.length); + it('renders the expected number of attack discoveries', () => { + expect(screen.queryAllByTestId('attackDiscovery')).toHaveLength(attackDiscoveries.length); }); it('does NOT render the empty prompt', () => { @@ -283,15 +287,15 @@ describe('AiInsights', () => { describe('when loading', () => { beforeEach(() => { - (useInsights as jest.Mock).mockReturnValue( - getMockUseInsightsWithNoInsightsLoading(jest.fn()) // <-- loading + (useAttackDiscovery as jest.Mock).mockReturnValue( + getMockUseAttackDiscoveriesWithNoAttackDiscoveriesLoading(jest.fn()) // <-- loading ); render( <TestProviders> <Router history={historyMock}> <UpsellingProvider upsellingService={mockUpselling}> - <AiInsights /> + <AttackDiscoveryPage /> </UpsellingProvider> </Router> </TestProviders> @@ -306,8 +310,8 @@ describe('AiInsights', () => { expect(screen.getByTestId('loadingCallout')).toBeInTheDocument(); }); - it('does NOT render insights', () => { - expect(screen.queryAllByTestId('insight')).toHaveLength(0); + it('does NOT render attack discoveries', () => { + expect(screen.queryAllByTestId('attackDiscovery')).toHaveLength(0); }); it('does NOT render the empty prompt', () => { @@ -334,7 +338,7 @@ describe('AiInsights', () => { <Router history={historyMock}> <UpsellingProvider upsellingService={mockUpselling}> <MockAssistantProvider assistantAvailability={assistantUnavailable}> - <AiInsights /> + <AttackDiscoveryPage /> </MockAssistantProvider> </UpsellingProvider> </Router> @@ -350,8 +354,8 @@ describe('AiInsights', () => { expect(screen.queryByTestId('summary')).toBeNull(); }); - it('does NOT render insights', () => { - expect(screen.queryAllByTestId('insight')).toHaveLength(0); + it('does NOT render attack discoveries', () => { + expect(screen.queryAllByTestId('attackDiscovery')).toHaveLength(0); }); it('does NOT render the loading callout', () => { diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/index.tsx similarity index 65% rename from x-pack/plugins/security_solution/public/ai_insights/pages/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/index.tsx index 71b79980998370..e0318f61995dfa 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { css } from '@emotion/react'; import { - AI_INSIGHTS_STORAGE_KEY, + ATTACK_DISCOVERY_STORAGE_KEY, DEFAULT_ASSISTANT_NAMESPACE, useAssistantContext, } from '@kbn/elastic-assistant'; @@ -20,6 +20,7 @@ import { useLocalStorage } from 'react-use'; import { SecurityRoutePageWrapper } from '../../common/components/security_route_page_wrapper'; import { SecurityPageName } from '../../../common/constants'; import { HeaderPage } from '../../common/components/header_page'; +import { useSpaceId } from '../../common/hooks/use_space_id'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { EmptyPrompt } from './empty_prompt'; import { Header } from './header'; @@ -30,15 +31,17 @@ import { showLoading, showSummary, } from './helpers'; -import { Insight } from '../insight'; +import { AttackDiscoveryPanel } from '../attack_discovery_panel'; import { LoadingCallout } from './loading_callout'; import { PageTitle } from './page_title'; import { Summary } from './summary'; import { Upgrade } from './upgrade'; -import { useInsights } from '../use_insights'; -import type { AlertsInsight } from '../types'; +import { useAttackDiscovery } from '../use_attack_discovery'; +import type { AttackDiscovery } from '../types'; + +const AttackDiscoveryPageComponent: React.FC = () => { + const spaceId = useSpaceId() ?? 'default'; -const AiInsightsComponent: React.FC = () => { const { assistantAvailability: { isAssistantEnabled }, knowledgeBase, @@ -49,13 +52,13 @@ const AiInsightsComponent: React.FC = () => { const onToggleShowAnonymized = useCallback(() => setShowAnonymized((current) => !current), []); // get the last selected connector ID from local storage: - const [localStorageAiInsightsConnectorId, setLocalStorageAiInsightsConnectorId] = + const [localStorageAttackDiscoveryConnectorId, setLocalStorageAttackDiscoveryConnectorId] = useLocalStorage<string>( - `${DEFAULT_ASSISTANT_NAMESPACE}.${AI_INSIGHTS_STORAGE_KEY}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}` + `${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}` ); const [connectorId, setConnectorId] = React.useState<string | undefined>( - localStorageAiInsightsConnectorId + localStorageAttackDiscoveryConnectorId ); // state for the connector loading in the background: @@ -63,38 +66,41 @@ const AiInsightsComponent: React.FC = () => { const { approximateFutureTime, - cachedInsights, - fetchInsights, + attackDiscoveries, + cachedAttackDiscoveries, + fetchAttackDiscoveries, generationIntervals, - insights, isLoading, lastUpdated, replacements, - } = useInsights({ + } = useAttackDiscovery({ connectorId, setConnectorId, setLoadingConnectorId, }); - // get last updated from the cached insights if it exists: + // get last updated from the cached attack discoveries if it exists: const [selectedConnectorLastUpdated, setSelectedConnectorLastUpdated] = useState<Date | null>( - cachedInsights[connectorId ?? '']?.updated ?? null + cachedAttackDiscoveries[connectorId ?? '']?.updated ?? null ); - // get cached insights if they exist: - const [selectedConnectorInsights, setSelectedConnectorInsights] = useState<AlertsInsight[]>( - cachedInsights[connectorId ?? '']?.insights ?? [] - ); + // get cached attack discoveries if they exist: + const [selectedConnectorAttackDiscoveries, setSelectedConnectorAttackDiscoveries] = useState< + AttackDiscovery[] + >(cachedAttackDiscoveries[connectorId ?? '']?.attackDiscoveries ?? []); - // get replacements from the cached insights if they exist: + // get replacements from the cached attack discoveries if they exist: const [selectedConnectorReplacements, setSelectedConnectorReplacements] = useState<Replacements>( - cachedInsights[connectorId ?? '']?.replacements ?? {} + cachedAttackDiscoveries[connectorId ?? '']?.replacements ?? {} ); - // the number of unique alerts in the insights: + // the number of unique alerts in the attack discoveries: const alertsCount = useMemo( - () => uniq(selectedConnectorInsights.flatMap((insight) => insight.alertIds)).length, - [selectedConnectorInsights] + () => + uniq( + selectedConnectorAttackDiscoveries.flatMap((attackDiscovery) => attackDiscovery.alertIds) + ).length, + [selectedConnectorAttackDiscoveries] ); /** The callback when users select a connector ID */ @@ -102,21 +108,21 @@ const AiInsightsComponent: React.FC = () => { (selectedConnectorId: string) => { // update the connector ID in local storage: setConnectorId(selectedConnectorId); - setLocalStorageAiInsightsConnectorId(selectedConnectorId); + setLocalStorageAttackDiscoveryConnectorId(selectedConnectorId); - // get the cached insights for the selected connector: - const cached = cachedInsights[selectedConnectorId]; + // get the cached attack discoveries for the selected connector: + const cached = cachedAttackDiscoveries[selectedConnectorId]; if (cached != null) { setSelectedConnectorReplacements(cached.replacements ?? {}); - setSelectedConnectorInsights(cached.insights ?? []); + setSelectedConnectorAttackDiscoveries(cached.attackDiscoveries ?? []); setSelectedConnectorLastUpdated(cached.updated ?? null); } else { setSelectedConnectorReplacements({}); - setSelectedConnectorInsights([]); + setSelectedConnectorAttackDiscoveries([]); setSelectedConnectorLastUpdated(null); } }, - [cachedInsights, setLocalStorageAiInsightsConnectorId] + [cachedAttackDiscoveries, setLocalStorageAttackDiscoveryConnectorId] ); // get connector intervals from generation intervals: @@ -127,15 +133,15 @@ const AiInsightsComponent: React.FC = () => { const pageTitle = useMemo(() => <PageTitle />, []); - const onGenerate = useCallback(async () => fetchInsights(), [fetchInsights]); + const onGenerate = useCallback(async () => fetchAttackDiscoveries(), [fetchAttackDiscoveries]); useEffect(() => { setSelectedConnectorReplacements(replacements); - setSelectedConnectorInsights(insights); + setSelectedConnectorAttackDiscoveries(attackDiscoveries); setSelectedConnectorLastUpdated(lastUpdated); - }, [insights, lastUpdated, replacements]); + }, [attackDiscoveries, lastUpdated, replacements]); - const insightsCount = selectedConnectorInsights.length; + const attackDiscoveriesCount = selectedConnectorAttackDiscoveries.length; if (!isAssistantEnabled) { return ( @@ -156,8 +162,8 @@ const AiInsightsComponent: React.FC = () => { data-test-subj="fullHeightContainer" > <SecurityRoutePageWrapper - data-test-subj="aiInsightsPage" - pageName={SecurityPageName.aiInsights} + data-test-subj="attackDiscoveryPage" + pageName={SecurityPageName.attackDiscovery} > <HeaderPage border title={pageTitle}> <Header @@ -170,13 +176,13 @@ const AiInsightsComponent: React.FC = () => { </HeaderPage> {showSummary({ + attackDiscoveriesCount, connectorId, - insightsCount, loadingConnectorId, }) && ( <Summary alertsCount={alertsCount} - insightsCount={insightsCount} + attackDiscoveriesCount={attackDiscoveriesCount} lastUpdated={selectedConnectorLastUpdated} onToggleShowAnonymized={onToggleShowAnonymized} showAnonymized={showAnonymized} @@ -185,8 +191,8 @@ const AiInsightsComponent: React.FC = () => { <> {showLoading({ + attackDiscoveriesCount, connectorId, - insightsCount, isLoading, loadingConnectorId, }) ? ( @@ -196,11 +202,11 @@ const AiInsightsComponent: React.FC = () => { connectorIntervals={connectorIntervals} /> ) : ( - selectedConnectorInsights.map((insight, i) => ( - <React.Fragment key={insight.id}> - <Insight + selectedConnectorAttackDiscoveries.map((attackDiscovery, i) => ( + <React.Fragment key={attackDiscovery.id}> + <AttackDiscoveryPanel + attackDiscovery={attackDiscovery} initialIsOpen={getInitialIsOpen(i)} - insight={insight} showAnonymized={showAnonymized} replacements={selectedConnectorReplacements} /> @@ -220,7 +226,7 @@ const AiInsightsComponent: React.FC = () => { <EuiSpacer size="xxl" /> <EuiFlexItem grow={false}> - {showEmptyPrompt({ insightsCount, isLoading }) && ( + {showEmptyPrompt({ attackDiscoveriesCount, isLoading }) && ( <EmptyPrompt alertsCount={knowledgeBase.latestAlerts} isDisabled={connectorId == null} @@ -232,10 +238,12 @@ const AiInsightsComponent: React.FC = () => { <EuiFlexItem grow={true} /> </EuiFlexGroup> - <SpyRoute pageName={SecurityPageName.aiInsights} /> + <SpyRoute pageName={SecurityPageName.attackDiscovery} /> </SecurityRoutePageWrapper> </div> ); }; -export const AiInsights = React.memo(AiInsightsComponent); +AttackDiscoveryPageComponent.displayName = 'AttackDiscoveryPage'; + +export const AttackDiscoveryPage = React.memo(AttackDiscoveryPageComponent); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/generation_timing/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/generation_timing/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/generation_timing/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/generation_timing/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/helpers.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/helpers.ts diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/translations.ts similarity index 75% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/translations.ts index 266c08cfd2d9ee..502449d9256761 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/countdown/last_times_popover/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/last_times_popover/translations.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; export const AVERAGE_TIME_IS_CALCULATED = (intervals: number) => i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.lastTimesPopover.aiIsCurrentlyAnalyzing', + 'xpack.securitySolution.attackDiscovery.loadingCallout.countdown.lastTimesPopover.aiIsCurrentlyAnalyzing', { defaultMessage: 'Average time is calculated over the last {intervals} {intervals, plural, =1 {generation} other {generations}} on the selected connector:', @@ -18,7 +18,7 @@ export const AVERAGE_TIME_IS_CALCULATED = (intervals: number) => ); export const SECONDS_ABBREVIATION = i18n.translate( - 'xpack.securitySolution.aiInsights.loadingCallout.countdown.lastTimesPopover.secondsAbbreviationLabel', + 'xpack.securitySolution.attackDiscovery.loadingCallout.countdown.lastTimesPopover.secondsAbbreviationLabel', { defaultMessage: 's', // short for seconds } diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/translations.ts new file mode 100644 index 00000000000000..02273d213d5e7f --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/translations.ts @@ -0,0 +1,29 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ABOVE_THE_AVERAGE_TIME = i18n.translate( + 'xpack.securitySolution.attackDiscovery.loadingCallout.countdown.aboveTheAverageTimeLabel', + { + defaultMessage: 'Above the average time:', + } +); + +export const APPROXIMATE_TIME_REMAINING = i18n.translate( + 'xpack.securitySolution.attackDiscovery.loadingCallout.countdown.approximateTimeRemainingLabel', + { + defaultMessage: 'Approximate time remaining:', + } +); + +export const AVERAGE_TIME = i18n.translate( + 'xpack.securitySolution.attackDiscovery.loadingCallout.countdown.averageTimeLabel', + { + defaultMessage: 'Average time', + } +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/info_popover_body/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/info_popover_body/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/info_popover_body/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/info_popover_body/index.tsx diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/loading_messages/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/loading_messages/index.tsx similarity index 92% rename from x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/loading_messages/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/loading_messages/index.tsx index 42d7cb540a2bf1..9acd7b4d2dbbf8 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/loading_callout/loading_messages/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/loading_messages/index.tsx @@ -31,10 +31,10 @@ const LoadingMessagesComponent: React.FC<Props> = ({ alertsCount }) => { css={css` font-weight: 600; `} - data-test-subj="insightsGenerationInProgress" + data-test-subj="attackDiscoveryGenerationInProgress" size="s" > - {i18n.INSIGHTS_GENERATION_IN_PROGRESS} + {i18n.ATTACK_DISCOVERY_GENERATION_IN_PROGRESS} </EuiText> </EuiFlexItem> diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/translations.ts new file mode 100644 index 00000000000000..a63a419b147b5a --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/translations.ts @@ -0,0 +1,24 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const AI_IS_CURRENTLY_ANALYZING = (alertsCount: number) => + i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.loadingCallout.aiIsCurrentlyAnalyzing', + { + defaultMessage: `AI is currently analyzing up to {alertsCount} {alertsCount, plural, =1 {alert} other {alerts}} in the last 24 hours to generate discoveries.`, + values: { alertsCount }, + } + ); + +export const ATTACK_DISCOVERY_GENERATION_IN_PROGRESS = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.loadingCallout.attackDiscoveryGenerationInProgressLabel', + { + defaultMessage: 'Attack discovery in progress', + } +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/page_title/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/index.tsx similarity index 92% rename from x-pack/plugins/security_solution/public/ai_insights/pages/page_title/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/index.tsx index 25af7daf7f8190..236ad73a6995b6 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/page_title/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/index.tsx @@ -26,8 +26,8 @@ const PageTitleComponent: React.FC = () => { return ( <EuiFlexGroup alignItems="center" data-test-subj="pageTitle" gutterSize="none"> <EuiFlexItem grow={false}> - <EuiTitle data-test-subj="aiInsightsPageTitle" size="l"> - <h1>{i18n.AI_INSIGHTS_PAGE_TITLE}</h1> + <EuiTitle data-test-subj="attackDiscoveryPageTitle" size="l"> + <h1>{i18n.ATTACK_DISCOVERY_PAGE_TITLE}</h1> </EuiTitle> </EuiFlexItem> diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/translations.ts new file mode 100644 index 00000000000000..5d2b8bf92dbac8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/page_title/translations.ts @@ -0,0 +1,22 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const ATTACK_DISCOVERY_PAGE_TITLE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.pageTitle.pageTitle', + { + defaultMessage: 'Attack discovery', + } +); + +export const BETA = i18n.translate( + 'xpack.securitySolution.attackDiscovery.pages.pageTitle.betaBadge', + { + defaultMessage: 'Beta', + } +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/session_storage/index.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts similarity index 50% rename from x-pack/plugins/security_solution/public/ai_insights/pages/session_storage/index.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts index a3c068c1bdece6..2794660976bda8 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/session_storage/index.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts @@ -6,30 +6,30 @@ */ import type { Replacements } from '@kbn/elastic-assistant-common'; -import type { AlertsInsight } from '../../types'; +import type { AttackDiscovery } from '../../types'; -export interface CachedInsights { +export interface CachedAttackDiscoveries { connectorId: string; updated: Date; - insights: AlertsInsight[]; + attackDiscoveries: AttackDiscovery[]; replacements: Replacements; } -export const encodeCachedInsights = ( - cachedInsights: Record<string, CachedInsights> +export const encodeCachedAttackDiscoveries = ( + cachedAttackDiscoveries: Record<string, CachedAttackDiscoveries> ): string | null => { try { - return JSON.stringify(cachedInsights, null, 2); + return JSON.stringify(cachedAttackDiscoveries, null, 2); } catch { return null; } }; -export const decodeCachedInsights = ( - cachedInsights: string -): Record<string, CachedInsights> | null => { +export const decodeCachedAttackDiscoveries = ( + cachedAttackDiscoveries: string +): Record<string, CachedAttackDiscoveries> | null => { try { - return JSON.parse(cachedInsights); + return JSON.parse(cachedAttackDiscoveries); } catch { return null; } diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/summary/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary/index.tsx similarity index 93% rename from x-pack/plugins/security_solution/public/ai_insights/pages/summary/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/summary/index.tsx index 7cb9ff010bcf27..5794901a858928 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/summary/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary/index.tsx @@ -14,7 +14,7 @@ import { SHOW_REAL_VALUES, SHOW_ANONYMIZED_LABEL } from '../translations'; interface Props { alertsCount: number; - insightsCount: number; + attackDiscoveriesCount: number; lastUpdated: Date | null; onToggleShowAnonymized: () => void; showAnonymized: boolean; @@ -22,7 +22,7 @@ interface Props { const SummaryComponent: React.FC<Props> = ({ alertsCount, - insightsCount, + attackDiscoveriesCount, lastUpdated, onToggleShowAnonymized, showAnonymized, @@ -31,8 +31,8 @@ const SummaryComponent: React.FC<Props> = ({ <EuiFlexItem grow={false}> <SummaryCount alertsCount={alertsCount} + attackDiscoveriesCount={attackDiscoveriesCount} lastUpdated={lastUpdated} - insightsCount={insightsCount} /> <EuiSpacer size="l" /> </EuiFlexItem> diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/index.tsx similarity index 87% rename from x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/index.tsx index 5cc30e1f5936ec..6ef7160eb267a1 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/index.tsx @@ -10,17 +10,21 @@ import { css } from '@emotion/react'; import React, { useEffect, useMemo, useState } from 'react'; import moment from 'moment'; -import { ALERTS, INSIGHTS, LAST_GENERATED } from './translations'; +import { ALERTS, DISCOVERIES, LAST_GENERATED } from './translations'; export const EMPTY_LAST_UPDATED_DATE = '--'; interface Props { alertsCount: number; - insightsCount: number; + attackDiscoveriesCount: number; lastUpdated: Date | null; } -const SummaryCountComponent: React.FC<Props> = ({ alertsCount, insightsCount, lastUpdated }) => { +const SummaryCountComponent: React.FC<Props> = ({ + alertsCount, + attackDiscoveriesCount, + lastUpdated, +}) => { const { euiTheme } = useEuiTheme(); const [formattedDate, setFormattedDate] = useState<string>(EMPTY_LAST_UPDATED_DATE); @@ -71,8 +75,8 @@ const SummaryCountComponent: React.FC<Props> = ({ alertsCount, insightsCount, la size="xs" > <EuiFlexGroup alignItems="center" gutterSize="none"> - <EuiFlexItem data-test-subj="insightsCount" grow={false}> - {INSIGHTS(insightsCount)} + <EuiFlexItem data-test-subj="discoveriesCount" grow={false}> + {DISCOVERIES(attackDiscoveriesCount)} </EuiFlexItem> {Separator} diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/translations.ts similarity index 53% rename from x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/translations.ts index 1962420edefe46..3dbf5d9b8783e3 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/summary_count/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/summary_count/translations.ts @@ -8,19 +8,19 @@ import { i18n } from '@kbn/i18n'; export const ALERTS = (alertsCount: number) => - i18n.translate('xpack.securitySolution.aiInsights.summaryCount.alertsLabel', { + i18n.translate('xpack.securitySolution.attackDiscovery.summaryCount.alertsLabel', { defaultMessage: `{alertsCount} {alertsCount, plural, =1 {alert} other {alerts}}`, values: { alertsCount }, }); -export const INSIGHTS = (insightsCount: number) => - i18n.translate('xpack.securitySolution.aiInsights.summaryCount.insightsLabel', { - defaultMessage: `{insightsCount} {insightsCount, plural, =1 {insight} other {insights}}`, - values: { insightsCount }, +export const DISCOVERIES = (attackDiscoveriesCount: number) => + i18n.translate('xpack.securitySolution.attackDiscovery.summaryCount.discoveriesLabel', { + defaultMessage: `{attackDiscoveriesCount} {attackDiscoveriesCount, plural, =1 {discovery} other {discoveries}}`, + values: { attackDiscoveriesCount }, }); export const LAST_GENERATED = i18n.translate( - 'xpack.securitySolution.aiInsights.summaryCount.lastGeneratedLabel', + 'xpack.securitySolution.attackDiscovery.summaryCount.lastGeneratedLabel', { defaultMessage: 'Generated', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/translations.ts similarity index 59% rename from x-pack/plugins/security_solution/public/ai_insights/pages/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/pages/translations.ts index e379e4019e1436..3f8b87a9058c2d 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/translations.ts @@ -7,22 +7,22 @@ import { i18n } from '@kbn/i18n'; -export const ERROR_GENERATING_INSIGHTS = i18n.translate( - 'xpack.securitySolution.aiInsights.errorGeneratingInsightsToastTitle', +export const ERROR_GENERATING_ATTACK_DISCOVERIES = i18n.translate( + 'xpack.securitySolution.attackDiscovery.errorGeneratingAttackDiscoveriesToastTitle', { - defaultMessage: 'Error generating insights', + defaultMessage: 'Error generating attack discoveries', } ); export const SHOW_REAL_VALUES = i18n.translate( - 'xpack.securitySolution.aiInsights.showRealValuesLabel', + 'xpack.securitySolution.attackDiscovery.showRealValuesLabel', { defaultMessage: 'Show real values', } ); export const SHOW_ANONYMIZED_LABEL = i18n.translate( - 'xpack.securitySolution.aiInsights.showAnonymizedLabel', + 'xpack.securitySolution.attackDiscovery.showAnonymizedLabel', { defaultMessage: 'Show anonymized', } diff --git a/x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/index.tsx similarity index 79% rename from x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/index.tsx index 6da099e42f2b15..bf2cd241408a11 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/pages/upgrade/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/index.tsx @@ -25,7 +25,7 @@ const UpgradeComponent: React.FC = () => { <EuiFlexItem grow={false}> <EuiFlexGroup alignItems="center" direction="row" gutterSize="none"> <EuiFlexItem data-test-subj="upgradeTitle" grow={false}> - <span>{i18n.AI_INSIGHTS}</span> + <span>{i18n.FIND_POTENTIAL_ATTACKS_WITH_AI}</span> </EuiFlexItem> </EuiFlexGroup> </EuiFlexItem> @@ -38,8 +38,8 @@ const UpgradeComponent: React.FC = () => { () => ( <EuiFlexGroup alignItems="center" direction="column" gutterSize="none"> <EuiFlexItem grow={false}> - <EuiText color="subdued" data-test-subj="aiInsightsAreAvailable"> - {i18n.AI_INSIGHTS_ARE_AVAILABLE} + <EuiText color="subdued" data-test-subj="attackDiscoveryIsAvailable"> + {i18n.ATTACK_DISCOVERY_IS_AVAILABLE} </EuiText> </EuiFlexItem> @@ -53,7 +53,16 @@ const UpgradeComponent: React.FC = () => { [] ); - const actions = useMemo(() => <UpgradeButtons basePath={http.basePath.get()} />, [http.basePath]); + const actions = useMemo( + () => ( + <EuiFlexGroup justifyContent="center" gutterSize="none"> + <EuiFlexItem grow={false}> + <UpgradeButtons basePath={http.basePath.get()} /> + </EuiFlexItem> + </EuiFlexGroup> + ), + [http.basePath] + ); return <EuiEmptyPrompt actions={actions} body={body} data-test-subj="upgrade" title={title} />; }; diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/translations.ts new file mode 100644 index 00000000000000..eece0fb5a68700 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/upgrade/translations.ts @@ -0,0 +1,36 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const FIND_POTENTIAL_ATTACKS_WITH_AI = i18n.translate( + 'xpack.securitySolution.attackDiscovery.upgrade.findPotentialAttacksWithAiTitle', + { + defaultMessage: 'Find potential attacks with AI', + } +); + +export const ATTACK_DISCOVERY_IS_AVAILABLE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.upgrade.attackDiscoveryIsAvailable', + { + defaultMessage: 'Your license does not support Attack discovery.', + } +); + +export const PLEASE_UPGRADE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.upgrade.pleaseUpgradeMessage', + { + defaultMessage: 'Please upgrade your license to use this feature.', + } +); + +export const UPGRADE = i18n.translate( + 'xpack.securitySolution.attackDiscovery.upgrade.upgradeButton', + { + defaultMessage: 'Upgrade', + } +); diff --git a/x-pack/plugins/security_solution/public/ai_insights/routes.tsx b/x-pack/plugins/security_solution/public/attack_discovery/routes.tsx similarity index 67% rename from x-pack/plugins/security_solution/public/ai_insights/routes.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/routes.tsx index 14c3ac8f475326..10c9a70e4217f0 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/routes.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/routes.tsx @@ -7,19 +7,19 @@ import React from 'react'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; -import { AiInsights } from './pages'; +import { AttackDiscoveryPage } from './pages'; import type { SecuritySubPluginRoutes } from '../app/types'; import { SecurityPageName } from '../app/types'; -import { AI_INSIGHTS_PATH } from '../../common/constants'; +import { ATTACK_DISCOVERY_PATH } from '../../common/constants'; import { PluginTemplateWrapper } from '../common/components/plugin_template_wrapper'; import { SecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper'; -export const AiInsightsRoutes = () => ( +export const AttackDiscoveryRoutes = () => ( <PluginTemplateWrapper> - <TrackApplicationView viewId={SecurityPageName.aiInsights}> - <SecurityRoutePageWrapper pageName={SecurityPageName.aiInsights}> - <AiInsights /> + <TrackApplicationView viewId={SecurityPageName.attackDiscovery}> + <SecurityRoutePageWrapper pageName={SecurityPageName.attackDiscovery}> + <AttackDiscoveryPage /> </SecurityRoutePageWrapper> </TrackApplicationView> </PluginTemplateWrapper> @@ -27,7 +27,7 @@ export const AiInsightsRoutes = () => ( export const routes: SecuritySubPluginRoutes = [ { - path: AI_INSIGHTS_PATH, - component: AiInsightsRoutes, + path: ATTACK_DISCOVERY_PATH, + component: AttackDiscoveryRoutes, }, ]; diff --git a/x-pack/plugins/security_solution/public/ai_insights/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/translations.ts similarity index 56% rename from x-pack/plugins/security_solution/public/ai_insights/translations.ts rename to x-pack/plugins/security_solution/public/attack_discovery/translations.ts index 562ca0ac5e6719..50bab7ae536eb3 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/translations.ts @@ -8,71 +8,64 @@ import { i18n } from '@kbn/i18n'; export const COMMAND_AND_CONTROL = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.commandAndControlLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.commandAndControlLabel', { defaultMessage: 'Command & Control', } ); export const DISCOVERY = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.discoveryLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.discoveryLabel', { defaultMessage: 'Discovery', } ); export const EXECUTION = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.executionLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.executionLabel', { defaultMessage: 'Execution', } ); export const EXFILTRATION = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.exfiltrationLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.exfiltrationLabel', { defaultMessage: 'Exfiltration', } ); export const LATERAL_MOVEMENT = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.lateralMovementLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.lateralMovementLabel', { defaultMessage: 'Lateral Movement', } ); export const INITIAL_ACCESS = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.initialAccessLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.initialAccessLabel', { defaultMessage: 'Initial Access', } ); export const PERSISTENCE = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.persistenceLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.persistenceLabel', { defaultMessage: 'Persistence', } ); export const PRIVILEGE_ESCALATION = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.privilegeEscalationLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.privilegeEscalationLabel', { defaultMessage: 'Privilege Escalation', } ); export const RECONNAISSANCE = i18n.translate( - 'xpack.securitySolution.aiInsights.mitre.attack.tactics.reconnaissanceLabel', + 'xpack.securitySolution.attackDiscovery.mitre.attack.tactics.reconnaissanceLabel', { defaultMessage: 'Reconnaissance', } ); - -export const INSIGHTS_CONVERSATION_TITLE = i18n.translate( - 'xpack.securitySolution.aiInsights.insightsConversationTitle', - { - defaultMessage: 'Insights', - } -); diff --git a/x-pack/plugins/security_solution/public/ai_insights/types.ts b/x-pack/plugins/security_solution/public/attack_discovery/types.ts similarity index 81% rename from x-pack/plugins/security_solution/public/ai_insights/types.ts rename to x-pack/plugins/security_solution/public/attack_discovery/types.ts index 901ebf109a499f..5a5c490042d063 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/types.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -export interface AlertsInsight { +export interface AttackDiscovery { alertIds: string[]; detailsMarkdown: string; entitySummaryMarkdown: string; @@ -15,7 +15,7 @@ export interface AlertsInsight { title: string; } -/** Generation intervals measure the time it takes to generate insights */ +/** Generation intervals measure the time it takes to generate attack discoveries */ export interface GenerationInterval { connectorId: string; date: Date; diff --git a/x-pack/plugins/security_solution/public/ai_insights/use_insights/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/use_attack_discovery/index.tsx similarity index 61% rename from x-pack/plugins/security_solution/public/ai_insights/use_insights/index.tsx rename to x-pack/plugins/security_solution/public/attack_discovery/use_attack_discovery/index.tsx index 43fc74ddc9671e..c37680709916c9 100644 --- a/x-pack/plugins/security_solution/public/ai_insights/use_insights/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/use_attack_discovery/index.tsx @@ -6,14 +6,14 @@ */ import { - AI_INSIGHTS_STORAGE_KEY, + ATTACK_DISCOVERY_STORAGE_KEY, DEFAULT_ASSISTANT_NAMESPACE, useAssistantContext, useLoadConnectors, } from '@kbn/elastic-assistant'; -import type { AlertsInsightsPostRequestBody, Replacements } from '@kbn/elastic-assistant-common'; +import type { AttackDiscoveryPostRequestBody, Replacements } from '@kbn/elastic-assistant-common'; import { - AlertsInsightsPostResponse, + AttackDiscoveryPostResponse, ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, } from '@kbn/elastic-assistant-common'; import { isEmpty } from 'lodash/fp'; @@ -23,23 +23,39 @@ import { useLocalStorage, useSessionStorage } from 'react-use'; import * as uuid from 'uuid'; import { useFetchAnonymizationFields } from '@kbn/elastic-assistant/impl/assistant/api/anonymization_fields/use_fetch_anonymization_fields'; +import { useSpaceId } from '../../common/hooks/use_space_id'; import { useKibana } from '../../common/lib/kibana'; import { replaceNewlineLiterals } from '../helpers'; +import { useAttackDiscoveryTelemetry } from '../hooks/use_attack_discovery_telemetry'; import { - CACHED_INSIGHTS_SESSION_STORAGE_KEY, + CACHED_ATTACK_DISCOVERIES_SESSION_STORAGE_KEY, GENERATION_INTERVALS_LOCAL_STORAGE_KEY, getErrorToastText, getFallbackActionTypeId, } from '../pages/helpers'; import { getAverageIntervalSeconds } from '../pages/loading_callout/countdown/last_times_popover/helpers'; -import type { CachedInsights } from '../pages/session_storage'; -import { encodeCachedInsights, decodeCachedInsights } from '../pages/session_storage'; -import { ERROR_GENERATING_INSIGHTS } from '../pages/translations'; -import type { AlertsInsight, GenerationInterval } from '../types'; +import type { CachedAttackDiscoveries } from '../pages/session_storage'; +import { + encodeCachedAttackDiscoveries, + decodeCachedAttackDiscoveries, +} from '../pages/session_storage'; +import { ERROR_GENERATING_ATTACK_DISCOVERIES } from '../pages/translations'; +import type { AttackDiscovery, GenerationInterval } from '../types'; const MAX_GENERATION_INTERVALS = 5; -export const useInsights = ({ +export interface UseAttackDiscovery { + approximateFutureTime: Date | null; + attackDiscoveries: AttackDiscovery[]; + cachedAttackDiscoveries: Record<string, CachedAttackDiscoveries>; + fetchAttackDiscoveries: () => Promise<void>; + generationIntervals: Record<string, GenerationInterval[]> | undefined; + isLoading: boolean; + lastUpdated: Date | null; + replacements: Replacements; +} + +export const useAttackDiscovery = ({ connectorId, setConnectorId, setLoadingConnectorId, @@ -47,7 +63,10 @@ export const useInsights = ({ connectorId: string | undefined; setConnectorId?: (connectorId: string | undefined) => void; setLoadingConnectorId?: (loadingConnectorId: string | null) => void; -}) => { +}): UseAttackDiscovery => { + const { reportAttackDiscoveriesGenerated } = useAttackDiscoveryTelemetry(); + const spaceId = useSpaceId() ?? 'default'; + // get Kibana services and connectors const { http, @@ -65,19 +84,20 @@ export const useInsights = ({ const { data: anonymizationFields } = useFetchAnonymizationFields(); - // get cached insights from session storage: - const [sessionStorageCachedInsights, setSessionStorageCachedInsights] = useSessionStorage<string>( - `${DEFAULT_ASSISTANT_NAMESPACE}.${AI_INSIGHTS_STORAGE_KEY}.${CACHED_INSIGHTS_SESSION_STORAGE_KEY}` - ); - const [cachedInsights, setCachedInsights] = useState<Record<string, CachedInsights>>( - decodeCachedInsights(sessionStorageCachedInsights) ?? {} - ); + // get cached attack discoveries from session storage: + const [sessionStorageCachedAttackDiscoveries, setSessionStorageCachedAttackDiscoveries] = + useSessionStorage<string>( + `${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${CACHED_ATTACK_DISCOVERIES_SESSION_STORAGE_KEY}` + ); + const [cachedAttackDiscoveries, setCachedAttackDiscoveries] = useState< + Record<string, CachedAttackDiscoveries> + >(decodeCachedAttackDiscoveries(sessionStorageCachedAttackDiscoveries) ?? {}); // get generation intervals from local storage: const [localStorageGenerationIntervals, setLocalStorageGenerationIntervals] = useLocalStorage< Record<string, GenerationInterval[]> >( - `${DEFAULT_ASSISTANT_NAMESPACE}.${AI_INSIGHTS_STORAGE_KEY}.${GENERATION_INTERVALS_LOCAL_STORAGE_KEY}` + `${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${GENERATION_INTERVALS_LOCAL_STORAGE_KEY}` ); const [generationIntervals, setGenerationIntervals] = React.useState< Record<string, GenerationInterval[]> | undefined @@ -92,27 +112,27 @@ export const useInsights = ({ // generation can take a long time, so we calculate an approximate future time: const [approximateFutureTime, setApproximateFutureTime] = useState<Date | null>(null); - // get cached insights if they exist: - const [insights, setInsights] = useState<AlertsInsight[]>( - cachedInsights[connectorId ?? '']?.insights ?? [] + // get cached attack discoveries if they exist: + const [attackDiscoveries, setAttackDiscoveries] = useState<AttackDiscovery[]>( + cachedAttackDiscoveries[connectorId ?? '']?.attackDiscoveries ?? [] ); - // get replacements from the cached insights if they exist: + // get replacements from the cached attack discoveries if they exist: const [replacements, setReplacements] = useState<Replacements>( - cachedInsights[connectorId ?? '']?.replacements ?? {} + cachedAttackDiscoveries[connectorId ?? '']?.replacements ?? {} ); - // get last updated from the cached insights if it exists: + // get last updated from the cached attack discoveries if it exists: const [lastUpdated, setLastUpdated] = useState<Date | null>( - cachedInsights[connectorId ?? '']?.updated ?? null + cachedAttackDiscoveries[connectorId ?? '']?.updated ?? null ); /** The callback when users click the Generate button */ - const fetchInsights = useCallback(async () => { + const fetchAttackDiscoveries = useCallback(async () => { const selectedConnector = aiConnectors?.find((connector) => connector.id === connectorId); const actionTypeId = getFallbackActionTypeId(selectedConnector?.actionTypeId); - const body: AlertsInsightsPostRequestBody = { + const body: AttackDiscoveryPostRequestBody = { actionTypeId, alertsIndexPattern: alertsIndexPattern ?? '', anonymizationFields: anonymizationFields?.data ?? [], @@ -138,14 +158,14 @@ export const useInsights = ({ const startTime = moment(); // start timing the generation - // call the internal API to generate insights: - const rawResponse = await http.fetch('/internal/elastic_assistant/insights/alerts', { + // call the internal API to generate attack discoveries: + const rawResponse = await http.fetch('/internal/elastic_assistant/attack_discovery', { body: JSON.stringify(body), method: 'POST', version: ELASTIC_AI_ASSISTANT_INTERNAL_API_VERSION, }); - const parsedResponse = AlertsInsightsPostResponse.safeParse(rawResponse); + const parsedResponse = AttackDiscoveryPostResponse.safeParse(rawResponse); if (!parsedResponse.success) { throw new Error('Failed to parse the response'); } @@ -153,16 +173,16 @@ export const useInsights = ({ const endTime = moment(); const durationMs = endTime.diff(startTime); - // update the cached insights with the new insights: - const newInsights: AlertsInsight[] = - parsedResponse.data.insights?.map((insight) => ({ - alertIds: [...insight.alertIds], - detailsMarkdown: replaceNewlineLiterals(insight.detailsMarkdown), - entitySummaryMarkdown: replaceNewlineLiterals(insight.entitySummaryMarkdown), + // update the cached attack discoveries with the new discoveries: + const newAttackDiscoveries: AttackDiscovery[] = + parsedResponse.data.attackDiscoveries?.map((attackDiscovery) => ({ + alertIds: [...attackDiscovery.alertIds], + detailsMarkdown: replaceNewlineLiterals(attackDiscovery.detailsMarkdown), + entitySummaryMarkdown: replaceNewlineLiterals(attackDiscovery.entitySummaryMarkdown), id: uuid.v4(), - mitreAttackTactics: insight.mitreAttackTactics, - summaryMarkdown: replaceNewlineLiterals(insight.summaryMarkdown), - title: insight.title, + mitreAttackTactics: attackDiscovery.mitreAttackTactics, + summaryMarkdown: replaceNewlineLiterals(attackDiscovery.summaryMarkdown), + title: attackDiscovery.title, })) ?? []; const responseReplacements = parsedResponse.data.replacements ?? {}; @@ -170,18 +190,20 @@ export const useInsights = ({ const newLastUpdated = new Date(); - const newCachedInsights = { - ...cachedInsights, + const newCachedAttackDiscoveries = { + ...cachedAttackDiscoveries, [connectorId ?? '']: { connectorId: connectorId ?? '', - insights: newInsights, + attackDiscoveries: newAttackDiscoveries, replacements: newReplacements, updated: newLastUpdated, }, }; - setCachedInsights(newCachedInsights); - setSessionStorageCachedInsights(encodeCachedInsights(newCachedInsights) ?? ''); + setCachedAttackDiscoveries(newCachedAttackDiscoveries); + setSessionStorageCachedAttackDiscoveries( + encodeCachedAttackDiscoveries(newCachedAttackDiscoveries) ?? '' + ); // update the generation intervals with the latest timing: const previousConnectorIntervals: GenerationInterval[] = @@ -205,12 +227,13 @@ export const useInsights = ({ setLocalStorageGenerationIntervals(newGenerationIntervals); setReplacements(newReplacements); - setInsights(newInsights); + setAttackDiscoveries(newAttackDiscoveries); setLastUpdated(newLastUpdated); setConnectorId?.(connectorId); + reportAttackDiscoveriesGenerated({ actionTypeId }); } catch (error) { toasts?.addDanger(error, { - title: ERROR_GENERATING_INSIGHTS, + title: ERROR_GENERATING_ATTACK_DISCOVERIES, text: getErrorToastText(error), }); } finally { @@ -222,17 +245,18 @@ export const useInsights = ({ aiConnectors, alertsIndexPattern, anonymizationFields?.data, - cachedInsights, + cachedAttackDiscoveries, connectorId, connectorIntervals, generationIntervals, http, knowledgeBase.latestAlerts, replacements, + reportAttackDiscoveriesGenerated, setConnectorId, setLoadingConnectorId, setLocalStorageGenerationIntervals, - setSessionStorageCachedInsights, + setSessionStorageCachedAttackDiscoveries, toasts, traceOptions?.langSmithApiKey, traceOptions?.langSmithProject, @@ -240,10 +264,10 @@ export const useInsights = ({ return { approximateFutureTime, - cachedInsights, - fetchInsights, + attackDiscoveries, + cachedAttackDiscoveries, + fetchAttackDiscoveries, generationIntervals, - insights, isLoading, lastUpdated, replacements, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts index 373df1e4a3ba8b..cc695d72a8ffcb 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts @@ -18,7 +18,7 @@ export const CATEGORIES: SeparatorLinkCategory[] = [ linkIds: [ SecurityPageName.rulesLanding, SecurityPageName.alerts, - SecurityPageName.aiInsights, + SecurityPageName.attackDiscovery, SecurityPageName.cloudSecurityPostureFindings, SecurityPageName.case, ], diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts index 052a3296d84147..a6eff07ac00ff6 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts @@ -55,7 +55,7 @@ export enum TelemetryEventTypes { AssetCriticalityCsvPreviewGenerated = 'Asset Criticality Csv Preview Generated', AssetCriticalityFileSelected = 'Asset Criticality File Selected', AssetCriticalityCsvImported = 'Asset Criticality CSV Imported', - InsightsGenerated = 'Insights Generated', + AttackDiscoveriesGenerated = 'Attack Discoveries Generated', EntityDetailsClicked = 'Entity Details Clicked', EntityAlertsClicked = 'Entity Alerts Clicked', EntityRiskFiltered = 'Entity Risk Filtered', diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/index.ts similarity index 93% rename from x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/index.ts rename to x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/index.ts index 80fd4e30238461..eaad6840348fbf 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/index.ts @@ -9,7 +9,7 @@ import type { TelemetryEvent } from '../../types'; import { TelemetryEventTypes } from '../../constants'; export const insightsGeneratedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.InsightsGenerated, + eventType: TelemetryEventTypes.AttackDiscoveriesGenerated, schema: { actionTypeId: { type: 'keyword', diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/types.ts similarity index 57% rename from x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/types.ts rename to x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/types.ts index ca211bb1f95c59..a2ad7ba93455bc 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/insights/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/attack_discovery/types.ts @@ -8,15 +8,15 @@ import type { RootSchema } from '@kbn/analytics-client'; import type { TelemetryEventTypes } from '../../constants'; -export interface ReportInsightsGeneratedParams { +export interface ReportAttackDiscoveriesGeneratedParams { actionTypeId: string; provider?: string; model?: string; } -export type ReportInsightsTelemetryEventParams = ReportInsightsGeneratedParams; +export type ReportAttackDiscoveryTelemetryEventParams = ReportAttackDiscoveriesGeneratedParams; -export interface InsightsTelemetryEvent { - eventType: TelemetryEventTypes.InsightsGenerated; - schema: RootSchema<ReportInsightsGeneratedParams>; +export interface AttackDiscoveryTelemetryEvent { + eventType: TelemetryEventTypes.AttackDiscoveriesGenerated; + schema: RootSchema<ReportAttackDiscoveriesGeneratedParams>; } diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts index c69f00279d20bd..fa9875a027db68 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts @@ -28,7 +28,7 @@ import { assistantMessageSentEvent, assistantQuickPrompt, } from './ai_assistant'; -import { insightsGeneratedEvent } from './insights'; +import { insightsGeneratedEvent } from './attack_discovery'; import { dataQualityIndexCheckedEvent, dataQualityCheckAllClickedEvent } from './data_quality'; import { DocumentDetailsFlyoutOpenedEvent, diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts index 061138d25007ad..e1c4c8d4746cfd 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts @@ -35,5 +35,5 @@ export const createTelemetryClientMock = (): jest.Mocked<TelemetryClientStart> = reportAssetCriticalityCsvPreviewGenerated: jest.fn(), reportAssetCriticalityFileSelected: jest.fn(), reportAssetCriticalityCsvImported: jest.fn(), - reportInsightsGenerated: jest.fn(), + reportAttackDiscoveriesGenerated: jest.fn(), }); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts index bbd291076c3cec..3ca55ab75e6851 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts @@ -24,7 +24,7 @@ import type { ReportAssistantMessageSentParams, ReportAssistantQuickPromptParams, ReportAssistantSettingToggledParams, - ReportInsightsGeneratedParams, + ReportAttackDiscoveriesGeneratedParams, ReportRiskInputsExpandedFlyoutOpenedParams, ReportToggleRiskSummaryClickedParams, ReportDetailsFlyoutOpenedParams, @@ -74,8 +74,8 @@ export class TelemetryClient implements TelemetryClientStart { this.analytics.reportEvent(TelemetryEventTypes.AssistantSettingToggled, params); }; - public reportInsightsGenerated = (params: ReportInsightsGeneratedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.InsightsGenerated, params); + public reportAttackDiscoveriesGenerated = (params: ReportAttackDiscoveriesGeneratedParams) => { + this.analytics.reportEvent(TelemetryEventTypes.AttackDiscoveriesGenerated, params); }; public reportEntityDetailsClicked = ({ entity }: ReportEntityDetailsClickedParams) => { diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts index a2cef18618ff5c..85c567121ac2d0 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts @@ -8,10 +8,10 @@ import type { RootSchema } from '@kbn/analytics-client'; import type { AnalyticsServiceSetup } from '@kbn/core/public'; import type { - InsightsTelemetryEvent, - ReportInsightsGeneratedParams, - ReportInsightsTelemetryEventParams, -} from './events/insights/types'; + AttackDiscoveryTelemetryEvent, + ReportAttackDiscoveriesGeneratedParams, + ReportAttackDiscoveryTelemetryEventParams, +} from './events/attack_discovery/types'; import type { SecurityMetadata } from '../../../actions/types'; import type { ML_JOB_TELEMETRY_STATUS, TelemetryEventTypes } from './constants'; import type { @@ -62,7 +62,7 @@ import type { export * from './events/ai_assistant/types'; export * from './events/alerts_grouping/types'; -export * from './events/insights/types'; +export * from './events/attack_discovery/types'; export * from './events/data_quality/types'; export * from './events/onboarding/types'; export type { @@ -109,7 +109,7 @@ export interface ReportBreadcrumbClickedParams { export type TelemetryEventParams = | ReportAlertsGroupingTelemetryEventParams | ReportAssistantTelemetryEventParams - | ReportInsightsTelemetryEventParams + | ReportAttackDiscoveryTelemetryEventParams | ReportEntityAnalyticsTelemetryEventParams | ReportMLJobUpdateParams | ReportCellActionClickedParams @@ -133,8 +133,8 @@ export interface TelemetryClientStart { reportAssistantQuickPrompt(params: ReportAssistantQuickPromptParams): void; reportAssistantSettingToggled(params: ReportAssistantSettingToggledParams): void; - // Insights - reportInsightsGenerated(params: ReportInsightsGeneratedParams): void; + // Attack discovery + reportAttackDiscoveriesGenerated(params: ReportAttackDiscoveriesGeneratedParams): void; // Entity Analytics reportEntityDetailsClicked(params: ReportEntityDetailsClickedParams): void; @@ -174,7 +174,7 @@ export type TelemetryEvent = | EntityAnalyticsTelemetryEvent | DataQualityTelemetryEvents | DocumentDetailsTelemetryEvents - | InsightsTelemetryEvent + | AttackDiscoveryTelemetryEvent | { eventType: TelemetryEventTypes.MLJobUpdate; schema: RootSchema<ReportMLJobUpdateParams>; diff --git a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx index 5c7686563f4b5c..166d14b01c1a1d 100644 --- a/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx +++ b/x-pack/plugins/security_solution/public/lazy_sub_plugins.tsx @@ -10,7 +10,7 @@ * By loading these later we can reduce the initial bundle size and allow users to delay loading these dependencies until they are needed. */ -import { AiInsights } from './ai_insights'; +import { AttackDiscovery } from './attack_discovery'; import { Cases } from './cases'; import { Detections } from './detections'; import { Exceptions } from './exceptions'; @@ -33,7 +33,7 @@ import { MachineLearning } from './machine_learning'; * The classes used to instantiate the sub plugins. These are grouped into a single object for the sake of bundling them in a single dynamic import. */ const subPluginClasses = { - AiInsights, + AttackDiscovery, Detections, Cases, Exceptions, diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 0146aac6f6f0da..dfadc74330281c 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -304,8 +304,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S if (!this._subPlugins) { const { subPluginClasses } = await this.lazySubPlugins(); this._subPlugins = { - aiInsights: new subPluginClasses.AiInsights(), alerts: new subPluginClasses.Detections(), + attackDiscovery: new subPluginClasses.AttackDiscovery(), rules: new subPluginClasses.Rules(), exceptions: new subPluginClasses.Exceptions(), cases: new subPluginClasses.Cases(), @@ -337,8 +337,10 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S ): Promise<StartedSubPlugins> { const subPlugins = await this.createSubPlugins(); return { - aiInsights: subPlugins.aiInsights.start(this.experimentalFeatures.assistantAlertsInsights), alerts: subPlugins.alerts.start(storage), + attackDiscovery: subPlugins.attackDiscovery.start( + this.experimentalFeatures.attackDiscoveryEnabled + ), cases: subPlugins.cases.start(), cloudDefend: subPlugins.cloudDefend.start(), cloudSecurityPosture: subPlugins.cloudSecurityPosture.start(), diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 3b808fab4ec2b1..409f019238caf5 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -72,7 +72,7 @@ import type { CloudSecurityPosture } from './cloud_security_posture'; import type { CloudDefend } from './cloud_defend'; import type { ThreatIntelligence } from './threat_intelligence'; import type { SecuritySolutionTemplateWrapper } from './app/home/template_wrapper'; -import type { AiInsights } from './ai_insights'; +import type { AttackDiscovery } from './attack_discovery'; import type { Explore } from './explore'; import type { NavigationLink } from './common/links'; import type { EntityAnalytics } from './entity_analytics'; @@ -207,8 +207,8 @@ export const CASES_SUB_PLUGIN_KEY = 'cases'; export interface SubPlugins { [CASES_SUB_PLUGIN_KEY]: Cases; - aiInsights: AiInsights; alerts: Detections; + attackDiscovery: AttackDiscovery; cloudDefend: CloudDefend; cloudSecurityPosture: CloudSecurityPosture; dashboards: Dashboards; @@ -229,8 +229,8 @@ export interface SubPlugins { // TODO: find a better way to defined these types export interface StartedSubPlugins { [CASES_SUB_PLUGIN_KEY]: ReturnType<Cases['start']>; - aiInsights: ReturnType<AiInsights['start']>; alerts: ReturnType<Detections['start']>; + attackDiscovery: ReturnType<AttackDiscovery['start']>; cloudDefend: ReturnType<CloudDefend['start']>; cloudSecurityPosture: ReturnType<CloudSecurityPosture['start']>; dashboards: ReturnType<Dashboards['start']>; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/insights/insights_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/attack_discovery_tool.ts similarity index 71% rename from x-pack/plugins/security_solution/server/assistant/tools/insights/insights_tool.ts rename to x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/attack_discovery_tool.ts index 471377b9c201cb..e2b03c53473763 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/insights/insights_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/attack_discovery_tool.ts @@ -16,26 +16,26 @@ import { APP_UI_ID } from '../../../../common'; import { getAnonymizedAlerts } from './get_anonymized_alerts'; import { getOutputParser } from './get_output_parser'; import { sizeIsOutOfRange } from '../open_and_acknowledged_alerts/helpers'; -import { getInsightsPrompt } from './get_insights_prompt'; +import { getAttackDiscoveryPrompt } from './get_attack_discovery_prompt'; -export interface InsightsToolParams extends AssistantToolParams { +export interface AttackDiscoveryToolParams extends AssistantToolParams { alertsIndexPattern: string; size: number; } -export const INSIGHTS_TOOL_DESCRIPTION = - 'Call this for insights containing `markdown` that should be displayed verbatim (with no additional processing).'; +export const ATTACK_DISCOVERY_TOOL_DESCRIPTION = + 'Call this for attack discoveries containing `markdown` that should be displayed verbatim (with no additional processing).'; /** - * Returns a tool for insights from open and acknowledged alerts, or null if - * the request doesn't have all the required parameters. + * Returns a tool for generating attack discoveries from open and acknowledged + * alerts, or null if the request doesn't have all the required parameters. */ -export const INSIGHTS_TOOL: AssistantTool = { - id: 'insights-tool', - name: 'InsightsTool', - description: INSIGHTS_TOOL_DESCRIPTION, +export const ATTACK_DISCOVERY_TOOL: AssistantTool = { + id: 'attack-discovery', + name: 'AttackDiscoveryTool', + description: ATTACK_DISCOVERY_TOOL_DESCRIPTION, sourceRegister: APP_UI_ID, - isSupported: (params: AssistantToolParams): params is InsightsToolParams => { + isSupported: (params: AssistantToolParams): params is AttackDiscoveryToolParams => { const { alertsIndexPattern, llm, request, size } = params; return ( requestHasRequiredAnonymizationParams(request) && @@ -56,14 +56,14 @@ export const INSIGHTS_TOOL: AssistantTool = { onNewReplacements, replacements, size, - } = params as InsightsToolParams; + } = params as AttackDiscoveryToolParams; return new DynamicTool({ - name: 'InsightsTool', - description: INSIGHTS_TOOL_DESCRIPTION, + name: 'AttackDiscoveryTool', + description: ATTACK_DISCOVERY_TOOL_DESCRIPTION, func: async () => { if (llm == null) { - throw new Error('LLM is required for insights'); + throw new Error('LLM is required for attack discoveries'); } const anonymizedAlerts = await getAnonymizedAlerts({ @@ -94,12 +94,12 @@ export const INSIGHTS_TOOL: AssistantTool = { }); const result = await answerFormattingChain.call({ - query: getInsightsPrompt({ anonymizedAlerts }), + query: getAttackDiscoveryPrompt({ anonymizedAlerts }), }); return JSON.stringify(result.records, null, 2); }, - tags: ['insights'], + tags: ['attack-discovery'], }); }, }; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/insights/get_anonymized_alerts.ts b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_anonymized_alerts.ts similarity index 100% rename from x-pack/plugins/security_solution/server/assistant/tools/insights/get_anonymized_alerts.ts rename to x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_anonymized_alerts.ts diff --git a/x-pack/plugins/security_solution/server/assistant/tools/insights/get_insights_prompt.ts b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_attack_discovery_prompt.ts similarity index 91% rename from x-pack/plugins/security_solution/server/assistant/tools/insights/get_insights_prompt.ts rename to x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_attack_discovery_prompt.ts index 30d6254c574c4e..df211f0bd0a7d0 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/insights/get_insights_prompt.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_attack_discovery_prompt.ts @@ -5,7 +5,8 @@ * 2.0. */ -export const getInsightsPrompt = ({ +// NOTE: we ask the LLM to `provide insights`. We do NOT use the feature name, `AttackDiscovery`, in the prompt. +export const getAttackDiscoveryPrompt = ({ anonymizedAlerts, }: { anonymizedAlerts: string[]; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/insights/get_output_parser.ts b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_output_parser.ts similarity index 96% rename from x-pack/plugins/security_solution/server/assistant/tools/insights/get_output_parser.ts rename to x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_output_parser.ts index 604c0853ee92bf..1c839ffc645229 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/insights/get_output_parser.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/get_output_parser.ts @@ -37,6 +37,7 @@ const MITRE_ATTACK_TACTICS = [ EXFILTRATION, ] as const; +// NOTE: we ask the LLM for `insight`s. We do NOT use the feature name, `AttackDiscovery`, in the prompt. export const getOutputParser = () => StructuredOutputParser.fromZodSchema( z @@ -68,7 +69,6 @@ export const getOutputParser = () => .describe(`A markdown summary of insight, using the same ${SYNTAX} syntax`), title: z .string() - .optional() .describe( 'A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible.' ), diff --git a/x-pack/plugins/security_solution/server/assistant/tools/insights/helpers.ts b/x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/server/assistant/tools/insights/helpers.ts rename to x-pack/plugins/security_solution/server/assistant/tools/attack_discovery/helpers.ts diff --git a/x-pack/plugins/security_solution/server/assistant/tools/index.ts b/x-pack/plugins/security_solution/server/assistant/tools/index.ts index a464bf2a58f703..b99c1f6e0cd380 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/index.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/index.ts @@ -10,11 +10,11 @@ import type { AssistantTool } from '@kbn/elastic-assistant-plugin/server'; import { ALERT_COUNTS_TOOL } from './alert_counts/alert_counts_tool'; import { ESQL_KNOWLEDGE_BASE_TOOL } from './esql_language_knowledge_base/esql_language_knowledge_base_tool'; import { OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL } from './open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool'; -import { INSIGHTS_TOOL } from './insights/insights_tool'; +import { ATTACK_DISCOVERY_TOOL } from './attack_discovery/attack_discovery_tool'; export const getAssistantTools = (): AssistantTool[] => [ ALERT_COUNTS_TOOL, - INSIGHTS_TOOL, + ATTACK_DISCOVERY_TOOL, ESQL_KNOWLEDGE_BASE_TOOL, OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL, ]; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 1b282771a608ab..998e02aba56ad7 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -559,8 +559,8 @@ export class Plugin implements ISecuritySolutionPlugin { // Assistant Tool and Feature Registration plugins.elasticAssistant.registerTools(APP_UI_ID, getAssistantTools()); plugins.elasticAssistant.registerFeatures(APP_UI_ID, { - assistantAlertsInsights: config.experimentalFeatures.assistantAlertsInsights, assistantModelEvaluation: config.experimentalFeatures.assistantModelEvaluation, + attackDiscoveryEnabled: config.experimentalFeatures.attackDiscoveryEnabled, }); plugins.elasticAssistant.registerFeatures('management', { assistantModelEvaluation: config.experimentalFeatures.assistantModelEvaluation,