Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.13][Security Solution][Endpoint] Add beta badge to sentinel one connector cards/flyout and responder/isolation action flyouts #176228

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
52e7fee
add beta badge to sentinel one connector cards/flyout
ashokaditya Feb 5, 2024
a5dedfb
add beta tag to sentinel one responder
ashokaditya Feb 7, 2024
2ec1340
Add beta badge to alert isolate/release action flyout
ashokaditya Feb 7, 2024
41a950b
Update header.test.tsx
ashokaditya Feb 7, 2024
af0bb4b
Update beta badge tooltip text
ashokaditya Feb 7, 2024
194852f
add tests
ashokaditya Feb 7, 2024
5f09dc0
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Feb 7, 2024
9797a21
beta badges for responder and isolate/release flyout
ashokaditya Feb 7, 2024
fcb7684
update tests
ashokaditya Feb 7, 2024
6d2a68e
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 7, 2024
6bb9d7f
fix lint and import
ashokaditya Feb 7, 2024
8d46b7e
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 7, 2024
7818a8f
Connector shows tech preview badge without ff
ashokaditya Feb 8, 2024
a8d80a1
update tests
ashokaditya Feb 8, 2024
e99103b
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 8, 2024
f1d7821
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 9, 2024
0a4ab17
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 9, 2024
8b64722
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 9, 2024
a1081c2
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 9, 2024
3e32ee3
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 12, 2024
a6dad82
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 12, 2024
f0cde7a
Merge branch 'main' into task/dw-beta-badge-sentinel-one-8526
ashokaditya Feb 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export const BETA = i18n.translate('xpack.securitySolution.pages.common.beta', {
defaultMessage: 'Beta',
});

export const BETA_TOOLTIP = i18n.translate('xpack.securitySolution.pages.common.beta.tooltip', {
defaultMessage:
'This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.',
});

export const UPDATE_ALERT_STATUS_FAILED = (conflicts: number) =>
i18n.translate('xpack.securitySolution.pages.common.updateAlertStatusFailed', {
values: { conflicts },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ import React from 'react';
import { render } from '@testing-library/react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { useIsolateHostPanelContext } from './context';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { PanelHeader } from './header';
import { FLYOUT_HEADER_TITLE_TEST_ID } from './test_ids';
import { isAlertFromSentinelOneEvent } from '../../../common/utils/sentinelone_alert_check';

jest.mock('../../../common/hooks/use_experimental_features');
jest.mock('../../../common/utils/sentinelone_alert_check');
jest.mock('./context');

const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock;
const mockIsAlertFromSentinelOneEvent = isAlertFromSentinelOneEvent as jest.Mock;

const renderPanelHeader = () =>
render(
<IntlProvider locale="en">
Expand All @@ -22,21 +29,57 @@ const renderPanelHeader = () =>
);

describe('<PanelHeader />', () => {
(useIsolateHostPanelContext as jest.Mock).mockReturnValue({ isolateAction: 'isolateHost' });
beforeEach(() => {
mockUseIsExperimentalFeatureEnabled.mockReturnValue(false);
});

it.each([
{
isolateAction: 'isolateHost',
title: 'Isolate host',
},
{
isolateAction: 'unisolateHost',
title: 'Release host',
},
])('should display release host message', ({ isolateAction, title }) => {
(useIsolateHostPanelContext as jest.Mock).mockReturnValue({ isolateAction });

it('should display isolate host message', () => {
const { getByTestId } = renderPanelHeader();

expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('Isolate host');
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent(title);
});

it('should display release host message', () => {
(useIsolateHostPanelContext as jest.Mock).mockReturnValue({ isolateAction: 'unisolateHost' });
it.each(['isolateHost', 'unisolateHost'])(
logeekal marked this conversation as resolved.
Show resolved Hide resolved
'should display beta badge on %s host message for SentinelOne alerts',
(action) => {
(useIsolateHostPanelContext as jest.Mock).mockReturnValue({
isolateAction: action,
});
mockUseIsExperimentalFeatureEnabled.mockReturnValue(true);
mockIsAlertFromSentinelOneEvent.mockReturnValue(true);

const { getByTestId } = renderPanelHeader();
const { getByTestId } = renderPanelHeader();

expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('Release host');
});
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toHaveTextContent('Beta');
}
);

it.each(['isolateHost', 'unisolateHost'])(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting you to add one more test to test impact of experimental_flag = false on the visibility of the beta badge.

'should not display beta badge on %s host message for non-SentinelOne alerts',
(action) => {
(useIsolateHostPanelContext as jest.Mock).mockReturnValue({
isolateAction: action,
});
mockUseIsExperimentalFeatureEnabled.mockReturnValue(true);
mockIsAlertFromSentinelOneEvent.mockReturnValue(false);

const { getByTestId } = renderPanelHeader();

expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(FLYOUT_HEADER_TITLE_TEST_ID)).not.toHaveTextContent('Beta');
}
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
* 2.0.
*/

import { EuiTitle } from '@elastic/eui';
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import type { FC } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { BETA, BETA_TOOLTIP } from '../../../common/translations';
import { isAlertFromSentinelOneEvent } from '../../../common/utils/sentinelone_alert_check';
import { useIsolateHostPanelContext } from './context';
import { FLYOUT_HEADER_TITLE_TEST_ID } from './test_ids';
import { FlyoutHeader } from '../../shared/components/flyout_header';
Expand All @@ -17,20 +20,34 @@ import { FlyoutHeader } from '../../shared/components/flyout_header';
* Document details expandable right section header for the isolate host panel
*/
export const PanelHeader: FC = () => {
const { isolateAction } = useIsolateHostPanelContext();
const { isolateAction, dataFormattedForFieldBrowser: data } = useIsolateHostPanelContext();
const isSentinelOneAlert = isAlertFromSentinelOneEvent({ data });
const isSentinelOneV1Enabled = useIsExperimentalFeatureEnabled(
'responseActionsSentinelOneV1Enabled'
);

const title =
isolateAction === 'isolateHost' ? (
<FormattedMessage
id="xpack.securitySolution.flyout.isolateHost.isolateTitle"
defaultMessage="Isolate host"
/>
) : (
<FormattedMessage
id="xpack.securitySolution.flyout.isolateHost.releaseTitle"
defaultMessage="Release host"
/>
);
const title = (
<EuiFlexGroup responsive gutterSize="s">
<EuiFlexItem grow={false}>
{isolateAction === 'isolateHost' ? (
<FormattedMessage
id="xpack.securitySolution.flyout.isolateHost.isolateTitle"
defaultMessage="Isolate host"
/>
) : (
<FormattedMessage
id="xpack.securitySolution.flyout.isolateHost.releaseTitle"
defaultMessage="Release host"
/>
)}
</EuiFlexItem>
{isSentinelOneV1Enabled && isSentinelOneAlert && (
<EuiFlexItem grow={false}>
<EuiBetaBadge label={BETA} tooltipContent={BETA_TOOLTIP} />
</EuiFlexItem>
)}
</EuiFlexGroup>
);

return (
<FlyoutHeader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

import React, { useCallback } from 'react';
import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { BETA, BETA_TOOLTIP } from '../../common/translations';
import { useLicense } from '../../common/hooks/use_license';
import type { ImmutableArray } from '../../../common/endpoint/types';
import {
Expand All @@ -26,6 +28,7 @@ import {
import { useConsoleManager } from '../components/console';
import { MissingEncryptionKeyCallout } from '../components/missing_encryption_key_callout';
import { RESPONDER_PAGE_TITLE } from './translations';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';

type ShowResponseActionsConsole = (props: ResponderInfoProps) => void;

Expand All @@ -50,6 +53,9 @@ export const useWithShowResponder = (): ShowResponseActionsConsole => {
const consoleManager = useConsoleManager();
const endpointPrivileges = useUserPrivileges().endpointPrivileges;
const isEnterpriseLicense = useLicense().isEnterprise();
const isSentinelOneV1Enabled = useIsExperimentalFeatureEnabled(
'responseActionsSentinelOneV1Enabled'
);

return useCallback(
(props: ResponderInfoProps) => {
Expand Down Expand Up @@ -126,7 +132,19 @@ export const useWithShowResponder = (): ShowResponseActionsConsole => {
hostName,
},
consoleProps,
PageTitleComponent: () => <>{RESPONDER_PAGE_TITLE}</>,
PageTitleComponent: () => {
if (isSentinelOneV1Enabled && agentType === 'sentinel_one') {
return (
<EuiFlexGroup>
<EuiFlexItem>{RESPONDER_PAGE_TITLE}</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge label={BETA} tooltipContent={BETA_TOOLTIP} />
</EuiFlexItem>
</EuiFlexGroup>
);
}
return <>{RESPONDER_PAGE_TITLE}</>;
},
ActionComponents: endpointPrivileges.canReadActionsLogManagement
? [ActionLogButton]
: undefined,
Expand All @@ -140,6 +158,6 @@ export const useWithShowResponder = (): ShowResponseActionsConsole => {
.show();
}
},
[endpointPrivileges, isEnterpriseLicense, consoleManager]
[endpointPrivileges, isEnterpriseLicense, isSentinelOneV1Enabled, consoleManager]
);
};
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,6 @@
"@kbn/elastic-assistant-common",
"@kbn/core-elasticsearch-server-mocks",
"@kbn/lens-embeddable-utils",
"@kbn/esql-utils"
"@kbn/esql-utils",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues;
*/
export const allowedExperimentalValues = Object.freeze({
isMustacheAutocompleteOn: false,
// set to true to show tech preview badge on sentinel one connector
sentinelOneConnectorOn: true,
// set to true to show beta badge on sentinel one connector
// TODO: set to true when 8.13 is ready
sentinelOneConnectorOnBeta: false,
});

export type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,14 @@ export function registerConnectorTypes({
connectorTypeRegistry.register(getTinesConnectorType());
connectorTypeRegistry.register(getD3SecurityConnectorType());

if (ExperimentalFeaturesService.get().sentinelOneConnectorOn) {
// get sentinelOne connector type
// when either feature flag is enabled
if (
// 8.12
ExperimentalFeaturesService.get().sentinelOneConnectorOn ||
// 8.13
ExperimentalFeaturesService.get().sentinelOneConnectorOnBeta
) {
connectorTypeRegistry.register(getSentinelOneConnectorType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ import type {
ActionTypeModel as ConnectorTypeModel,
GenericValidationResult,
} from '@kbn/triggers-actions-ui-plugin/public';
import { getIsExperimentalFeatureEnabled } from '../../common/get_experimental_features';
import {
SENTINELONE_CONNECTOR_ID,
SENTINELONE_TITLE,
SUB_ACTION,
} from '../../../common/sentinelone/constants';
import type {
SentinelOneActionParams,
SentinelOneConfig,
SentinelOneSecrets,
SentinelOneActionParams,
} from '../../../common/sentinelone/types';

interface ValidationErrors {
Expand All @@ -31,11 +32,16 @@ export function getConnectorType(): ConnectorTypeModel<
SentinelOneSecrets,
SentinelOneActionParams
> {
const isSentinelOneBetaBadgeEnabled = getIsExperimentalFeatureEnabled(
'sentinelOneConnectorOnBeta'
);

return {
id: SENTINELONE_CONNECTOR_ID,
actionTypeTitle: SENTINELONE_TITLE,
iconClass: lazy(() => import('./logo')),
isExperimental: true,
isBeta: isSentinelOneBetaBadgeEnabled ? true : undefined,
isExperimental: isSentinelOneBetaBadgeEnabled ? undefined : true,
selectMessage: i18n.translate(
'xpack.stackConnectors.security.sentinelone.config.selectMessageText',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
*/

import React, { useEffect, useState } from 'react';
import { EuiFlexItem, EuiCard, EuiIcon, EuiFlexGrid, EuiSpacer } from '@elastic/eui';
import { EuiCard, EuiFlexGrid, EuiFlexItem, EuiIcon, EuiSpacer, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiToolTip } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { ActionType, ActionTypeIndex, ActionTypeRegistryContract } from '../../../types';
import { loadActionTypes } from '../../lib/action_connector_api';
import { actionTypeCompare } from '../../lib/action_type_compare';
import { checkActionTypeEnabled } from '../../lib/check_action_type_enabled';
import { useKibana } from '../../../common/lib/kibana';
import { SectionLoading } from '../../components/section_loading';
import { betaBadgeProps } from './beta_badge_props';
import { betaBadgeProps, technicalPreviewBadgeProps } from './beta_badge_props';

interface Props {
onActionTypeChange: (actionType: ActionType) => void;
Expand Down Expand Up @@ -77,12 +76,13 @@ export const ActionTypeMenu = ({
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const registeredActionTypes = Object.entries(actionTypesIndex ?? [])
.filter(
([id, details]) =>
actionTypeRegistry.has(id) &&
details.enabledInConfig === true &&
!actionTypeRegistry.get(id).hideInUi
!actionTypeRegistry.get(id).hideInUi &&
details.enabledInConfig
)
.map(([id, actionType]) => {
const actionTypeModel = actionTypeRegistry.get(id);
Expand All @@ -91,6 +91,7 @@ export const ActionTypeMenu = ({
selectMessage: actionTypeModel ? actionTypeModel.selectMessage : '',
actionType,
name: actionType.name,
isBeta: actionTypeModel.isBeta,
isExperimental: actionTypeModel.isExperimental,
};
});
Expand All @@ -101,7 +102,13 @@ export const ActionTypeMenu = ({
const checkEnabledResult = checkActionTypeEnabled(item.actionType);
const card = (
<EuiCard
betaBadgeProps={item.isExperimental ? betaBadgeProps : undefined}
betaBadgeProps={
item.isBeta
? betaBadgeProps
: item.isExperimental
? technicalPreviewBadgeProps
: undefined
}
titleSize="xs"
data-test-subj={`${item.actionType.id}-card`}
icon={<EuiIcon size="xl" type={item.iconClass} />}
Expand All @@ -117,7 +124,7 @@ export const ActionTypeMenu = ({
return (
<EuiFlexItem key={index}>
{checkEnabledResult.isEnabled && card}
{checkEnabledResult.isEnabled === false && (
{!checkEnabledResult.isEnabled && (
<EuiToolTip position="top" content={checkEnabledResult.message}>
{card}
</EuiToolTip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
import { i18n } from '@kbn/i18n';

export const betaBadgeProps = {
label: i18n.translate('xpack.triggersActionsUI.betaBadgeLabel', {
defaultMessage: 'Beta',
}),
tooltipContent: i18n.translate('xpack.triggersActionsUI.betaBadgeDescription', {
defaultMessage:
'This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.',
}),
};

export const technicalPreviewBadgeProps = {
label: i18n.translate('xpack.triggersActionsUI.technicalPreviewBadgeLabel', {
defaultMessage: 'Technical preview',
}),
Expand Down
Loading