diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index e313796fe9559..68f4308384649 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -14,12 +14,11 @@ export type ExperimentalFeatures = { [K in keyof typeof allowedExperimentalValue export const allowedExperimentalValues = Object.freeze({ tGridEnabled: true, tGridEventRenderedViewEnabled: true, + + // FIXME:PT delete? excludePoliciesInFilterEnabled: false, + kubernetesEnabled: true, - disableIsolationUIPendingStatuses: false, - pendingActionResponsesWithAck: true, - policyListEnabled: true, - policyResponseInFleetEnabled: true, chartEmbeddablesEnabled: true, donutChartEmbeddablesEnabled: false, // Depends on https://github.com/elastic/kibana/issues/136409 item 2 - 6 alertsPreviewChartEmbeddablesEnabled: false, // Depends on https://github.com/elastic/kibana/issues/136409 item 9 @@ -33,11 +32,6 @@ export const allowedExperimentalValues = Object.freeze({ */ previewTelemetryUrlEnabled: false, - /** - * Enables the Endpoint response actions console in various areas of the app - */ - responseActionsConsoleEnabled: true, - /** * Enables the insights module for related alerts by process ancestry */ @@ -73,33 +67,13 @@ export const allowedExperimentalValues = Object.freeze({ */ endpointResponseActionsEnabled: false, - /** - * Enables endpoint package level rbac - */ - endpointRbacEnabled: true, - - /** - * Enables endpoint package level rbac for response actions only. - * if endpointRbacEnabled is enabled, it will take precedence. - */ - endpointRbacV1Enabled: true, /** * Enables the alert details page currently only accessible via the alert details flyout and alert table context menu */ alertDetailsPageEnabled: false, /** - * Enables the `get-file` endpoint response action - */ - responseActionGetFileEnabled: true, - - /** - * Enables the `execute` endpoint response action - */ - responseActionExecuteEnabled: true, - - /** - * Enables the `upload` endpoint response action + * Enables the `upload` endpoint response action (v8.9) */ responseActionUploadEnabled: false, @@ -153,7 +127,6 @@ export const allowedExperimentalValues = Object.freeze({ type ExperimentalConfigKeys = Array; type Mutable = { -readonly [P in keyof T]: T[P] }; -const SecuritySolutionInvalidExperimentalValue = class extends Error {}; const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly; /** @@ -163,25 +136,27 @@ const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly { +export const parseExperimentalConfigValue = ( + configValue: string[] +): { features: ExperimentalFeatures; invalid: string[] } => { const enabledFeatures: Mutable> = {}; + const invalidKeys: string[] = []; for (const value of configValue) { - if (!isValidExperimentalValue(value)) { - throw new SecuritySolutionInvalidExperimentalValue(`[${value}] is not valid.`); + if (!allowedKeys.includes(value as keyof ExperimentalFeatures)) { + invalidKeys.push(value); + } else { + enabledFeatures[value as keyof ExperimentalFeatures] = true; } - - enabledFeatures[value as keyof ExperimentalFeatures] = true; } return { - ...allowedExperimentalValues, - ...enabledFeatures, + features: { + ...allowedExperimentalValues, + ...enabledFeatures, + }, + invalid: invalidKeys, }; }; -export const isValidExperimentalValue = (value: string): value is keyof ExperimentalFeatures => { - return allowedKeys.includes(value as keyof ExperimentalFeatures); -}; - export const getExperimentalAllowedValues = (): string[] => [...allowedKeys]; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx index 1b2d021634a2f..47bd5ab94e291 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/endpoint_agent_status/endpoint_agent_status.tsx @@ -22,7 +22,6 @@ import { HOST_STATUS_TO_BADGE_COLOR } from '../../../../management/pages/endpoin import { getEmptyValue } from '../../empty_value'; import type { ResponseActionsApiCommandNames } from '../../../../../common/endpoint/service/response_actions/constants'; import { RESPONSE_ACTION_API_COMMANDS_TO_CONSOLE_COMMAND_MAP } from '../../../../../common/endpoint/service/response_actions/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; import { useGetEndpointPendingActionsSummary } from '../../../../management/hooks/response_actions/use_get_endpoint_pending_actions_summary'; import { useTestIdGenerator } from '../../../../management/hooks/use_test_id_generator'; import type { HostInfo, EndpointPendingActions } from '../../../../../common/endpoint/types'; @@ -187,9 +186,6 @@ interface EndpointHostResponseActionsStatusProps { const EndpointHostResponseActionsStatus = memo( ({ pendingActions, isIsolated, 'data-test-subj': dataTestSubj }) => { const getTestId = useTestIdGenerator(dataTestSubj); - const isPendingStatusDisabled = useIsExperimentalFeatureEnabled( - 'disableIsolationUIPendingStatuses' - ); interface PendingActionsState { actionList: Array<{ label: string; count: number }>; @@ -269,15 +265,6 @@ const EndpointHostResponseActionsStatus = memo { }, }; }); -jest.mock('../../../hooks/use_experimental_features', () => ({ - useIsExperimentalFeatureEnabled: jest.fn((feature: string) => feature === 'endpointRbacEnabled'), -})); const useKibanaMock = useKibana as jest.Mocked; const useHttpMock = _useHttp as jest.Mock; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts index 0e56d214fcb25..5fa2400dab6b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts @@ -22,7 +22,6 @@ import { getEndpointAuthzInitialState, } from '../../../../../common/endpoint/service/authz'; import { useSecuritySolutionStartDependencies } from './security_solution_start_dependencies'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; /** * Retrieve the endpoint privileges for the current user. @@ -49,9 +48,6 @@ export const useEndpointPrivileges = (): Immutable => { const [userRolesCheckDone, setUserRolesCheckDone] = useState(false); const [userRoles, setUserRoles] = useState>([]); - const isEndpointRbacEnabled = useIsExperimentalFeatureEnabled('endpointRbacEnabled'); - const isEndpointRbacV1Enabled = useIsExperimentalFeatureEnabled('endpointRbacV1Enabled'); - const [checkHostIsolationExceptionsDone, setCheckHostIsolationExceptionsDone] = useState(false); const [hasHostIsolationExceptionsItems, setHasHostIsolationExceptionsItems] = @@ -67,7 +63,7 @@ export const useEndpointPrivileges = (): Immutable => { licenseService, fleetAuthz, userRoles, - isEndpointRbacEnabled || isEndpointRbacV1Enabled, + true, hasHostIsolationExceptionsItems ) : getEndpointAuthzInitialState()), @@ -81,8 +77,6 @@ export const useEndpointPrivileges = (): Immutable => { fleetAuthz, licenseService, userRoles, - isEndpointRbacEnabled, - isEndpointRbacV1Enabled, hasHostIsolationExceptionsItems, ]); diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.test.ts b/x-pack/plugins/security_solution/public/common/store/reducer.test.ts index 35a30738eb6f1..705902596fbbf 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.test.ts @@ -35,7 +35,7 @@ describe('createInitialState', () => { >; const defaultState = { defaultDataView: mockSourcererState.defaultDataView, - enableExperimental: parseExperimentalConfigValue([]), + enableExperimental: parseExperimentalConfigValue([]).features, kibanaDataViews: [mockSourcererState.defaultDataView], signalIndexName: 'siem-signals-default', }; diff --git a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_item.tsx b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_item.tsx index 8104e9a1703fc..9a9d8e7d7470d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_item.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/endpoint_responder/use_responder_action_item.tsx @@ -13,16 +13,12 @@ import { isTimelineEventItemAnAlert, } from '../../../common/utils/endpoint_alert_check'; import { ResponderContextMenuItem } from './responder_context_menu_item'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { getFieldValue } from '../host_isolation/helpers'; export const useResponderActionItem = ( eventDetailsData: TimelineEventsDetailsItem[] | null, onClick: () => void ): JSX.Element[] => { - const isResponseActionsConsoleEnabled = useIsExperimentalFeatureEnabled( - 'responseActionsConsoleEnabled' - ); const { loading: isAuthzLoading, canAccessResponseConsole } = useUserPrivileges().endpointPrivileges; @@ -42,7 +38,7 @@ export const useResponderActionItem = ( return useMemo(() => { const actions: JSX.Element[] = []; - if (isResponseActionsConsoleEnabled && !isAuthzLoading && canAccessResponseConsole && isAlert) { + if (!isAuthzLoading && canAccessResponseConsole && isAlert) { actions.push( { apiMocks = endpointMetadataHttpMocks(mockStartServicesMock.http as jest.Mocked); }); - describe('when the `responseActionsConsoleEnabled` feature flag is false', () => { - beforeAll(() => { - (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((featureKey) => { - if (featureKey === 'responseActionsConsoleEnabled') { - return false; - } - return true; - }); - }); - - afterAll(() => { - (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(() => true); - }); - - it('should hide the button if feature flag if off', async () => { - render(); - - expect(findLaunchResponderButton()).toHaveLength(0); - }); - }); - it('should not display the button if user is not allowed to write event filters', async () => { (useUserPrivileges as jest.Mock).mockReturnValue({ ...mockInitialUserPrivilegesState(), diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts index b3a7a8d4c813a..fd64cff764966 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/console_commands_definition.ts @@ -143,8 +143,6 @@ export const getEndpointConsoleCommands = ({ }): CommandDefinition[] => { const featureFlags = ExperimentalFeaturesService.get(); - const isGetFileEnabled = featureFlags.responseActionGetFileEnabled; - const isExecuteEnabled = featureFlags.responseActionExecuteEnabled; const isUploadEnabled = featureFlags.responseActionUploadEnabled; const doesEndpointSupportCommand = (commandName: ConsoleResponseActionCommands) => { @@ -379,11 +377,7 @@ export const getEndpointConsoleCommands = ({ helpDisabled: doesEndpointSupportCommand('processes') === false, helpHidden: !getRbacControl({ commandName: 'processes', privileges: endpointPrivileges }), }, - ]; - - // `get-file` is currently behind feature flag - if (isGetFileEnabled) { - consoleCommands.push({ + { name: 'get-file', about: getCommandAboutInfo({ aboutInfo: i18n.translate('xpack.securitySolution.endpointConsoleCommands.getFile.about', { @@ -429,13 +423,8 @@ export const getEndpointConsoleCommands = ({ commandName: 'get-file', privileges: endpointPrivileges, }), - }); - } - - // `execute` is currently behind feature flag - // planned for 8.8 - if (isExecuteEnabled) { - consoleCommands.push({ + }, + { name: 'execute', about: getCommandAboutInfo({ aboutInfo: i18n.translate('xpack.securitySolution.endpointConsoleCommands.execute.about', { @@ -487,8 +476,8 @@ export const getEndpointConsoleCommands = ({ commandName: 'execute', privileges: endpointPrivileges, }), - }); - } + }, + ]; // `upload` command // planned for 8.9 diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx index f5e96f0e8a706..4b1d26a6e243f 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/lib/integration_tests/console_commands_definition.test.tsx @@ -25,11 +25,6 @@ describe('When displaying Endpoint Response Actions', () => { beforeEach(() => { const testSetup = getConsoleTestSetup(); - testSetup.setExperimentalFlag({ - responseActionGetFileEnabled: true, - responseActionExecuteEnabled: true, - }); - const endpointMetadata = new EndpointMetadataGenerator().generate(); const commands = getEndpointConsoleCommands({ endpointAgentId: '123', diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index 91f5a9439b334..bab16f27dbace 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -247,17 +247,6 @@ export const useActionsLogFilter = ({ : RESPONSE_ACTION_API_COMMANDS_NAMES.filter((commandName) => { const featureFlags = ExperimentalFeaturesService.get(); - // `get-file` is currently behind FF - if (commandName === 'get-file' && !featureFlags.responseActionGetFileEnabled) { - return false; - } - - // TODO: remove this when `execute` is no longer behind FF - // planned for 8.8 - if (commandName === 'execute' && !featureFlags.responseActionExecuteEnabled) { - return false; - } - // upload - v8.9 if (commandName === 'upload' && !featureFlags.responseActionUploadEnabled) { return false; diff --git a/x-pack/plugins/security_solution/public/management/components/no_permissons/index.ts b/x-pack/plugins/security_solution/public/management/components/no_permissons/index.ts deleted file mode 100644 index 25421ba1dcd1a..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/no_permissons/index.ts +++ /dev/null @@ -1,8 +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. - */ - -export { NoPermissions } from './no_permissions'; diff --git a/x-pack/plugins/security_solution/public/management/components/no_permissons/no_permissions.tsx b/x-pack/plugins/security_solution/public/management/components/no_permissons/no_permissions.tsx deleted file mode 100644 index 3852cfbf73995..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/no_permissons/no_permissions.tsx +++ /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 React, { memo } from 'react'; -import { EuiEmptyPrompt, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; - -export const NoPermissions = memo(() => { - return ( - <> - - } - body={ - - - - } - /> - - ); -}); -NoPermissions.displayName = 'NoPermissions'; diff --git a/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.test.tsx b/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.test.tsx index 2e4429bc1fd70..2fdac844b5e41 100644 --- a/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.test.tsx @@ -11,19 +11,15 @@ import type { AppContextTestRender } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { PrivilegedRoute } from './privileged_route'; import type { PrivilegedRouteProps } from './privileged_route'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; import { AdministrationSubTab } from '../../types'; import { MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH } from '../../common/constants'; import { MANAGEMENT_PATH } from '../../../../common/constants'; -jest.mock('../../../common/hooks/use_experimental_features'); - describe('PrivilegedRoute', () => { const noPrivilegesPageTestId = 'noPrivilegesPage'; const noPermissionsPageTestId = 'noIngestPermissions'; const componentTestId = 'component-to-render'; - let featureFlags: { endpointRbacEnabled: boolean; endpointRbacV1Enabled: boolean }; let currentPath: string; let renderProps: PrivilegedRouteProps; @@ -31,7 +27,6 @@ describe('PrivilegedRoute', () => { let render: () => void; beforeEach(() => { - featureFlags = { endpointRbacEnabled: false, endpointRbacV1Enabled: false }; currentPath = 'path'; renderProps = { component: () =>
, @@ -50,96 +45,45 @@ describe('PrivilegedRoute', () => { ); }; - - const useIsExperimentalFeatureEnabledMock = (feature: keyof typeof featureFlags) => - featureFlags[feature]; - - (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation( - useIsExperimentalFeatureEnabledMock - ); }); - const testCommonPathsForAllFeatureFlags = () => { - it('renders component if it has privileges and on correct path', async () => { - render(); - - expect(renderResult.getByTestId(componentTestId)).toBeTruthy(); - expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); - expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); - }); - - it('renders nothing if path is different', async () => { - renderProps.path = 'different'; - - render(); - - expect(renderResult.queryByTestId(componentTestId)).toBeNull(); - expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); - expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); - }); - }; - - describe('no feature flags', () => { - testCommonPathsForAllFeatureFlags(); + it('renders component if it has privileges and on correct path', async () => { + render(); - it('renders `you need to be superuser` if no privileges', async () => { - renderProps.hasPrivilege = false; - - render(); - - expect(renderResult.getByTestId(noPermissionsPageTestId)).toBeTruthy(); - expect(renderResult.queryByTestId(componentTestId)).toBeNull(); - expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); - }); + expect(renderResult.getByTestId(componentTestId)).toBeTruthy(); + expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); + expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); }); - describe('endpointRbacV1Enabled', () => { - beforeEach(() => { - featureFlags.endpointRbacV1Enabled = true; - }); - - testCommonPathsForAllFeatureFlags(); - - describe('no privileges', () => { - it('renders `you need to have privileges` on Response actions history', async () => { - renderProps.hasPrivilege = false; - renderProps.path = MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH; - currentPath = `${MANAGEMENT_PATH}/${AdministrationSubTab.responseActionsHistory}`; + it('renders nothing if path is different', async () => { + renderProps.path = 'different'; - render(); + render(); - expect(renderResult.getByTestId(noPrivilegesPageTestId)).toBeTruthy(); - expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); - expect(renderResult.queryByTestId(componentTestId)).toBeNull(); - }); + expect(renderResult.queryByTestId(componentTestId)).toBeNull(); + expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); + expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); + }); - it('renders `you need to be superuser` on other pages', async () => { - renderProps.hasPrivilege = false; + it('renders `you need to have privileges` on Response actions history', async () => { + renderProps.hasPrivilege = false; + renderProps.path = MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH; + currentPath = `${MANAGEMENT_PATH}/${AdministrationSubTab.responseActionsHistory}`; - render(); + render(); - expect(renderResult.getByTestId(noPermissionsPageTestId)).toBeTruthy(); - expect(renderResult.queryByTestId(noPrivilegesPageTestId)).toBeNull(); - expect(renderResult.queryByTestId(componentTestId)).toBeNull(); - }); - }); + expect(renderResult.getByTestId(noPrivilegesPageTestId)).toBeTruthy(); + expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); + expect(renderResult.queryByTestId(componentTestId)).toBeNull(); }); - describe('endpointRbacEnabled', () => { - beforeEach(() => { - featureFlags.endpointRbacEnabled = true; - }); - - testCommonPathsForAllFeatureFlags(); - - it('renders `you need to have RBAC privileges` if no privileges', async () => { - renderProps.hasPrivilege = false; + it('renders `you need to have RBAC privileges` if no privileges', async () => { + renderProps.hasPrivilege = false; - render(); + render(); - expect(renderResult.getByTestId(noPrivilegesPageTestId)).toBeTruthy(); - expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); - expect(renderResult.queryByTestId(componentTestId)).toBeNull(); - }); + expect(renderResult.getByTestId(noPrivilegesPageTestId)).toBeTruthy(); + expect(renderResult.queryByTestId(noPermissionsPageTestId)).toBeNull(); + expect(renderResult.queryByTestId(componentTestId)).toBeNull(); }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.tsx b/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.tsx index 3ec48101f6a02..1bdddde1bdea6 100644 --- a/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.tsx +++ b/x-pack/plugins/security_solution/public/management/components/privileged_route/privileged_route.tsx @@ -5,13 +5,10 @@ * 2.0. */ import type { ComponentType } from 'react'; -import React, { memo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { Route } from '@kbn/kibana-react-plugin/public'; import type { DocLinks } from '@kbn/doc-links'; import { NoPrivilegesPage } from '../../../common/components/no_privileges'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; -import { NoPermissions } from '../no_permissons'; -import { MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH } from '../../common/constants'; export interface PrivilegedRouteProps { path: string; @@ -20,22 +17,18 @@ export interface PrivilegedRouteProps { } export const PrivilegedRoute = memo(({ component, hasPrivilege, path }: PrivilegedRouteProps) => { - const isEndpointRbacEnabled = useIsExperimentalFeatureEnabled('endpointRbacEnabled'); - const isEndpointRbacV1Enabled = useIsExperimentalFeatureEnabled('endpointRbacV1Enabled'); + const docLinkSelector = useCallback((docLinks: DocLinks) => { + return docLinks.securitySolution.privileges; + }, []); - const docLinkSelector = (docLinks: DocLinks) => docLinks.securitySolution.privileges; + const componentToRender = useMemo(() => { + if (!hasPrivilege) { + // eslint-disable-next-line react/display-name + return () => ; + } - let componentToRender = component; - - if (!hasPrivilege) { - const shouldUseMissingPrivilegesScreen = - isEndpointRbacEnabled || - (isEndpointRbacV1Enabled && path === MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH); - - componentToRender = shouldUseMissingPrivilegesScreen - ? () => - : NoPermissions; - } + return component; + }, [component, docLinkSelector, hasPrivilege]); return ; }); diff --git a/x-pack/plugins/security_solution/public/management/links.test.ts b/x-pack/plugins/security_solution/public/management/links.test.ts index 68510306af6b5..664bd562ec235 100644 --- a/x-pack/plugins/security_solution/public/management/links.test.ts +++ b/x-pack/plugins/security_solution/public/management/links.test.ts @@ -146,9 +146,6 @@ describe('links', () => { fakeHttpServices.get.mockResolvedValue({ total: 0 }); licenseServiceMock.isPlatinumPlus.mockReturnValue(false); - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: true }, - }); const filteredLinks = await getManagementFilteredLinks( coreMockStarted, @@ -187,9 +184,6 @@ describe('links', () => { fakeHttpServices.get.mockResolvedValue({ total: 100 }); licenseServiceMock.isPlatinumPlus.mockReturnValue(false); - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: true }, - }); const filteredLinks = await getManagementFilteredLinks( coreMockStarted, @@ -222,31 +216,7 @@ describe('links', () => { }); }); - // this can be removed after removing endpointRbacEnabled feature flag - describe('without endpointRbacEnabled', () => { - beforeAll(() => { - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: false }, - }); - }); - - it('shows Trusted Applications for non-superuser, too', async () => { - (calculateEndpointAuthz as jest.Mock).mockReturnValue(getEndpointAuthzInitialStateMock()); - - const filteredLinks = await getManagementFilteredLinks(coreMockStarted, getPlugins([])); - - expect(filteredLinks).toEqual(links); - }); - }); - - // this can be the default after removing endpointRbacEnabled feature flag - describe('with endpointRbacEnabled', () => { - beforeAll(() => { - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: true }, - }); - }); - + describe('RBAC checks', () => { it('should return all links for user with all sub-feature privileges', async () => { (calculateEndpointAuthz as jest.Mock).mockReturnValue(getEndpointAuthzInitialStateMock()); diff --git a/x-pack/plugins/security_solution/public/management/links.ts b/x-pack/plugins/security_solution/public/management/links.ts index 81b2c9e35af96..daf0df2d4736f 100644 --- a/x-pack/plugins/security_solution/public/management/links.ts +++ b/x-pack/plugins/security_solution/public/management/links.ts @@ -60,7 +60,6 @@ import { IconHostIsolation } from './icons/host_isolation'; import { IconSiemRules } from './icons/siem_rules'; import { IconTrustedApplications } from './icons/trusted_applications'; import { HostIsolationExceptionsApiClient } from './pages/host_isolation_exceptions/host_isolation_exceptions_api_client'; -import { ExperimentalFeaturesService } from '../common/experimental_features_service'; const categories = [ { @@ -165,7 +164,6 @@ export const links: LinkItem = { path: POLICIES_PATH, skipUrlState: true, hideTimeline: true, - experimentalKey: 'policyListEnabled', }, { id: SecurityPageName.trustedApps, @@ -241,14 +239,8 @@ export const getManagementFilteredLinks = async ( plugins: StartPlugins ): Promise => { const fleetAuthz = plugins.fleet?.authz; - - const { endpointRbacEnabled, endpointRbacV1Enabled } = ExperimentalFeaturesService.get(); - const isEndpointRbacEnabled = endpointRbacEnabled || endpointRbacV1Enabled; - const linksToExclude: SecurityPageName[] = []; - const currentUser = await plugins.security.authc.getCurrentUser(); - const isPlatinumPlus = licenseService.isPlatinumPlus(); let hasHostIsolationExceptions: boolean = isPlatinumPlus; @@ -264,7 +256,7 @@ export const getManagementFilteredLinks = async ( fleetAuthz && hasKibanaPrivilege( fleetAuthz, - isEndpointRbacEnabled, + true, currentUser.roles.includes('superuser'), 'readHostIsolationExceptions' ) @@ -288,7 +280,7 @@ export const getManagementFilteredLinks = async ( licenseService, fleetAuthz, currentUser.roles, - isEndpointRbacEnabled, + true, hasHostIsolationExceptions ) : getEndpointAuthzInitialState(); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 0a6c82ff452a2..eba863b3b8ddb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -10,7 +10,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { pagePathGetters } from '@kbn/fleet-plugin/public'; import { useUserPrivileges } from '../../../../../common/components/user_privileges'; import { useWithShowEndpointResponder } from '../../../../hooks'; -import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import { APP_UI_ID } from '../../../../../../common/constants'; import { getEndpointDetailsPath, getEndpointListPath } from '../../../../common/routing'; import type { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/types'; @@ -37,9 +36,6 @@ export const useEndpointActionItems = ( const fleetAgentPolicies = useEndpointSelector(agentPolicies); const allCurrentUrlParams = useEndpointSelector(uiQueryParams); const showEndpointResponseActionsConsole = useWithShowEndpointResponder(); - const isResponseActionsConsoleEnabled = useIsExperimentalFeatureEnabled( - 'responseActionsConsoleEnabled' - ); const { canAccessResponseConsole, canIsolateHost, @@ -123,7 +119,7 @@ export const useEndpointActionItems = ( return [ ...isolationActions, - ...(isResponseActionsConsoleEnabled && canAccessResponseConsole + ...(canAccessResponseConsole ? [ { 'data-test-subj': 'console', @@ -268,7 +264,6 @@ export const useEndpointActionItems = ( endpointMetadata, fleetAgentPolicies, getAppUrl, - isResponseActionsConsoleEnabled, showEndpointResponseActionsConsole, options?.isEndpointList, canIsolateHost, diff --git a/x-pack/plugins/security_solution/public/management/pages/integration_tests/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/integration_tests/index.test.tsx index a6e98483a03a6..d0cbd71e187c5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/integration_tests/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/integration_tests/index.test.tsx @@ -13,8 +13,6 @@ import type { AppContextTestRender } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { useUserPrivileges } from '../../../common/components/user_privileges'; import { endpointPageHttpMock } from '../endpoint_hosts/mocks'; -import { ExperimentalFeaturesService } from '../../../common/experimental_features_service'; -import { allowedExperimentalValues } from '../../../../common/experimental_features'; jest.mock('../../../common/components/user_privileges'); @@ -24,12 +22,6 @@ describe('when in the Administration tab', () => { let render: () => ReturnType; const mockedContext = createAppRootMockRenderer(); - beforeAll(() => { - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues }, - }); - }); - beforeEach(() => { endpointPageHttpMock(mockedContext.coreStart.http); render = () => mockedContext.render(); @@ -41,13 +33,6 @@ describe('when in the Administration tab', () => { }); describe('when the user has no permissions', () => { - // remove this beforeAll hook when feature flag is removed - beforeAll(() => { - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: true }, - }); - }); - it('should display `no permission` if no `canAccessEndpointManagement`', async () => { useUserPrivilegesMock.mockReturnValue({ endpointPrivileges: { loading: false, canAccessEndpointManagement: false }, @@ -112,13 +97,6 @@ describe('when in the Administration tab', () => { }); describe('when the user has permissions', () => { - // remove this beforeAll hook when feature flag is removed - beforeAll(() => { - ExperimentalFeaturesService.init({ - experimentalFeatures: { ...allowedExperimentalValues, endpointRbacEnabled: true }, - }); - }); - it('should display the Management view if user has privileges', async () => { useUserPrivilegesMock.mockReturnValue({ endpointPrivileges: { loading: false, canReadEndpointList: true, canAccessFleet: true }, diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/index.tsx index 50556686438ae..e9eef8c5a8132 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/index.tsx @@ -21,10 +21,8 @@ import { } from '../../common/constants'; import { NotFoundPage } from '../../../app/404'; import { getPolicyDetailPath } from '../../common/routing'; -import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; export const PolicyContainer = memo(() => { - const isPolicyListEnabled = useIsExperimentalFeatureEnabled('policyListEnabled'); return ( { exact render={(props) => } /> - {isPolicyListEnabled && ( - - )} + ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index 3505f242b8ca9..020ef18ed0d8b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -27,16 +27,13 @@ import { import { policyListApiPathHandlers } from '../store/test_mock_utils'; import { PolicyDetails } from './policy_details'; import { APP_UI_ID } from '../../../../../common/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; jest.mock('./policy_forms/components/policy_form_layout', () => ({ PolicyFormLayout: () => <>, })); jest.mock('../../../../common/components/user_privileges'); -jest.mock('../../../../common/hooks/use_experimental_features'); const useUserPrivilegesMock = useUserPrivileges as jest.Mock; -const useIsExperimentalFeatureMock = useIsExperimentalFeatureEnabled as jest.Mock; describe('Policy Details', () => { const policyDetailsPathUrl = getPolicyDetailPath('1'); @@ -66,9 +63,6 @@ describe('Policy Details', () => { let releaseApiFailure: () => void; beforeEach(() => { - useIsExperimentalFeatureMock.mockReturnValue({ - policyListEnabled: true, - }); http.get.mockImplementation(async () => { await new Promise((_, reject) => { releaseApiFailure = reject.bind(null, new Error('policy not found')); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index c02937b15d56d..04d9e14efa5b9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -21,13 +21,11 @@ import { AdministrationListPage } from '../../../components/administration_list_ import type { BackToExternalAppButtonProps } from '../../../components/back_to_external_app_button/back_to_external_app_button'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button/back_to_external_app_button'; import type { PolicyDetailsRouteState } from '../../../../../common/endpoint/types'; -import { getEndpointListPath, getPoliciesPath } from '../../../common/routing'; +import { getPoliciesPath } from '../../../common/routing'; import { useAppUrl } from '../../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../../common/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; export const PolicyDetails = React.memo(() => { - const isPolicyListEnabled = useIsExperimentalFeatureEnabled('policyListEnabled'); const { state: routeState = {} } = useLocation(); const { getAppUrl } = useAppUrl(); @@ -49,38 +47,20 @@ export const PolicyDetails = React.memo(() => { }; } - if (isPolicyListEnabled) { - // default is to go back to the policy list - const policyListPath = getPoliciesPath(); - return { - backButtonLabel: i18n.translate('xpack.securitySolution.policyDetails.backToPolicyButton', { - defaultMessage: 'Back to policy list', - }), - backButtonUrl: getAppUrl({ path: policyListPath }), - onBackButtonNavigateTo: [ - APP_UI_ID, - { - path: policyListPath, - }, - ], - }; - } else { - // remove else block once policy list is not hidden behind feature flag - const endpointListPath = getEndpointListPath({ name: 'endpointList' }); - return { - backButtonLabel: i18n.translate('xpack.securitySolution.policyDetails.backToEndpointList', { - defaultMessage: 'View all endpoints', - }), - backButtonUrl: getAppUrl({ path: endpointListPath }), - onBackButtonNavigateTo: [ - APP_UI_ID, - { - path: endpointListPath, - }, - ], - }; - } - }, [getAppUrl, routeState?.backLink, isPolicyListEnabled]); + const policyListPath = getPoliciesPath(); + return { + backButtonLabel: i18n.translate('xpack.securitySolution.policyDetails.backToPolicyButton', { + defaultMessage: 'Back to policy list', + }), + backButtonUrl: getAppUrl({ path: policyListPath }), + onBackButtonNavigateTo: [ + APP_UI_ID, + { + path: policyListPath, + }, + ], + }; + }, [getAppUrl, routeState.backLink]); const headerRightContent = ( { const [showConfirm, setShowConfirm] = useState(false); const [routeState, setRouteState] = useState(); const policyName = policyItem?.name ?? ''; - const isPolicyListEnabled = useIsExperimentalFeatureEnabled('policyListEnabled'); const routingOnCancelNavigateTo = routeState?.onCancelNavigateTo; const navigateToAppArguments = useMemo((): Parameters => { @@ -73,12 +71,10 @@ export const PolicyFormLayout = React.memo(() => { return [ APP_UI_ID, { - path: isPolicyListEnabled - ? getPoliciesPath() - : getEndpointListPath({ name: 'endpointList' }), + path: getPoliciesPath(), }, ]; - }, [isPolicyListEnabled, routingOnCancelNavigateTo]); + }, [routingOnCancelNavigateTo]); // Handle showing update statuses useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index b13a200161d22..45680b4c6cd3a 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -86,7 +86,9 @@ export class Plugin implements IPlugin(); - this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental || []); + this.experimentalFeatures = parseExperimentalConfigValue( + this.config.enableExperimental || [] + ).features; this.kibanaVersion = initializerContext.env.packageInfo.version; this.kibanaBranch = initializerContext.env.packageInfo.branch; this.prebuiltRulesPackageVersion = this.config.prebuiltRulesPackageVersion; @@ -268,18 +270,17 @@ export class Plugin implements IPlugin { - const enableExperimental: Array = [ - // Remove property below once `get-file` FF is enabled or removed - 'responseActionGetFileEnabled', - // remove property below once `execute` FF is enabled or removed - 'responseActionExecuteEnabled', - 'responseActionUploadEnabled', - ]; + const enableExperimental: Array = ['responseActionUploadEnabled']; return { [SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX, @@ -32,7 +26,7 @@ export const createMockConfig = (): ConfigType => { alertIgnoreFields: [], maxUploadResponseActionFileBytes: 26214400, - experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features, enabled: true, }; }; @@ -45,7 +39,7 @@ const withExperimentalFeature = ( return { ...config, enableExperimental, - experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features, }; }; diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 44ff0e4d1aa73..dc732061ab947 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -10,13 +10,7 @@ import { schema } from '@kbn/config-schema'; import type { PluginInitializerContext } from '@kbn/core/server'; import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX } from '../common/constants'; import type { ExperimentalFeatures } from '../common/experimental_features'; -import { - getExperimentalAllowedValues, - isValidExperimentalValue, - parseExperimentalConfigValue, -} from '../common/experimental_features'; - -const allowedExperimentalValues = getExperimentalAllowedValues(); +import { parseExperimentalConfigValue } from '../common/experimental_features'; export const configSchema = schema.object({ maxRuleImportExportSize: schema.number({ defaultValue: 10000 }), @@ -94,15 +88,6 @@ export const configSchema = schema.object({ */ enableExperimental: schema.arrayOf(schema.string(), { defaultValue: () => [], - validate(list) { - for (const key of list) { - if (!isValidExperimentalValue(key)) { - return `[${key}] is not allowed. Allowed values are: ${allowedExperimentalValues.join( - ', ' - )}`; - } - } - }, }), /** @@ -141,7 +126,20 @@ export type ConfigType = ConfigSchema & { export const createConfig = (context: PluginInitializerContext): ConfigType => { const pluginConfig = context.config.get>(); - const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental); + const logger = context.logger.get('config'); + + const { invalid, features: experimentalFeatures } = parseExperimentalConfigValue( + pluginConfig.enableExperimental + ); + + if (invalid.length) { + logger.warn(`Unsupported "xpack.securitySolution.enableExperimental" values detected. +The following configuration values are no longer supported and should be removed from the kibana configuration file: + + xpack.securitySolution.enableExperimental: +${invalid.map((key) => ` - ${key}`).join('\n')} +`); + } return { ...pluginConfig, diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 14475e1a8630d..8db3c3cd7df64 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -162,7 +162,6 @@ export class EndpointAppContextService { public async getEndpointAuthz(request: KibanaRequest): Promise { const fleetAuthz = await this.getFleetAuthzService().fromRequest(request); const userRoles = this.security?.authc.getCurrentUser(request)?.roles ?? []; - const { endpointRbacEnabled, endpointRbacV1Enabled } = this.experimentalFeatures; const isPlatinumPlus = this.getLicenseService().isPlatinumPlus(); const listClient = this.getExceptionListsClient(); @@ -174,7 +173,7 @@ export class EndpointAppContextService { this.getLicenseService(), fleetAuthz, userRoles, - endpointRbacEnabled || endpointRbacV1Enabled, + true, hasExceptionsListItems ); } diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 959ac0bf51ef3..b2e1202790b9b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -83,7 +83,7 @@ export const createMockEndpointAppContext = ( config: () => Promise.resolve(config), serverConfig: config, service: createMockEndpointAppContextService(mockManifestManager), - experimentalFeatures: parseExperimentalConfigValue(config.enableExperimental), + experimentalFeatures: parseExperimentalConfigValue(config.enableExperimental).features, }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts index 1f96cb4dff64a..6587a6446a52c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/index.ts @@ -29,10 +29,6 @@ export function registerActionRoutes( registerActionListRoutes(router, endpointContext); registerActionDetailsRoutes(router, endpointContext); registerResponseActionRoutes(router, endpointContext); - - // APIs specific to `get-file` are behind FF - if (endpointContext.experimentalFeatures.responseActionGetFileEnabled) { - registerActionFileDownloadRoutes(router, endpointContext); - registerActionFileInfoRoute(router, endpointContext); - } + registerActionFileDownloadRoutes(router, endpointContext); + registerActionFileInfoRoute(router, endpointContext); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts index 0348227561677..1d82c9faa5461 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts @@ -145,37 +145,31 @@ export function registerResponseActionRoutes( ) ); - // `get-file` currently behind FF - if (endpointContext.experimentalFeatures.responseActionGetFileEnabled) { - router.post( - { - path: GET_FILE_ROUTE, - validate: EndpointActionGetFileSchema, - options: { authRequired: true, tags: ['access:securitySolution'] }, - }, - withEndpointAuthz( - { all: ['canWriteFileOperations'] }, - logger, - responseActionRequestHandler(endpointContext, 'get-file') - ) - ); - } - - // `execute` currently behind FF (planned for 8.8) - if (endpointContext.experimentalFeatures.responseActionExecuteEnabled) { - router.post( - { - path: EXECUTE_ROUTE, - validate: ExecuteActionRequestSchema, - options: { authRequired: true, tags: ['access:securitySolution'] }, - }, - withEndpointAuthz( - { all: ['canWriteExecuteOperations'] }, - logger, - responseActionRequestHandler(endpointContext, 'execute') - ) - ); - } + router.post( + { + path: GET_FILE_ROUTE, + validate: EndpointActionGetFileSchema, + options: { authRequired: true, tags: ['access:securitySolution'] }, + }, + withEndpointAuthz( + { all: ['canWriteFileOperations'] }, + logger, + responseActionRequestHandler(endpointContext, 'get-file') + ) + ); + + router.post( + { + path: EXECUTE_ROUTE, + validate: ExecuteActionRequestSchema, + options: { authRequired: true, tags: ['access:securitySolution'] }, + }, + withEndpointAuthz( + { all: ['canWriteExecuteOperations'] }, + logger, + responseActionRequestHandler(endpointContext, 'execute') + ) + ); registerActionFileUploadRoute(router, endpointContext); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts index c04d9e34a7c4d..fca197b22c748 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts @@ -49,7 +49,7 @@ describe('Endpoint Pending Action Summary API', () => { endpointResponses: LogsEndpointActionResponse[] ) => void; - const setupRouteHandler = (pendingActionResponsesWithAck: boolean = true): void => { + const setupRouteHandler = (): void => { const esClientMock = elasticsearchServiceMock.createScopedClusterClient(); const routerMock = httpServiceMock.createRouter(); @@ -71,7 +71,6 @@ describe('Endpoint Pending Action Summary API', () => { service: endpointAppContextService, experimentalFeatures: { ...endpointContextMock.experimentalFeatures, - pendingActionResponsesWithAck, }, }); @@ -126,6 +125,10 @@ describe('Endpoint Pending Action Summary API', () => { }; }; + beforeEach(() => { + setupRouteHandler(); + }); + afterEach(() => { if (endpointAppContextService) { endpointAppContextService.stop(); @@ -158,261 +161,240 @@ describe('Endpoint Pending Action Summary API', () => { }); }); - describe.each([ - ['when pendingActionResponsesWithAck is TRUE', true], - ['when pendingActionResponsesWithAck is FALSE', false], - ])('response %s', (_, pendingActionResponsesWithAck) => { - const getExpected = (value: number): number => { - return pendingActionResponsesWithAck ? value : 0; - }; - - beforeEach(() => { - setupRouteHandler(pendingActionResponsesWithAck); + it('should include agent IDs in the output, even if they have no actions', async () => { + const mockID = 'XYZABC-000'; + havingActionsAndResponses([], []); + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + }); - it('should include agent IDs in the output, even if they have no actions', async () => { - const mockID = 'XYZABC-000'; - havingActionsAndResponses([], []); - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + it('should include total counts for large (more than a page) action counts', async () => { + const mockID = 'XYZABC-000'; + const actions: LogsEndpointAction[] = Array.from({ length: 1400 }, () => + endpointActionGenerator.generate({ + agent: { id: [mockID] }, + EndpointActions: { data: { command: 'isolate' } }, + }) + ); + havingActionsAndResponses(actions, []); + + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); - it('should include total counts for large (more than a page) action counts', async () => { - const mockID = 'XYZABC-000'; - const actions: LogsEndpointAction[] = Array.from({ length: 1400 }, () => + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( + 1400 + ); + }); + + it('should respond with a valid pending action', async () => { + const mockID = 'XYZABC-000'; + havingActionsAndResponses( + [ endpointActionGenerator.generate({ agent: { id: [mockID] }, - EndpointActions: { data: { command: 'isolate' } }, - }) - ); - havingActionsAndResponses(actions, []); - - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( - getExpected(1400) - ); + }), + ], + [] + ); + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); - - it('should respond with a valid pending action', async () => { - const mockID = 'XYZABC-000'; - havingActionsAndResponses( - [ - endpointActionGenerator.generate({ - agent: { id: [mockID] }, - }), - ], - [] - ); - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + }); + it('should include a total count of a pending action', async () => { + const mockID = 'XYZABC-000'; + havingActionsAndResponses( + [ + endpointActionGenerator.generate({ + agent: { id: [mockID] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + endpointActionGenerator.generate({ + agent: { id: [mockID] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + ], + [] + ); + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); - it('should include a total count of a pending action', async () => { - const mockID = 'XYZABC-000'; - havingActionsAndResponses( - [ - endpointActionGenerator.generate({ - agent: { id: [mockID] }, - EndpointActions: { data: { command: 'isolate' } }, - }), + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual(2); + }); + it('should show multiple pending actions, and their counts', async () => { + const mockID = 'XYZABC-000'; + havingActionsAndResponses( + Array.from( + ['isolate', 'isolate', 'isolate', 'unisolate', 'unisolate'], + (command) => endpointActionGenerator.generate({ agent: { id: [mockID] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - ], - [] - ); - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( - getExpected(2) - ); - }); - it('should show multiple pending actions, and their counts', async () => { - const mockID = 'XYZABC-000'; - havingActionsAndResponses( - Array.from( - ['isolate', 'isolate', 'isolate', 'unisolate', 'unisolate'], - (command) => - endpointActionGenerator.generate({ - agent: { id: [mockID] }, - EndpointActions: { data: { command } }, - }) - ), - [] - ); - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( - getExpected(3) - ); - expect( - (response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.unisolate - ).toEqual(getExpected(2)); + EndpointActions: { data: { command } }, + }) + ), + [] + ); + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); - it('should calculate correct pending counts from grouped/bulked actions', async () => { - const mockID = 'XYZABC-000'; - havingActionsAndResponses( - [ - endpointActionGenerator.generate({ - agent: { id: [mockID, 'IRRELEVANT-OTHER-AGENT', 'ANOTHER-POSSIBLE-AGENT'] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - endpointActionGenerator.generate({ - agent: { id: [mockID, 'YET-ANOTHER-AGENT-ID'] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - endpointActionGenerator.generate({ - agent: { id: ['YET-ANOTHER-AGENT-ID'] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - ], - [] - ); - const response = await getPendingStatus({ - query: { - agent_ids: [mockID], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( - getExpected(2) - ); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual(3); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.unisolate).toEqual( + 2 + ); + }); + it('should calculate correct pending counts from grouped/bulked actions', async () => { + const mockID = 'XYZABC-000'; + havingActionsAndResponses( + [ + endpointActionGenerator.generate({ + agent: { id: [mockID, 'IRRELEVANT-OTHER-AGENT', 'ANOTHER-POSSIBLE-AGENT'] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + endpointActionGenerator.generate({ + agent: { id: [mockID, 'YET-ANOTHER-AGENT-ID'] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + endpointActionGenerator.generate({ + agent: { id: ['YET-ANOTHER-AGENT-ID'] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + ], + [] + ); + const response = await getPendingStatus({ + query: { + agent_ids: [mockID], + }, }); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockID); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual(2); + }); - it('should exclude actions that have responses from the pending count', async () => { - const mockAgentID = 'XYZABC-000'; - const actionID = 'some-known-actionid'; - havingActionsAndResponses( - [ - endpointActionGenerator.generate({ - agent: { id: [mockAgentID] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - endpointActionGenerator.generate({ - agent: { id: [mockAgentID] }, - EndpointActions: { action_id: actionID, data: { command: 'isolate' } }, - }), - ], - [ - endpointActionGenerator.generateResponse({ - agent: { id: [mockAgentID] }, - EndpointActions: { action_id: actionID, data: { command: 'isolate' } }, - }), - ] - ); - (endpointAppContextService.getEndpointMetadataService as jest.Mock) = jest - .fn() - .mockReturnValue({ - findHostMetadataForFleetAgents: jest.fn().mockResolvedValue([]), - }); - const response = await getPendingStatus({ - query: { - agent_ids: [mockAgentID], - }, + it('should exclude actions that have responses from the pending count', async () => { + const mockAgentID = 'XYZABC-000'; + const actionID = 'some-known-actionid'; + havingActionsAndResponses( + [ + endpointActionGenerator.generate({ + agent: { id: [mockAgentID] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + endpointActionGenerator.generate({ + agent: { id: [mockAgentID] }, + EndpointActions: { action_id: actionID, data: { command: 'isolate' } }, + }), + ], + [ + endpointActionGenerator.generateResponse({ + agent: { id: [mockAgentID] }, + EndpointActions: { action_id: actionID, data: { command: 'isolate' } }, + }), + ] + ); + (endpointAppContextService.getEndpointMetadataService as jest.Mock) = jest + .fn() + .mockReturnValue({ + findHostMetadataForFleetAgents: jest.fn().mockResolvedValue([]), }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockAgentID); - expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual( - getExpected(1) - ); + const response = await getPendingStatus({ + query: { + agent_ids: [mockAgentID], + }, }); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(1); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].agent_id).toEqual(mockAgentID); + expect((response.ok.mock.calls[0][0]?.body as any)?.data[0].pending_actions.isolate).toEqual(1); + }); - it('should have accurate counts for multiple agents, bulk actions, and responses', async () => { - const agentOne = 'XYZABC-000'; - const agentTwo = 'DEADBEEF'; - const agentThree = 'IDIDIDID'; - - const actionTwoID = 'ID-TWO'; - havingActionsAndResponses( - [ - endpointActionGenerator.generate({ - agent: { id: [agentOne, agentTwo, agentThree] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - endpointActionGenerator.generate({ - agent: { id: [agentTwo, agentThree] }, - EndpointActions: { data: { command: 'isolate' }, action_id: actionTwoID }, - }), - endpointActionGenerator.generate({ - agent: { id: [agentThree] }, - EndpointActions: { data: { command: 'isolate' } }, - }), - ], + it('should have accurate counts for multiple agents, bulk actions, and responses', async () => { + const agentOne = 'XYZABC-000'; + const agentTwo = 'DEADBEEF'; + const agentThree = 'IDIDIDID'; - [ - endpointActionGenerator.generateResponse({ - agent: { id: [agentThree] }, - EndpointActions: { - action_id: actionTwoID, - }, - }), - ] - ); - (endpointAppContextService.getEndpointMetadataService as jest.Mock) = jest - .fn() - .mockReturnValue({ - findHostMetadataForFleetAgents: jest.fn().mockResolvedValue([]), - }); - const response = await getPendingStatus({ - query: { - agent_ids: [agentOne, agentTwo, agentThree], - }, - }); - expect(response.ok).toBeCalled(); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(3); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ - agent_id: agentOne, - pending_actions: { - isolate: getExpected(1), - }, - }); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ - agent_id: agentTwo, - pending_actions: { - isolate: getExpected(2), - }, - }); - expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ - agent_id: agentThree, - pending_actions: { - isolate: getExpected(2), // present in all three actions, but second one has a response, therefore not pending - }, + const actionTwoID = 'ID-TWO'; + havingActionsAndResponses( + [ + endpointActionGenerator.generate({ + agent: { id: [agentOne, agentTwo, agentThree] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + endpointActionGenerator.generate({ + agent: { id: [agentTwo, agentThree] }, + EndpointActions: { data: { command: 'isolate' }, action_id: actionTwoID }, + }), + endpointActionGenerator.generate({ + agent: { id: [agentThree] }, + EndpointActions: { data: { command: 'isolate' } }, + }), + ], + + [ + endpointActionGenerator.generateResponse({ + agent: { id: [agentThree] }, + EndpointActions: { + action_id: actionTwoID, + }, + }), + ] + ); + (endpointAppContextService.getEndpointMetadataService as jest.Mock) = jest + .fn() + .mockReturnValue({ + findHostMetadataForFleetAgents: jest.fn().mockResolvedValue([]), }); + const response = await getPendingStatus({ + query: { + agent_ids: [agentOne, agentTwo, agentThree], + }, + }); + expect(response.ok).toBeCalled(); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toHaveLength(3); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ + agent_id: agentOne, + pending_actions: { + isolate: 1, + }, + }); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ + agent_id: agentTwo, + pending_actions: { + isolate: 2, + }, + }); + expect((response.ok.mock.calls[0][0]?.body as any)?.data).toContainEqual({ + agent_id: agentThree, + pending_actions: { + isolate: 2, // present in all three actions, but second one has a response, therefore not pending + }, }); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts index 2660fcd37103e..eb392e546d396 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts @@ -59,8 +59,7 @@ export const actionStatusRequestHandler = function ( esClient, endpointContext.service.getEndpointMetadataService(), logger, - agentIDs, - endpointContext.experimentalFeatures.pendingActionResponsesWithAck + agentIDs ); return res.ok({ diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts index a2aafaab57d42..543612bb5e39e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/pending_actions_summary.ts @@ -24,8 +24,7 @@ export const getPendingActionsSummary = async ( metadataService: EndpointMetadataService, logger: Logger, /** The Fleet Agent IDs to be checked */ - agentIDs: string[], - isPendingActionResponsesWithAckEnabled: boolean + agentIDs: string[] ): Promise => { const { data: unExpiredActionList } = await getActionList({ esClient, @@ -60,12 +59,8 @@ export const getPendingActionsSummary = async ( for (const agentID of agentIDs) { const agentPendingActions: EndpointPendingActions['pending_actions'] = {}; const setActionAsPending = (commandName: string) => { - // Add the command to the list of pending actions, but set it to zero if the - // `pendingActionResponsesWithAck` feature flag is false. - // Otherwise, just increment the count for this command - agentPendingActions[commandName] = !isPendingActionResponsesWithAckEnabled - ? 0 - : (agentPendingActions[commandName] ?? 0) + 1; + // Add the command to the list of pending actions and increment the count for this command + agentPendingActions[commandName] = (agentPendingActions[commandName] ?? 0) + 1; }; pending.push({ diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts index 10eccac516847..dda2d60ca94e3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts @@ -90,7 +90,7 @@ export const buildManifestManagerContextMock = ( ...fullOpts, artifactClient: createEndpointArtifactClientMock(), logger: loggingSystemMock.create().get() as jest.Mocked, - experimentalFeatures: parseExperimentalConfigValue([]), + experimentalFeatures: parseExperimentalConfigValue([]).features, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/app_features/security_kibana_features.ts b/x-pack/plugins/security_solution/server/lib/app_features/security_kibana_features.ts index 56365487569a1..4bf4ceed34aad 100644 --- a/x-pack/plugins/security_solution/server/lib/app_features/security_kibana_features.ts +++ b/x-pack/plugins/security_solution/server/lib/app_features/security_kibana_features.ts @@ -19,10 +19,10 @@ import { SAVED_QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, } from '@kbn/securitysolution-rules'; +import type { ExperimentalFeatures } from '../../../common'; +import { SecuritySubFeatureId } from './security_kibana_sub_features'; import { APP_ID, LEGACY_NOTIFICATIONS_ID, SERVER_APP_ID } from '../../../common/constants'; import { savedObjectTypes } from '../../saved_objects'; -import type { ExperimentalFeatures } from '../../../common/experimental_features'; -import { SecuritySubFeatureId } from './security_kibana_sub_features'; import type { AppFeaturesSecurityConfig, BaseKibanaFeatureConfig } from './types'; import { AppFeatureSecurityKey } from '../../../common/types/app_features'; @@ -123,35 +123,21 @@ export const getSecurityBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({ }); export const getSecurityBaseKibanaSubFeatureIds = ( - experimentalFeatures: ExperimentalFeatures + _: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use ): SecuritySubFeatureId[] => { - const subFeatureIds: SecuritySubFeatureId[] = []; - - if (experimentalFeatures.endpointRbacEnabled) { - subFeatureIds.push( - SecuritySubFeatureId.endpointList, - SecuritySubFeatureId.trustedApplications, - SecuritySubFeatureId.hostIsolationExceptions, - SecuritySubFeatureId.blocklist, - SecuritySubFeatureId.eventFilters, - SecuritySubFeatureId.policyManagement - ); - } - - if (experimentalFeatures.endpointRbacEnabled || experimentalFeatures.endpointRbacV1Enabled) { - subFeatureIds.push( - SecuritySubFeatureId.responseActionsHistory, - SecuritySubFeatureId.hostIsolation, - SecuritySubFeatureId.processOperations - ); - } - if (experimentalFeatures.responseActionGetFileEnabled) { - subFeatureIds.push(SecuritySubFeatureId.fileOperations); - } - // planned for 8.8 - if (experimentalFeatures.responseActionExecuteEnabled) { - subFeatureIds.push(SecuritySubFeatureId.executeAction); - } + const subFeatureIds: SecuritySubFeatureId[] = [ + SecuritySubFeatureId.endpointList, + SecuritySubFeatureId.trustedApplications, + SecuritySubFeatureId.hostIsolationExceptions, + SecuritySubFeatureId.blocklist, + SecuritySubFeatureId.eventFilters, + SecuritySubFeatureId.policyManagement, + SecuritySubFeatureId.responseActionsHistory, + SecuritySubFeatureId.hostIsolation, + SecuritySubFeatureId.processOperations, + SecuritySubFeatureId.fileOperations, + SecuritySubFeatureId.executeAction, + ]; return subFeatureIds; }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index f7c8b9cb52be9..4472fa3c0d691 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -182,13 +182,9 @@ export const getHostEndpoint = async ( const fleetAgentId = endpointData.metadata.elastic.agent.id; const pendingActions = fleetAgentId - ? getPendingActionsSummary( - esClient.asInternalUser, - endpointMetadataService, - logger, - [fleetAgentId], - endpointContext.experimentalFeatures.pendingActionResponsesWithAck - ) + ? getPendingActionsSummary(esClient.asInternalUser, endpointMetadataService, logger, [ + fleetAgentId, + ]) .then((results) => { return results[0].pending_actions; }) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 10a801183345e..8bc6de24615e1 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -32100,8 +32100,6 @@ "xpack.securitySolution.endpointDetails.activityLog.logEntry.response.unisolationSuccessful": "Requête de libération de l'hôte reçue par Endpoint", "xpack.securitySolution.endpointDetails.overview": "Aperçu", "xpack.securitySolution.endpointDetails.responseActionsHistory": "Historique des actions de réponse", - "xpack.securitySolution.endpointManagement.noPermissionsSubText": "Vous devez disposer du rôle de superutilisateur pour utiliser cette fonctionnalité. Si vous ne disposez pas de ce rôle, ni d'autorisations pour modifier les rôles d'utilisateur, contactez votre administrateur Kibana.", - "xpack.securitySolution.endpointManagemnet.noPermissionsText": "Vous ne disposez pas des autorisations Kibana requises pour utiliser Elastic Security Administration", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "Politique appliquée", "xpack.securitySolution.endpointResponseActions.actionSubmitter.apiErrorDetails": "L'erreur suivante a été rencontrée :", "xpack.securitySolution.endpointResponseActions.executeAction.successTitle": "L'exécution de la commande a réussi.", @@ -33095,7 +33093,6 @@ "xpack.securitySolution.policy.list.subtitle": "Utiliser les politiques pour personnaliser les protections des points de terminaison et de charge de travail cloud, et d'autres configurations", "xpack.securitySolution.policy.list.title": "Politiques", "xpack.securitySolution.policy.list.updatedAt": "Dernière mise à jour", - "xpack.securitySolution.policyDetails.backToEndpointList": "Afficher tous les points de terminaison", "xpack.securitySolution.policyDetails.backToPolicyButton": "Retour à la liste des politiques", "xpack.securitySolution.policyDetails.missingArtifactAccess": "Vous ne disposez pas des autorisations Kibana requises pour utiliser l'artefact donné.", "xpack.securitySolution.policyList.packageVersionError": "Erreur lors de la récupération de la version du pack de points de terminaison", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 48166bcd4c77b..6fef718d2c05f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -32081,8 +32081,6 @@ "xpack.securitySolution.endpointDetails.activityLog.logEntry.response.unisolationSuccessful": "エンドポイントが受信したホストリリースリクエスト", "xpack.securitySolution.endpointDetails.overview": "概要", "xpack.securitySolution.endpointDetails.responseActionsHistory": "対応アクション履歴", - "xpack.securitySolution.endpointManagement.noPermissionsSubText": "この機能を使用するには、スーパーユーザーロールが必要です。スーパーユーザーロールがなく、ユーザーロールを編集する権限もない場合は、Kibana管理者に問い合わせてください。", - "xpack.securitySolution.endpointManagemnet.noPermissionsText": "Elastic Security Administrationを使用するために必要なKibana権限がありません。", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "ポリシーが適用されました", "xpack.securitySolution.endpointResponseActions.actionSubmitter.apiErrorDetails": "次のエラーが発生しました:", "xpack.securitySolution.endpointResponseActions.executeAction.successTitle": "コマンド実行が成功しました。", @@ -33076,7 +33074,6 @@ "xpack.securitySolution.policy.list.subtitle": "ポリシーを使用して、エンドポイントおよびクラウドワークロード保護、ならびに他の構成をカスタマイズ", "xpack.securitySolution.policy.list.title": "ポリシー", "xpack.securitySolution.policy.list.updatedAt": "最終更新", - "xpack.securitySolution.policyDetails.backToEndpointList": "すべてのエンドポイントを表示", "xpack.securitySolution.policyDetails.backToPolicyButton": "ポリシーリストに戻る", "xpack.securitySolution.policyDetails.missingArtifactAccess": "特定のアーティファクトを使用するために必要なKibana権限がありません。", "xpack.securitySolution.policyList.packageVersionError": "エンドポイントパッケージバージョンの取得エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 890c2cfaa9f84..1385dfba36fd6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -32077,8 +32077,6 @@ "xpack.securitySolution.endpointDetails.activityLog.logEntry.response.unisolationSuccessful": "终端收到释放主机请求", "xpack.securitySolution.endpointDetails.overview": "概览", "xpack.securitySolution.endpointDetails.responseActionsHistory": "响应操作历史记录", - "xpack.securitySolution.endpointManagement.noPermissionsSubText": "您必须具有超级用户角色才能使用此功能。如果您不具有超级用户角色,且无权编辑用户角色,请与 Kibana 管理员联系。", - "xpack.securitySolution.endpointManagemnet.noPermissionsText": "您没有所需的 Kibana 权限,无法使用 Elastic Security 管理", "xpack.securitySolution.endpointPolicyStatus.tooltipTitleLabel": "已应用策略", "xpack.securitySolution.endpointResponseActions.actionSubmitter.apiErrorDetails": "遇到以下错误:", "xpack.securitySolution.endpointResponseActions.executeAction.successTitle": "命令执行成功。", @@ -33072,7 +33070,6 @@ "xpack.securitySolution.policy.list.subtitle": "使用策略定制终端和云工作负载防护及其他配置", "xpack.securitySolution.policy.list.title": "策略", "xpack.securitySolution.policy.list.updatedAt": "上次更新时间", - "xpack.securitySolution.policyDetails.backToEndpointList": "查看所有终端", "xpack.securitySolution.policyDetails.backToPolicyButton": "返回到策略列表", "xpack.securitySolution.policyDetails.missingArtifactAccess": "您没有所需 Kibana 权限,无法使用给定项目。", "xpack.securitySolution.policyList.packageVersionError": "检索终端软件包版本时出错", diff --git a/x-pack/test/defend_workflows_cypress/endpoint_config.ts b/x-pack/test/defend_workflows_cypress/endpoint_config.ts index a6e3f15934b1a..a1e437a442a87 100644 --- a/x-pack/test/defend_workflows_cypress/endpoint_config.ts +++ b/x-pack/test/defend_workflows_cypress/endpoint_config.ts @@ -16,7 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const config = defendWorkflowsCypressConfig.getAll(); const hostIp = getLocalhostRealIp(); - const enabledFeatureFlags: Array = ['responseActionExecuteEnabled']; + const enabledFeatureFlags: Array = []; return { ...config, diff --git a/x-pack/test/security_solution_endpoint_api_int/config.ts b/x-pack/test/security_solution_endpoint_api_int/config.ts index aaae0787d3c82..0f2f245378a3c 100644 --- a/x-pack/test/security_solution_endpoint_api_int/config.ts +++ b/x-pack/test/security_solution_endpoint_api_int/config.ts @@ -31,12 +31,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.fleet.packages.0.version=latest`, // this will be removed in 8.7 when the file upload feature is released `--xpack.fleet.enableExperimental.0=diagnosticFileUploadEnabled`, - // this will be removed in 8.7 when the artifacts RBAC is released - `--xpack.securitySolution.enableExperimental=${JSON.stringify([ - 'endpointRbacEnabled', - 'responseActionGetFileEnabled', - 'responseActionExecuteEnabled', - ])}`, + // set any experimental feature flags for testing + `--xpack.securitySolution.enableExperimental=${JSON.stringify([])}`, ], }, };