diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 092875c57fbd0..c88f5c7abd7ae 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -69,19 +69,34 @@ export const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d"'; export enum SecurityPageName { administration = 'administration', alerts = 'alerts', + authentications = 'authentications', case = 'case', + caseConfigure = 'case-configure', + caseCreate = 'case-create', detections = 'detections', endpoints = 'endpoints', eventFilters = 'event_filters', + events = 'events', exceptions = 'exceptions', + explore = 'explore', hosts = 'hosts', + hostsAnomalies = 'hosts-anomalies', + hostsExternalAlerts = 'hosts-external_alerts', + investigate = 'investigate', network = 'network', + networkAnomalies = 'network-anomalies', + networkDns = 'network-dns', + networkExternalAlerts = 'network-external_alerts', + networkHttp = 'network-http', + networkTls = 'network-tls', + timelines = 'timelines', + timelinesTemplates = 'timelines-templates', overview = 'overview', policies = 'policies', rules = 'rules', - timelines = 'timelines', trustedApps = 'trusted_apps', ueba = 'ueba', + uncommonProcesses = 'uncommon_processes', } export const TIMELINES_PATH = '/timelines'; diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts index 6dd086757776f..5bcc598c7be5e 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts @@ -4,68 +4,95 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getDeepLinks } from '.'; -import { Capabilities } from '../../../../../../src/core/public'; +import { getDeepLinks, PREMIUM_DEEP_LINK_IDS } from '.'; +import { AppDeepLink, Capabilities } from '../../../../../../src/core/public'; import { SecurityPageName } from '../types'; import { mockGlobalState } from '../../common/mock'; -describe('public search functions', () => { - it('returns a subset of links for basic license, full set for platinum', () => { +const findDeepLink = (id: string, deepLinks: AppDeepLink[]): AppDeepLink | null => + deepLinks.reduce((deepLinkFound: AppDeepLink | null, deepLink) => { + if (deepLinkFound !== null) { + return deepLinkFound; + } + if (deepLink.id === id) { + return deepLink; + } + if (deepLink.deepLinks) { + return findDeepLink(id, deepLink.deepLinks); + } + return null; + }, null); + +describe('deepLinks', () => { + it('should return a subset of links for basic license and the full set for platinum', () => { const basicLicense = 'basic'; const platinumLicense = 'platinum'; const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense); const platinumLinks = getDeepLinks(mockGlobalState.app.enableExperimental, platinumLicense); - basicLinks.forEach((basicLink, index) => { - const platinumLink = platinumLinks[index]; - expect(basicLink.id).toEqual(platinumLink.id); - const platinumDeepLinksCount = platinumLink.deepLinks?.length || 0; - const basicDeepLinksCount = basicLink.deepLinks?.length || 0; - expect(platinumDeepLinksCount).toBeGreaterThanOrEqual(basicDeepLinksCount); + const testAllBasicInPlatinum = ( + basicDeepLinks: AppDeepLink[], + platinumDeepLinks: AppDeepLink[] + ) => { + basicDeepLinks.forEach((basicDeepLink) => { + const platinumDeepLink = platinumDeepLinks.find(({ id }) => id === basicDeepLink.id); + expect(platinumDeepLink).toBeTruthy(); + + if (platinumDeepLink && basicDeepLink.deepLinks) { + expect(platinumDeepLink.deepLinks).toBeTruthy(); + + if (platinumDeepLink.deepLinks) { + testAllBasicInPlatinum(basicDeepLink.deepLinks, platinumDeepLink.deepLinks); + } + } + }); + }; + testAllBasicInPlatinum(basicLinks, platinumLinks); + + PREMIUM_DEEP_LINK_IDS.forEach((premiumDeepLinkId) => { + expect(findDeepLink(premiumDeepLinkId, platinumLinks)).toBeTruthy(); + expect(findDeepLink(premiumDeepLinkId, basicLinks)).toBeFalsy(); }); }); - it('returns case links for basic license with only read_cases capabilities', () => { + it('should return case links for basic license with only read_cases capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, { siem: { read_cases: true, crud_cases: false }, } as unknown as Capabilities); - expect(basicLinks.some((l) => l.id === SecurityPageName.case)).toBeTruthy(); + expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeTruthy(); }); - it('returns case links with NO deepLinks for basic license with only read_cases capabilities', () => { + it('should return case links with NO deepLinks for basic license with only read_cases capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, { siem: { read_cases: true, crud_cases: false }, } as unknown as Capabilities); - - expect( - basicLinks.find((l) => l.id === SecurityPageName.case)?.deepLinks?.length === 0 - ).toBeTruthy(); + expect(findDeepLink(SecurityPageName.case, basicLinks)?.deepLinks?.length === 0).toBeTruthy(); }); - it('returns case links with deepLinks for basic license with crud_cases capabilities', () => { + it('should return case links with deepLinks for basic license with crud_cases capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, { siem: { read_cases: true, crud_cases: true }, } as unknown as Capabilities); expect( - (basicLinks.find((l) => l.id === SecurityPageName.case)?.deepLinks?.length ?? 0) > 0 + (findDeepLink(SecurityPageName.case, basicLinks)?.deepLinks?.length ?? 0) > 0 ).toBeTruthy(); }); - it('returns NO case links for basic license with NO read_cases capabilities', () => { + it('should return NO case links for basic license with NO read_cases capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, { siem: { read_cases: false, crud_cases: false }, } as unknown as Capabilities); - expect(basicLinks.some((l) => l.id === SecurityPageName.case)).toBeFalsy(); + expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeFalsy(); }); - it('returns case links for basic license with undefined capabilities', () => { + it('should return case links for basic license with undefined capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks( mockGlobalState.app.enableExperimental, @@ -73,10 +100,10 @@ describe('public search functions', () => { undefined ); - expect(basicLinks.some((l) => l.id === SecurityPageName.case)).toBeTruthy(); + expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeTruthy(); }); - it('returns case deepLinks for basic license with undefined capabilities', () => { + it('should return case deepLinks for basic license with undefined capabilities', () => { const basicLicense = 'basic'; const basicLinks = getDeepLinks( mockGlobalState.app.enableExperimental, @@ -85,20 +112,20 @@ describe('public search functions', () => { ); expect( - (basicLinks.find((l) => l.id === SecurityPageName.case)?.deepLinks?.length ?? 0) > 0 + (findDeepLink(SecurityPageName.case, basicLinks)?.deepLinks?.length ?? 0) > 0 ).toBeTruthy(); }); - it('returns NO ueba link when enableExperimental.uebaEnabled === false', () => { + it('should return NO ueba link when enableExperimental.uebaEnabled === false', () => { const deepLinks = getDeepLinks(mockGlobalState.app.enableExperimental); - expect(deepLinks.some((l) => l.id === SecurityPageName.ueba)).toBeFalsy(); + expect(findDeepLink(SecurityPageName.ueba, deepLinks)).toBeFalsy(); }); - it('returns ueba link when enableExperimental.uebaEnabled === true', () => { + it('should return ueba link when enableExperimental.uebaEnabled === true', () => { const deepLinks = getDeepLinks({ ...mockGlobalState.app.enableExperimental, uebaEnabled: true, }); - expect(deepLinks.some((l) => l.id === SecurityPageName.ueba)).toBeTruthy(); + expect(findDeepLink(SecurityPageName.ueba, deepLinks)).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 9f13a8be0e13a..277c99217f32b 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { Subject } from 'rxjs'; import { LicenseType } from '../../../../licensing/common/types'; -import { SecurityDeepLinkName, SecurityDeepLinks, SecurityPageName } from '../types'; +import { SecurityPageName } from '../types'; import { AppDeepLink, ApplicationStart, @@ -22,7 +22,9 @@ import { ALERTS, RULES, EXCEPTIONS, + EXPLORE, HOSTS, + INVESTIGATE, NETWORK, TIMELINES, CASE, @@ -45,7 +47,13 @@ import { } from '../../../common/constants'; import { ExperimentalFeatures } from '../../../common/experimental_features'; -export const topDeepLinks: AppDeepLink[] = [ +export const PREMIUM_DEEP_LINK_IDS: Set = new Set([ + SecurityPageName.hostsAnomalies, + SecurityPageName.networkAnomalies, + SecurityPageName.caseConfigure, +]); + +export const securitySolutionsDeepLinks: AppDeepLink[] = [ { id: SecurityPageName.overview, title: OVERVIEW, @@ -68,86 +76,7 @@ export const topDeepLinks: AppDeepLink[] = [ defaultMessage: 'Detect', }), ], - }, - { - id: SecurityPageName.hosts, - title: HOSTS, - path: HOSTS_PATH, - navLinkStatus: AppNavLinkStatus.visible, - keywords: [ - i18n.translate('xpack.securitySolution.search.hosts', { - defaultMessage: 'Hosts', - }), - ], - order: 9002, - }, - { - id: SecurityPageName.network, - title: NETWORK, - path: NETWORK_PATH, - navLinkStatus: AppNavLinkStatus.visible, - keywords: [ - i18n.translate('xpack.securitySolution.search.network', { - defaultMessage: 'Network', - }), - ], - order: 9003, - }, - { - id: SecurityPageName.ueba, - title: UEBA, - path: UEBA_PATH, - navLinkStatus: AppNavLinkStatus.visible, - keywords: [ - i18n.translate('xpack.securitySolution.search.ueba', { - defaultMessage: 'Users & Entities', - }), - ], - order: 9004, - }, - { - id: SecurityPageName.timelines, - title: TIMELINES, - path: TIMELINES_PATH, - navLinkStatus: AppNavLinkStatus.visible, - keywords: [ - i18n.translate('xpack.securitySolution.search.timelines', { - defaultMessage: 'Timelines', - }), - ], - order: 9005, - }, - { - id: SecurityPageName.case, - title: CASE, - path: CASES_PATH, - navLinkStatus: AppNavLinkStatus.visible, - keywords: [ - i18n.translate('xpack.securitySolution.search.cases', { - defaultMessage: 'Cases', - }), - ], - order: 9006, - }, - { - id: SecurityPageName.administration, - title: MANAGE, - path: ENDPOINTS_PATH, - navLinkStatus: AppNavLinkStatus.hidden, - keywords: [ - i18n.translate('xpack.securitySolution.search.manage', { - defaultMessage: 'Manage', - }), - ], - }, -]; - -const nestedDeepLinks: SecurityDeepLinks = { - [SecurityPageName.overview]: { - base: [], - }, - [SecurityPageName.detections]: { - base: [ + deepLinks: [ { id: SecurityPageName.alerts, title: ALERTS, @@ -187,124 +116,200 @@ const nestedDeepLinks: SecurityDeepLinks = { }, ], }, - [SecurityPageName.hosts]: { - base: [ - { - id: 'authentications', - title: i18n.translate('xpack.securitySolution.search.hosts.authentications', { - defaultMessage: 'Authentications', - }), - path: `${HOSTS_PATH}/authentications`, - }, - { - id: 'uncommonProcesses', - title: i18n.translate('xpack.securitySolution.search.hosts.uncommonProcesses', { - defaultMessage: 'Uncommon Processes', - }), - path: `${HOSTS_PATH}/uncommonProcesses`, - }, - { - id: 'events', - title: i18n.translate('xpack.securitySolution.search.hosts.events', { - defaultMessage: 'Events', - }), - path: `${HOSTS_PATH}/events`, - }, - { - id: 'externalAlerts', - title: i18n.translate('xpack.securitySolution.search.hosts.externalAlerts', { - defaultMessage: 'External Alerts', - }), - path: `${HOSTS_PATH}/externalAlerts`, - }, - ], - premium: [ - { - id: 'anomalies', - title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { - defaultMessage: 'Anomalies', - }), - path: `${HOSTS_PATH}/anomalies`, - }, + { + id: SecurityPageName.explore, + title: EXPLORE, + navLinkStatus: AppNavLinkStatus.hidden, + keywords: [ + i18n.translate('xpack.securitySolution.search.explore', { + defaultMessage: 'Explore', + }), ], - }, - [SecurityPageName.network]: { - base: [ - { - id: 'dns', - title: i18n.translate('xpack.securitySolution.search.network.dns', { - defaultMessage: 'DNS', - }), - path: `${NETWORK_PATH}/dns`, - }, - { - id: 'http', - title: i18n.translate('xpack.securitySolution.search.network.http', { - defaultMessage: 'HTTP', - }), - path: `${NETWORK_PATH}/http`, - }, + deepLinks: [ { - id: 'tls', - title: i18n.translate('xpack.securitySolution.search.network.tls', { - defaultMessage: 'TLS', - }), - path: `${NETWORK_PATH}/tls`, + id: SecurityPageName.hosts, + title: HOSTS, + path: HOSTS_PATH, + navLinkStatus: AppNavLinkStatus.visible, + keywords: [ + i18n.translate('xpack.securitySolution.search.hosts', { + defaultMessage: 'Hosts', + }), + ], + order: 9002, + deepLinks: [ + { + id: SecurityPageName.authentications, + title: i18n.translate('xpack.securitySolution.search.hosts.authentications', { + defaultMessage: 'Authentications', + }), + path: `${HOSTS_PATH}/authentications`, + }, + { + id: SecurityPageName.uncommonProcesses, + title: i18n.translate('xpack.securitySolution.search.hosts.uncommonProcesses', { + defaultMessage: 'Uncommon Processes', + }), + path: `${HOSTS_PATH}/uncommonProcesses`, + }, + { + id: SecurityPageName.events, + title: i18n.translate('xpack.securitySolution.search.hosts.events', { + defaultMessage: 'Events', + }), + path: `${HOSTS_PATH}/events`, + }, + { + id: SecurityPageName.hostsExternalAlerts, + title: i18n.translate('xpack.securitySolution.search.hosts.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${HOSTS_PATH}/externalAlerts`, + }, + { + id: SecurityPageName.hostsAnomalies, + title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${HOSTS_PATH}/anomalies`, + }, + ], }, { - id: 'externalAlertsNetwork', - title: i18n.translate('xpack.securitySolution.search.network.externalAlerts', { - defaultMessage: 'External Alerts', - }), - path: `${NETWORK_PATH}/external-alerts`, - }, - ], - premium: [ - { - id: 'anomalies', - title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { - defaultMessage: 'Anomalies', - }), - path: `${NETWORK_PATH}/anomalies`, + id: SecurityPageName.network, + title: NETWORK, + path: NETWORK_PATH, + navLinkStatus: AppNavLinkStatus.visible, + keywords: [ + i18n.translate('xpack.securitySolution.search.network', { + defaultMessage: 'Network', + }), + ], + order: 9003, + deepLinks: [ + { + id: SecurityPageName.networkDns, + title: i18n.translate('xpack.securitySolution.search.network.dns', { + defaultMessage: 'DNS', + }), + path: `${NETWORK_PATH}/dns`, + }, + { + id: SecurityPageName.networkHttp, + title: i18n.translate('xpack.securitySolution.search.network.http', { + defaultMessage: 'HTTP', + }), + path: `${NETWORK_PATH}/http`, + }, + { + id: SecurityPageName.networkTls, + title: i18n.translate('xpack.securitySolution.search.network.tls', { + defaultMessage: 'TLS', + }), + path: `${NETWORK_PATH}/tls`, + }, + { + id: SecurityPageName.networkExternalAlerts, + title: i18n.translate('xpack.securitySolution.search.network.externalAlerts', { + defaultMessage: 'External Alerts', + }), + path: `${NETWORK_PATH}/external-alerts`, + }, + { + id: SecurityPageName.networkAnomalies, + title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', { + defaultMessage: 'Anomalies', + }), + path: `${NETWORK_PATH}/anomalies`, + }, + ], }, ], }, - [SecurityPageName.ueba]: { - base: [], - }, - [SecurityPageName.timelines]: { - base: [ - { - id: 'timelineTemplates', - title: i18n.translate('xpack.securitySolution.search.timeline.templates', { - defaultMessage: 'Templates', - }), - path: `${TIMELINES_PATH}/template`, - }, + { + id: SecurityPageName.ueba, + title: UEBA, + path: UEBA_PATH, + navLinkStatus: AppNavLinkStatus.visible, + keywords: [ + i18n.translate('xpack.securitySolution.search.ueba', { + defaultMessage: 'Users & Entities', + }), ], + order: 9004, }, - [SecurityPageName.case]: { - base: [ + { + id: SecurityPageName.investigate, + title: INVESTIGATE, + navLinkStatus: AppNavLinkStatus.hidden, + keywords: [ + i18n.translate('xpack.securitySolution.search.investigate', { + defaultMessage: 'Investigate', + }), + ], + deepLinks: [ { - id: 'create', - title: i18n.translate('xpack.securitySolution.search.cases.create', { - defaultMessage: 'Create New Case', - }), - path: `${CASES_PATH}/create`, + id: SecurityPageName.timelines, + title: TIMELINES, + path: TIMELINES_PATH, + navLinkStatus: AppNavLinkStatus.visible, + keywords: [ + i18n.translate('xpack.securitySolution.search.timelines', { + defaultMessage: 'Timelines', + }), + ], + order: 9005, + deepLinks: [ + { + id: SecurityPageName.timelinesTemplates, + title: i18n.translate('xpack.securitySolution.search.timeline.templates', { + defaultMessage: 'Templates', + }), + path: `${TIMELINES_PATH}/template`, + }, + ], }, - ], - premium: [ { - id: 'configure', - title: i18n.translate('xpack.securitySolution.search.cases.configure', { - defaultMessage: 'Configure Cases', - }), - path: `${CASES_PATH}/configure`, + id: SecurityPageName.case, + title: CASE, + path: CASES_PATH, + navLinkStatus: AppNavLinkStatus.visible, + keywords: [ + i18n.translate('xpack.securitySolution.search.cases', { + defaultMessage: 'Cases', + }), + ], + order: 9006, + deepLinks: [ + { + id: SecurityPageName.caseCreate, + title: i18n.translate('xpack.securitySolution.search.cases.create', { + defaultMessage: 'Create New Case', + }), + path: `${CASES_PATH}/create`, + }, + { + id: SecurityPageName.caseConfigure, + title: i18n.translate('xpack.securitySolution.search.cases.configure', { + defaultMessage: 'Configure Cases', + }), + path: `${CASES_PATH}/configure`, + }, + ], }, ], }, - [SecurityPageName.administration]: { - base: [ + { + id: SecurityPageName.administration, + title: MANAGE, + path: ENDPOINTS_PATH, + navLinkStatus: AppNavLinkStatus.hidden, + keywords: [ + i18n.translate('xpack.securitySolution.search.manage', { + defaultMessage: 'Manage', + }), + ], + deepLinks: [ { id: SecurityPageName.endpoints, navLinkStatus: AppNavLinkStatus.visible, @@ -330,7 +335,7 @@ const nestedDeepLinks: SecurityDeepLinks = { }, ], }, -}; +]; /** * A function that generates the plugin deepLinks structure @@ -344,42 +349,44 @@ export function getDeepLinks( licenseType?: LicenseType, capabilities?: ApplicationStart['capabilities'] ): AppDeepLink[] { - return topDeepLinks - .filter( - (deepLink) => - (deepLink.id !== SecurityPageName.case && deepLink.id !== SecurityPageName.ueba) || // is not cases or ueba - (deepLink.id === SecurityPageName.case && - (capabilities == null || capabilities.siem.read_cases === true)) || // is cases with at least read only caps - (deepLink.id === SecurityPageName.ueba && enableExperimental.uebaEnabled) // is ueba with ueba feature flag enabled - ) - .map((deepLink) => { - const deepLinkId = deepLink.id as SecurityDeepLinkName; - const subPluginDeepLinks = nestedDeepLinks[deepLinkId]; - const baseDeepLinks = Array.isArray(subPluginDeepLinks.base) - ? [...subPluginDeepLinks.base] - : []; - if ( - deepLinkId === SecurityPageName.case && - capabilities != null && - capabilities.siem.crud_cases === false - ) { - return { - ...deepLink, - deepLinks: [], - }; - } + const isPremium = isPremiumLicense(licenseType); + + const filterDeepLinks = (deepLinks: AppDeepLink[]): AppDeepLink[] => { + return deepLinks + .filter((deepLink) => { + if (!isPremium && PREMIUM_DEEP_LINK_IDS.has(deepLink.id)) { + return false; + } + if (deepLink.id === SecurityPageName.case) { + return capabilities == null || capabilities.siem.read_cases === true; + } + if (deepLink.id === SecurityPageName.ueba) { + return enableExperimental.uebaEnabled; + } + return true; + }) + .map((deepLink) => { + if ( + deepLink.id === SecurityPageName.case && + capabilities != null && + capabilities.siem.crud_cases === false + ) { + return { + ...deepLink, + deepLinks: [], + }; + } + if (deepLink.deepLinks) { + return { + ...deepLink, + deepLinks: filterDeepLinks(deepLink.deepLinks), + }; + } + return deepLink; + }); + }; - if (isPremiumLicense(licenseType) && subPluginDeepLinks?.premium) { - return { - ...deepLink, - deepLinks: [...baseDeepLinks, ...subPluginDeepLinks.premium], - }; - } - return { - ...deepLink, - deepLinks: baseDeepLinks, - }; - }); + return filterDeepLinks(securitySolutionsDeepLinks); } export function isPremiumLicense(licenseType?: LicenseType): boolean { diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 490ff8936c18c..81f6de296d6a5 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -17,7 +17,7 @@ import { CombinedState, } from 'redux'; import { RouteProps } from 'react-router-dom'; -import { AppMountParameters, AppDeepLink } from '../../../../../src/core/public'; +import { AppMountParameters } from '../../../../../src/core/public'; import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; import { StartedSubPlugins, StartServices } from '../types'; @@ -35,7 +35,7 @@ import { State, SubPluginsInitReducer } from '../common/store'; import { Immutable } from '../../common/endpoint/types'; import { AppAction } from '../common/store/actions'; import { TimelineState } from '../timelines/store/timeline/types'; -import { SecurityPageName } from '../../common/constants'; + export { SecurityPageName } from '../../common/constants'; export interface SecuritySubPluginStore { @@ -60,23 +60,6 @@ export type SecuritySubPluginKeyStore = | 'alertList' | 'management'; -export type SecurityDeepLinkName = - | SecurityPageName.administration - | SecurityPageName.case - | SecurityPageName.detections - | SecurityPageName.hosts - | SecurityPageName.network - | SecurityPageName.overview - | SecurityPageName.timelines - | SecurityPageName.ueba; - -interface SecurityDeepLink { - base: AppDeepLink[]; - premium?: AppDeepLink[]; -} - -export type SecurityDeepLinks = { [key in SecurityDeepLinkName]: SecurityDeepLink }; - /** * Returned by the various 'SecuritySubPlugin' classes from the `start` method. */