diff --git a/x-pack/solutions/security/packages/features/src/notes/kibana_features.ts b/x-pack/solutions/security/packages/features/src/notes/kibana_features.ts index b418a13183ea7..4fa9ac1caa9d1 100644 --- a/x-pack/solutions/security/packages/features/src/notes/kibana_features.ts +++ b/x-pack/solutions/security/packages/features/src/notes/kibana_features.ts @@ -36,8 +36,8 @@ export const getNotesBaseKibanaFeature = ( all: params.savedObjects, read: params.savedObjects, }, - ui: ['read', 'crud'], - api: ['notes_read', 'notes_write'], + ui: [], + api: [], }, read: { app: [NOTES_FEATURE_ID, 'kibana'], @@ -46,8 +46,8 @@ export const getNotesBaseKibanaFeature = ( all: [], read: params.savedObjects, }, - ui: ['read'], - api: ['notes_read'], + ui: [], + api: [], }, }, }); diff --git a/x-pack/solutions/security/packages/features/src/product_features_keys.ts b/x-pack/solutions/security/packages/features/src/product_features_keys.ts index 30ea5bd8bc018..208ee3619608c 100644 --- a/x-pack/solutions/security/packages/features/src/product_features_keys.ts +++ b/x-pack/solutions/security/packages/features/src/product_features_keys.ts @@ -6,6 +6,10 @@ */ export enum ProductFeatureSecurityKey { + /** Enables Detections workflows, with rules and alerts management */ + detections = 'detections', + /** Enables Security dashboards */ + dashboards = 'dashboards', /** Enables Advanced Insights (Entity Risk, GenAI) */ advancedInsights = 'advanced_insights', /** diff --git a/x-pack/solutions/security/packages/features/src/security/product_feature_config.ts b/x-pack/solutions/security/packages/features/src/security/product_feature_config.ts index 219af9f531a32..7bff1c047157c 100644 --- a/x-pack/solutions/security/packages/features/src/security/product_feature_config.ts +++ b/x-pack/solutions/security/packages/features/src/security/product_feature_config.ts @@ -20,6 +20,24 @@ import type { DefaultSecurityProductFeaturesConfig } from './types'; * - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified. */ export const securityDefaultProductFeaturesConfig: DefaultSecurityProductFeaturesConfig = { + [ProductFeatureSecurityKey.dashboards]: { + privileges: { + all: { ui: ['dashboards'] }, + read: { ui: ['dashboards'] }, + }, + }, + [ProductFeatureSecurityKey.detections]: { + privileges: { + all: { + ui: ['rules', 'alerts', 'explore'], + api: [`${APP_ID}-detections`], // TODO: add detections API action `authz` + }, + read: { + ui: ['rules', 'alerts', 'explore'], + api: [`${APP_ID}-detections`], // TODO: add detections API action `authz` + }, + }, + }, [ProductFeatureSecurityKey.advancedInsights]: { privileges: { all: { diff --git a/x-pack/solutions/security/packages/features/src/timeline/kibana_features.ts b/x-pack/solutions/security/packages/features/src/timeline/kibana_features.ts index 5c9fbfecda570..0a60abdf02f32 100644 --- a/x-pack/solutions/security/packages/features/src/timeline/kibana_features.ts +++ b/x-pack/solutions/security/packages/features/src/timeline/kibana_features.ts @@ -36,8 +36,8 @@ export const getTimelineBaseKibanaFeature = ( all: params.savedObjects, read: params.savedObjects, }, - ui: ['read', 'crud'], - api: ['timeline_read', 'timeline_write'], + ui: [], + api: [], }, read: { app: [TIMELINE_FEATURE_ID, 'kibana'], @@ -46,8 +46,8 @@ export const getTimelineBaseKibanaFeature = ( all: [], read: params.savedObjects, }, - ui: ['read'], - api: ['timeline_read'], + ui: [], + api: [], }, }, }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_links.ts b/x-pack/solutions/security/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_links.ts index de41c8f74d6fa..3456f4a80ba1c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/solution_navigation/links/sections/investigations_links.ts @@ -6,8 +6,11 @@ */ import { ExternalPageName, SecurityPageName } from '@kbn/security-solution-navigation'; -import { INVESTIGATIONS_PATH } from '../../../../../common/constants'; -import { SECURITY_FEATURE_ID } from '../../../../../common'; +import { + INVESTIGATIONS_PATH, + NOTES_FEATURE_ID, + TIMELINE_FEATURE_ID, +} from '../../../../../common/constants'; import type { LinkItem } from '../../../../common/links/types'; import type { SolutionNavLink } from '../../../../common/links'; import { IconOsqueryLazy, IconTimelineLazy } from './lazy_icons'; @@ -18,7 +21,7 @@ const investigationsAppLink: LinkItem = { id: SecurityPageName.investigations, title: i18n.INVESTIGATIONS_TITLE, path: INVESTIGATIONS_PATH, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: [`${TIMELINE_FEATURE_ID}.read`, `${NOTES_FEATURE_ID}.read`], hideTimeline: true, skipUrlState: true, links: [], // timeline and note links are added via the methods below diff --git a/x-pack/solutions/security/plugins/security_solution/public/dashboards/links.ts b/x-pack/solutions/security/plugins/security_solution/public/dashboards/links.ts index 7feecbd1742c9..8c6654bf0d29e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/dashboards/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/dashboards/links.ts @@ -31,7 +31,7 @@ export const dashboardsLinks: LinkItem = { title: DASHBOARDS, path: DASHBOARDS_PATH, globalNavPosition: 1, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: [[`${SECURITY_FEATURE_ID}.show`, `${SECURITY_FEATURE_ID}.dashboards`]], globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.dashboards', { defaultMessage: 'Dashboards', diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/links.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/links.ts index 20b4e031e5478..58ac8d08eb4ab 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/links.ts @@ -13,7 +13,7 @@ export const links: LinkItem = { id: SecurityPageName.alerts, title: ALERTS, path: ALERTS_PATH, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: [[`${SECURITY_FEATURE_ID}.show`, `${SECURITY_FEATURE_ID}.alerts`]], globalNavPosition: 3, globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.alerts', { diff --git a/x-pack/solutions/security/plugins/security_solution/public/explore/links.ts b/x-pack/solutions/security/plugins/security_solution/public/explore/links.ts index 18f176494fffe..3b691bc2d9f95 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/explore/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/explore/links.ts @@ -209,7 +209,7 @@ export const exploreLinks: LinkItem = { title: EXPLORE, path: EXPLORE_PATH, globalNavPosition: 9, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: [[`${SECURITY_FEATURE_ID}.show`, `${SECURITY_FEATURE_ID}.explore`]], globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.explore', { defaultMessage: 'Explore', diff --git a/x-pack/solutions/security/plugins/security_solution/public/rules/links.ts b/x-pack/solutions/security/plugins/security_solution/public/rules/links.ts index 28f44585d3037..4730cf5854a00 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/rules/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/rules/links.ts @@ -38,7 +38,7 @@ export const links: LinkItem = { hideTimeline: true, skipUrlState: true, globalNavPosition: 2, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: [[`${SECURITY_FEATURE_ID}.show`, `${SECURITY_FEATURE_ID}.rules`]], links: [ { id: SecurityPageName.rules, diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/common/config.ts b/x-pack/solutions/security/plugins/security_solution_serverless/common/config.ts index 2f0cc03ccb320..26a527591235f 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/common/config.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/common/config.ts @@ -10,6 +10,7 @@ import { ProductLine, ProductTier } from './product'; export const productLine = schema.oneOf([ schema.literal(ProductLine.security), + schema.literal(ProductLine.ai), schema.literal(ProductLine.endpoint), schema.literal(ProductLine.cloud), ]); diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/common/pli/pli_config.ts b/x-pack/solutions/security/plugins/security_solution_serverless/common/pli/pli_config.ts index 77899352ab104..44121fce079fb 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/common/pli/pli_config.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/common/pli/pli_config.ts @@ -16,6 +16,10 @@ type PliProductFeatures = Readonly< export const PLI_PRODUCT_FEATURES: PliProductFeatures = { security: { essentials: [ + ProductFeatureKey.dashboards, + ProductFeatureKey.detections, + ProductFeatureKey.timeline, + ProductFeatureKey.notes, ProductFeatureKey.endpointHostManagement, ProductFeatureKey.endpointPolicyManagement, ], @@ -53,4 +57,14 @@ export const PLI_PRODUCT_FEATURES: PliProductFeatures = { essentials: [ProductFeatureKey.cloudSecurityPosture], complete: [], }, + ai: { + // Not split into essentials and complete, using essentials for now + essentials: [ + // I am guessing here + ProductFeatureKey.attackDiscovery, + ProductFeatureKey.assistant, + ProductFeatureKey.threatIntelligence, + ], + complete: [], + }, } as const; diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/common/product.ts b/x-pack/solutions/security/plugins/security_solution_serverless/common/product.ts index d5095eea31637..6697cd01e6754 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/common/product.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/common/product.ts @@ -7,6 +7,7 @@ export enum ProductLine { security = 'security', + ai = 'ai', endpoint = 'endpoint', cloud = 'cloud', } diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_soc_navigation.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_soc_navigation.ts new file mode 100644 index 0000000000000..e11339eaa7b5c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_soc_navigation.ts @@ -0,0 +1,62 @@ +/* + * 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 { + AppDeepLinkId, + GroupDefinition, + NavigationTreeDefinition, + NodeDefinition, +} from '@kbn/core-chrome-browser'; + +import type { WritableDraft } from 'immer/dist/internal'; +import { AssistantIcon } from '@kbn/ai-assistant-icon'; +import { remove } from 'lodash'; +import { SecurityPageName } from '@kbn/deeplinks-security'; +import { ProductLine } from '../../common/product'; +import type { SecurityProductTypes } from '../../common/config'; + +export const shouldUseAINavigation = (productTypes: SecurityProductTypes) => { + return productTypes.some((productType) => productType.product_line === ProductLine.ai); +}; + +// Apply AI for SOC navigation tree changes. +// The navigation tree received by parameter is generated at: x-pack/solutions/security/plugins/security_solution/public/app/solution_navigation/navigation_tree.ts +// An example of static navigation tree: x-pack/solutions/observability/plugins/observability/public/navigation_tree.ts +// This is a temporary solution until the "classic" navigation is deprecated and the "generated" navigationTree is replaced by a static navigationTree (probably multiple of them). +export const applyAiSocNavigation = ( + draft: WritableDraft> +): void => { + const group = draft.body[0] as WritableDraft>; + const [attachDiscovery] = group.children.reduce>>( + (nodes, category) => { + const [attachDiscoveryNode] = remove(category.children ?? [], { + id: SecurityPageName.attackDiscovery, + }); + if (attachDiscoveryNode) { + nodes.push(attachDiscoveryNode); + } + return nodes; + }, + [] + ); + + if (attachDiscovery) { + group.appendHorizontalRule = true; // does not seem to work :( talk with sharedUx team + + const aiGroup: GroupDefinition = { + type: 'navGroup', + id: 'security_solution_ai_nav', + title: 'AI for SOC', + icon: AssistantIcon, + children: [attachDiscovery], + breadcrumbStatus: 'hidden', + defaultIsCollapsed: false, + isCollapsible: false, + }; + draft.body.push(aiGroup); + } +}; diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/index.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/index.ts index bc72913044f6a..af1914914e53a 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/index.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/index.ts @@ -6,15 +6,16 @@ */ import { APP_PATH } from '@kbn/security-solution-plugin/common'; +import type { SecurityProductTypes } from '../../common/config'; import type { Services } from '../common/services'; import { subscribeBreadcrumbs } from './breadcrumbs'; import { initSideNavigation } from './side_navigation'; import { enableManagementCardsLanding } from './management_cards'; -export const startNavigation = (services: Services) => { +export const startNavigation = (services: Services, productTypes: SecurityProductTypes) => { services.serverless.setProjectHome(APP_PATH); - initSideNavigation(services); + initSideNavigation(services, productTypes); enableManagementCardsLanding(services); subscribeBreadcrumbs(services); }; diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/side_navigation.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/side_navigation.ts index f27dbbfb9ec79..523e5f573318b 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/side_navigation.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/side_navigation.ts @@ -9,15 +9,21 @@ import { i18n } from '@kbn/i18n'; import type { AppDeepLinkId, GroupDefinition, NodeDefinition } from '@kbn/core-chrome-browser'; import produce from 'immer'; import { map } from 'rxjs'; +import type { SecurityProductTypes } from '../../common/config'; import { type Services } from '../common/services'; +import { applyAiSocNavigation, shouldUseAINavigation } from './ai_soc_navigation'; const PROJECT_SETTINGS_TITLE = i18n.translate( 'xpack.securitySolutionServerless.navLinks.projectSettings.title', { defaultMessage: 'Project Settings' } ); -export const initSideNavigation = async (services: Services) => { +export const initSideNavigation = async ( + services: Services, + productTypes: SecurityProductTypes +) => { services.securitySolution.setIsSolutionNavigationEnabled(true); + const showAINavigation = shouldUseAINavigation(productTypes); const { navigationTree$, panelContentProvider } = await services.securitySolution.getSolutionNavigation(); @@ -41,6 +47,10 @@ export const initSideNavigation = async (services: Services) => { footerGroup.title = PROJECT_SETTINGS_TITLE; footerGroup.children.push({ cloudLink: 'billingAndSub', openInNewTab: true }); } + + if (showAINavigation) { + applyAiSocNavigation(draft); + } }) ) ); diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/plugin.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/plugin.ts index a9743ef4fa51f..56f2a0ba8698c 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/plugin.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/plugin.ts @@ -72,7 +72,7 @@ export class SecuritySolutionServerlessPlugin }); setOnboardingSettings(services); - startNavigation(services); + startNavigation(services, productTypes); return {}; }