diff --git a/src/platform/packages/shared/deeplinks/security/deep_links.ts b/src/platform/packages/shared/deeplinks/security/deep_links.ts index 6d9e1138e078c..a0fcd8f55da09 100644 --- a/src/platform/packages/shared/deeplinks/security/deep_links.ts +++ b/src/platform/packages/shared/deeplinks/security/deep_links.ts @@ -65,6 +65,7 @@ export enum SecurityPageName { rulesAdd = 'rules-add', rulesCreate = 'rules-create', rulesLanding = 'rules-landing', + rulesManagement = 'rules-management', siemMigrationsRules = 'siem_migrations-rules', /* * Warning: Computed values are not permitted in an enum with string valued members diff --git a/x-pack/solutions/security/plugins/security_solution/common/constants.ts b/x-pack/solutions/security/plugins/security_solution/common/constants.ts index c4bb9290ddb64..eda49eae164d0 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/constants.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/constants.ts @@ -101,6 +101,7 @@ export const RULES_LANDING_PATH = `${RULES_PATH}/landing` as const; export const RULES_ADD_PATH = `${RULES_PATH}/add_rules` as const; export const RULES_UPDATES = `${RULES_PATH}/updates` as const; export const RULES_CREATE_PATH = `${RULES_PATH}/create` as const; +export const RULES_MANAGEMENT_PATH = `${RULES_PATH}/management` 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; diff --git a/x-pack/solutions/security/plugins/security_solution/public/configurations/tabs/promotion_rules/promotion_rules_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/configurations/tabs/promotion_rules/promotion_rules_table.tsx index 5aadf25ab5d47..402a2833ac6af 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/configurations/tabs/promotion_rules/promotion_rules_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/configurations/tabs/promotion_rules/promotion_rules_table.tsx @@ -14,7 +14,6 @@ import { EuiSpacer, EuiTab, EuiTabs, - EuiText, } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import type { FindRulesSortField } from '../../../../common/api/detection_engine'; @@ -201,7 +200,6 @@ const useRulesColumns = ({ currentTab }: ColumnsProps): Array {value}, width: '30%', } as EuiBasicTableColumn, INDEXING_DURATION_COLUMN, @@ -217,7 +215,6 @@ const useRulesColumns = ({ currentTab }: ColumnsProps): Array {value}, width: '100%', } as EuiBasicTableColumn, LAST_EXECUTION_COLUMN, 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 2b113abc85212..43b9842aca6cb 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 @@ -12,6 +12,7 @@ import { RULES_ADD_PATH, RULES_CREATE_PATH, RULES_LANDING_PATH, + RULES_MANAGEMENT_PATH, RULES_PATH, SECURITY_FEATURE_ID, } from '../../common/constants'; @@ -38,7 +39,7 @@ export const links: LinkItem = { hideTimeline: true, skipUrlState: true, globalNavPosition: 2, - capabilities: [[`${SECURITY_FEATURE_ID}.show`, `${SECURITY_FEATURE_ID}.detections`]], + capabilities: `${SECURITY_FEATURE_ID}.show`, links: [ { id: SecurityPageName.rules, @@ -53,13 +54,24 @@ export const links: LinkItem = { defaultMessage: 'SIEM Rules', }), ], + capabilities: `${SECURITY_FEATURE_ID}.show`, links: [ + { + id: SecurityPageName.rulesManagement, + title: SIEM_RULES, + path: RULES_MANAGEMENT_PATH, + globalSearchDisabled: true, + skipUrlState: true, + hideTimeline: true, + capabilities: `${SECURITY_FEATURE_ID}.detections`, + }, { id: SecurityPageName.rulesAdd, title: ADD_RULES, path: RULES_ADD_PATH, skipUrlState: true, hideTimeline: true, + capabilities: `${SECURITY_FEATURE_ID}.detections`, }, { id: SecurityPageName.rulesCreate, @@ -67,6 +79,7 @@ export const links: LinkItem = { path: RULES_CREATE_PATH, skipUrlState: true, hideTimeline: false, + capabilities: `${SECURITY_FEATURE_ID}.detections`, }, ], }, @@ -100,7 +113,7 @@ export const links: LinkItem = { } ), path: COVERAGE_OVERVIEW_PATH, - capabilities: [`${SECURITY_FEATURE_ID}.show`], + capabilities: `${SECURITY_FEATURE_ID}.detections`, globalSearchKeywords: [ i18n.translate('xpack.securitySolution.appLinks.coverageOverviewDashboard', { defaultMessage: 'MITRE ATT&CK Coverage', diff --git a/x-pack/solutions/security/plugins/security_solution/public/rules/routes.tsx b/x-pack/solutions/security/plugins/security_solution/public/rules/routes.tsx index a08be19ac327e..557d4d58e31ed 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/rules/routes.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/rules/routes.tsx @@ -4,16 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { Redirect } from 'react-router-dom'; import { Routes, Route } from '@kbn/shared-ux-router'; -import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; +import type { Capabilities } from '@kbn/core-capabilities-common'; import * as i18n from './translations'; import { COVERAGE_OVERVIEW_PATH, RULES_LANDING_PATH, RULES_PATH, + SECURITY_FEATURE_ID, SecurityPageName, } from '../../common/constants'; import { NotFoundPage } from '../app/404'; @@ -30,80 +31,98 @@ import type { SecuritySubPluginRoutes } from '../app/types'; import { RulesLandingPage } from './landing'; import { CoverageOverviewPage } from '../detection_engine/rule_management_ui/pages/coverage_overview'; import { RuleDetailTabs } from '../detection_engine/rule_details_ui/pages/rule_details/use_rule_details_tabs'; -import { - SecurityRoutePageWrapper, - withSecurityRoutePageWrapper, -} from '../common/components/security_route_page_wrapper'; +import { withSecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper'; +import { hasCapabilities } from '../common/lib/capabilities'; +import { useKibana } from '../common/lib/kibana/kibana_react'; -const RulesSubRoutes = [ - { - path: '/rules/id/:detailName/edit', - main: EditRulePage, - exact: true, - }, - { - path: `/rules/id/:detailName/:tabName(${RuleDetailTabs.alerts}|${RuleDetailTabs.exceptions}|${RuleDetailTabs.endpointExceptions}|${RuleDetailTabs.executionResults}|${RuleDetailTabs.executionEvents})`, - main: RuleDetailsPage, - exact: true, - }, +const getRulesSubRoutes = (capabilities: Capabilities) => [ + ...(hasCapabilities(capabilities, `${SECURITY_FEATURE_ID}.detections`) // regular detection rules are enabled + ? [ + { + path: '/rules/id/:detailName/edit', + main: EditRulePage, + exact: true, + }, + ] + : []), + ...(hasCapabilities(capabilities, [ + `${SECURITY_FEATURE_ID}.detections`, + `${SECURITY_FEATURE_ID}.external_detections`, + ]) // some detection capability is enabled + ? [ + { + path: `/rules/id/:detailName/:tabName(${RuleDetailTabs.alerts}|${RuleDetailTabs.exceptions}|${RuleDetailTabs.endpointExceptions}|${RuleDetailTabs.executionResults}|${RuleDetailTabs.executionEvents})`, + main: RuleDetailsPage, + exact: true, + }, + ] + : []), { path: '/rules/create', - main: CreateRulePage, + main: withSecurityRoutePageWrapper(CreateRulePage, SecurityPageName.rulesCreate, { + redirectOnMissing: true, + omitSpyRoute: true, + }), exact: true, }, { path: `/rules/:tabName(${AllRulesTabs.management}|${AllRulesTabs.monitoring}|${AllRulesTabs.updates})`, - main: RulesPage, + main: withSecurityRoutePageWrapper(RulesPage, SecurityPageName.rulesManagement, { + redirectOnMissing: true, + omitSpyRoute: true, + }), exact: true, }, { path: '/rules/add_rules', - main: AddRulesPage, + main: withSecurityRoutePageWrapper(AddRulesPage, SecurityPageName.rulesAdd, { + redirectOnMissing: true, + omitSpyRoute: true, + }), exact: true, }, ]; const RulesContainerComponent: React.FC = () => { useReadonlyHeader(i18n.READ_ONLY_BADGE_TOOLTIP); + const { capabilities } = useKibana().services.application; + + const subRoutes = useMemo(() => { + return getRulesSubRoutes(capabilities).map((route) => ( + + + + )); + }, [capabilities]); return ( - - - ( - - )} - /> - - - - {RulesSubRoutes.map((route) => ( - - - - ))} - - - - + + ( + + )} + /> + + + + {subRoutes} + + + ); }; @@ -112,9 +131,7 @@ const Rules = React.memo(RulesContainerComponent); const CoverageOverviewRoutes = () => ( - - - + ); @@ -123,11 +140,15 @@ export const routes: SecuritySubPluginRoutes = [ path: RULES_LANDING_PATH, component: withSecurityRoutePageWrapper(RulesLandingPage, SecurityPageName.rulesLanding, { redirectOnMissing: true, + omitSpyRoute: true, }), }, { path: RULES_PATH, - component: Rules, + component: withSecurityRoutePageWrapper(Rules, SecurityPageName.rules, { + redirectOnMissing: true, + omitSpyRoute: true, + }), }, { path: COVERAGE_OVERVIEW_PATH, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/ai4dsoc/capabilities/access.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/ai4dsoc/capabilities/access.cy.ts index 34537f1e695c5..1ad23d0d0c014 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/ai4dsoc/capabilities/access.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/ai4dsoc/capabilities/access.cy.ts @@ -12,7 +12,7 @@ import { ALERT_SUMMARY_URL, ALERTS_URL, MAINTENANCE_WINDOW_URL, - RULES_LANDING_URL, + RULES_URL, STACK_RULES_URL, } from '../../../urls/navigation'; @@ -98,7 +98,7 @@ describe('Capabilities', { tags: '@serverless' }, () => { }); it('should redirect from rules to get started page', () => { - visit(RULES_LANDING_URL); + visit(RULES_URL); cy.get(GET_STARTED_PAGE).should('exist'); }); diff --git a/x-pack/test/security_solution_cypress/cypress/urls/navigation.ts b/x-pack/test/security_solution_cypress/cypress/urls/navigation.ts index 56213ee120e87..3025d7de7e325 100644 --- a/x-pack/test/security_solution_cypress/cypress/urls/navigation.ts +++ b/x-pack/test/security_solution_cypress/cypress/urls/navigation.ts @@ -23,7 +23,7 @@ export const CSP_BENCHMARKS_URL = '/app/security/cloud_security_posture/benchmar export const CSP_DASHBOARD_URL = '/app/security/cloud_security_posture/dashboard'; export const CSP_FINDINGS_URL = '/app/security/cloud_security_posture/findings/configurations'; -export const RULES_LANDING_URL = '/app/security/rules/landing'; +export const RULES_URL = '/app/security/rules'; export const RULES_COVERAGE_URL = '/app/security/rules_coverage_overview'; export const HOSTS_URL = '/app/security/hosts/events';