diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index aec2bc40a4824e..0981293a1607ab 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -97,7 +97,6 @@ export enum SecurityPageName {
endpoints = 'endpoints',
eventFilters = 'event_filters',
exceptions = 'exceptions',
- sharedExceptionListDetails = 'shared-exception-list-details',
exploreLanding = 'explore',
hostIsolationExceptions = 'host_isolation_exceptions',
hosts = 'hosts',
@@ -150,6 +149,7 @@ export const ALERTS_PATH = '/alerts' as const;
export const RULES_PATH = '/rules' as const;
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 USERS_PATH = '/users' as const;
export const KUBERNETES_PATH = '/kubernetes' as const;
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 8848f7bddc3d95..170e06742dcb0c 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
@@ -234,15 +234,6 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
defaultMessage: 'Exception lists',
}),
],
- deepLinks: [
- {
- id: SecurityPageName.sharedExceptionListDetails,
- title: 'List Details',
- path: '/exceptions/shared/:exceptionListId',
- navLinkStatus: AppNavLinkStatus.hidden,
- searchable: false,
- },
- ],
},
],
},
diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
index 84aec148913280..0cb13b5bcc4a8b 100644
--- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
+++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts
@@ -133,6 +133,11 @@ const rulesBReadcrumb = {
href: 'securitySolutionUI/rules',
};
+const exceptionsBReadcrumb = {
+ text: 'Rule Exceptions',
+ href: 'securitySolutionUI/exceptions',
+};
+
const manageBreadcrumbs = {
text: 'Manage',
href: 'securitySolutionUI/administration',
@@ -433,6 +438,32 @@ describe('Navigation Breadcrumbs', () => {
},
]);
});
+
+ test('should return Exceptions breadcrumbs when supplied exception Details pageName', () => {
+ const mockListName = 'new shared list';
+ const breadcrumbs = getBreadcrumbsForRoute(
+ {
+ ...getMockObject(
+ SecurityPageName.exceptions,
+ `/exceptions/details/${mockListName}`,
+ undefined
+ ),
+ state: {
+ listName: mockListName,
+ },
+ },
+ getSecuritySolutionUrl,
+ false
+ );
+ expect(breadcrumbs).toEqual([
+ securityBreadCrumb,
+ exceptionsBReadcrumb,
+ {
+ text: mockListName,
+ href: ``,
+ },
+ ]);
+ });
});
describe('setBreadcrumbs()', () => {
@@ -773,6 +804,31 @@ describe('Navigation Breadcrumbs', () => {
},
]);
});
+ test('should return Exceptions breadcrumbs when supplied exception Details pageName', () => {
+ const mockListName = 'new shared list';
+ const breadcrumbs = getBreadcrumbsForRoute(
+ {
+ ...getMockObject(
+ SecurityPageName.exceptions,
+ `/exceptions/details/${mockListName}`,
+ undefined
+ ),
+ state: {
+ listName: mockListName,
+ },
+ },
+ getSecuritySolutionUrl,
+ false
+ );
+ expect(breadcrumbs).toEqual([
+ securityBreadCrumb,
+ exceptionsBReadcrumb,
+ {
+ text: mockListName,
+ href: ``,
+ },
+ ]);
+ });
});
describe('setBreadcrumbs()', () => {
diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts
index afcaff3f3d0658..290f991b76ed83 100644
--- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts
+++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts
@@ -13,6 +13,7 @@ import type { StartServices } from '../../../../types';
import { getTrailingBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../../hosts/pages/details/utils';
import { getTrailingBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../../network/pages/details';
import { getTrailingBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../../detections/pages/detection_engine/rules/utils';
+import { getTrailingBreadcrumbs as geExceptionsBreadcrumbs } from '../../../../exceptions/utils/pages.utils';
import { getTrailingBreadcrumbs as getUsersBreadcrumbs } from '../../../../users/pages/details/utils';
import { getTrailingBreadcrumbs as getKubernetesBreadcrumbs } from '../../../../kubernetes/pages/utils/breadcrumbs';
import { getTrailingBreadcrumbs as getAlertDetailBreadcrumbs } from '../../../../detections/pages/alert_details/utils/breadcrumbs';
@@ -131,6 +132,8 @@ const getTrailingBreadcrumbsForRoutes = (
return getDetectionRulesBreadcrumbs(spyState, getSecuritySolutionUrl);
}
+ if (isExceptionRoutes(spyState)) return geExceptionsBreadcrumbs(spyState, getSecuritySolutionUrl);
+
if (isKubernetesRoutes(spyState)) {
return getKubernetesBreadcrumbs(spyState, getSecuritySolutionUrl);
}
@@ -162,6 +165,9 @@ const isRulesRoutes = (spyState: RouteSpyState): spyState is AdministrationRoute
spyState.pageName === SecurityPageName.rules ||
spyState.pageName === SecurityPageName.rulesCreate;
+const isExceptionRoutes = (spyState: RouteSpyState) =>
+ spyState.pageName === SecurityPageName.exceptions;
+
const isCloudSecurityPostureManagedRoutes = (spyState: RouteSpyState) =>
spyState.pageName === SecurityPageName.cloudSecurityPostureRules;
diff --git a/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx b/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx
index 746f49b3732840..3d7a0c500edace 100644
--- a/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx
+++ b/x-pack/plugins/security_solution/public/exceptions/hooks/use_exceptions_list.card/index.tsx
@@ -163,8 +163,8 @@ export const useExceptionsListCard = ({
// routes to x-pack/plugins/security_solution/public/exceptions/routes.tsx
const { onClick: goToExceptionDetail } = useGetSecuritySolutionLinkProps()({
- deepLinkId: SecurityPageName.sharedExceptionListDetails,
- path: `/exceptions/shared/${exceptionsList.list_id}`,
+ deepLinkId: SecurityPageName.exceptions,
+ path: `/details/${exceptionsList.list_id}`,
});
return {
listId,
diff --git a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts
index 9342ec06df291d..0e574c8b19039f 100644
--- a/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts
+++ b/x-pack/plugins/security_solution/public/exceptions/hooks/use_list_detail_view/index.ts
@@ -53,8 +53,8 @@ export const useListDetailsView = () => {
const { exportExceptionList, deleteExceptionList } = useApi(http);
- const { exceptionListId } = useParams<{
- exceptionListId: string;
+ const { detailName: exceptionListId } = useParams<{
+ detailName: string;
}>();
const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData();
diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx
index 7697ba01427d36..cd140a1160a440 100644
--- a/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx
+++ b/x-pack/plugins/security_solution/public/exceptions/pages/list_detail_view/index.tsx
@@ -4,7 +4,7 @@
* 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 type { FC } from 'react';
import {
@@ -13,6 +13,8 @@ import {
ViewerStatus,
} from '@kbn/securitysolution-exception-list-components';
import { EuiLoadingContent } from '@elastic/eui';
+import { SecurityPageName } from '../../../../common/constants';
+import { SpyRoute } from '../../../common/utils/route/spy_routes';
import { ReferenceErrorModal } from '../../../detections/components/value_lists_management_flyout/reference_error_modal';
import type { Rule } from '../../../detection_engine/rule_management/logic/types';
import { MissingPrivilegesCallOut } from '../../../detections/components/callouts/missing_privileges_callout';
@@ -53,53 +55,90 @@ export const ListsDetailViewComponent: FC = () => {
handleReferenceDelete,
} = useListDetailsView();
- if (viewerStatus === ViewerStatus.ERROR)
- return ;
+ const detailsViewContent = useMemo(() => {
+ if (viewerStatus === ViewerStatus.ERROR)
+ return ;
- if (isLoading) return ;
+ if (isLoading) return ;
- if (invalidListId || !listName || !list) return ;
- return (
- <>
-
-
+ if (invalidListId || !listName || !list) return ;
+ return (
+ <>
+
+
-
-
-
- {showManageRulesFlyout ? (
-
+
+
- ) : null}
+ {showManageRulesFlyout ? (
+
+ ) : null}
+ >
+ );
+ }, [
+ canUserEditList,
+ disableManageButton,
+ exportedList,
+ headerBackOptions,
+ invalidListId,
+ isLoading,
+ isReadOnly,
+ linkedRules,
+ list,
+ listDescription,
+ listId,
+ listName,
+ referenceModalState.contentText,
+ referenceModalState.rulesReferences,
+ refreshExceptions,
+ showManageButtonLoader,
+ showManageRulesFlyout,
+ showReferenceErrorModal,
+ viewerStatus,
+ onCancelManageRules,
+ onEditListDetails,
+ onExportList,
+ onManageRules,
+ onRuleSelectionChange,
+ onSaveManageRules,
+ handleCloseReferenceErrorModal,
+ handleDelete,
+ handleReferenceDelete,
+ ]);
+ return (
+ <>
+
+ {detailsViewContent}
>
);
};
diff --git a/x-pack/plugins/security_solution/public/exceptions/routes.tsx b/x-pack/plugins/security_solution/public/exceptions/routes.tsx
index 13cc29411f97c0..dd94e51f6c33ba 100644
--- a/x-pack/plugins/security_solution/public/exceptions/routes.tsx
+++ b/x-pack/plugins/security_solution/public/exceptions/routes.tsx
@@ -10,7 +10,11 @@ import { Route } from '@kbn/kibana-react-plugin/public';
import { TrackApplicationView } from '@kbn/usage-collection-plugin/public';
import * as i18n from './translations';
-import { EXCEPTIONS_PATH, SecurityPageName } from '../../common/constants';
+import {
+ EXCEPTIONS_PATH,
+ SecurityPageName,
+ EXCEPTION_LIST_DETAIL_PATH,
+} from '../../common/constants';
import { SharedLists, ListsDetailView } from './pages';
import { SpyRoute } from '../common/utils/route/spy_routes';
@@ -29,9 +33,8 @@ const ExceptionsRoutes = () => (
const ExceptionsListDetailRoute = () => (
-
+
-
);
@@ -42,7 +45,7 @@ const ExceptionsContainerComponent: React.FC = () => {
return (
-
+
);
diff --git a/x-pack/plugins/security_solution/public/exceptions/utils/pages.utils.ts b/x-pack/plugins/security_solution/public/exceptions/utils/pages.utils.ts
new file mode 100644
index 00000000000000..9c1a3289aca6fe
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/exceptions/utils/pages.utils.ts
@@ -0,0 +1,30 @@
+/*
+ * 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 { ChromeBreadcrumb } from '@kbn/core/public';
+import { EXCEPTIONS_PATH } from '../../../common/constants';
+import type { GetSecuritySolutionUrl } from '../../common/components/link_to';
+import type { RouteSpyState } from '../../common/utils/route/types';
+
+const isListDetailPage = (pathname: string) =>
+ pathname.includes(EXCEPTIONS_PATH) && pathname.includes('/details');
+
+export const getTrailingBreadcrumbs = (
+ params: RouteSpyState,
+ getSecuritySolutionUrl: GetSecuritySolutionUrl
+): ChromeBreadcrumb[] => {
+ let breadcrumb: ChromeBreadcrumb[] = [];
+
+ if (isListDetailPage(params.pathName) && params.state?.listName) {
+ breadcrumb = [
+ ...breadcrumb,
+ {
+ text: params.state.listName,
+ },
+ ];
+ }
+ return breadcrumb;
+};