[Security Solution] Static config settings for serverless (#167856)

## Summary

This PR implements a standard way to have different static settings for
the serverless and ess (stateful) environments. It centralizes flags,
which were set using different approaches previously, in a single
configuration.

This aims to make it easier for developers to enable/disable parts of
the application in serverless projects.

Default:
```
  sideNavEnabled: true,
  ILMEnabled: true,
  ESQLEnabled: true,
```

Serverless:
```
xpack.securitySolution.offeringSettings: {
    sideNavEnabled: false, # Internal security side navigation disabled, the serverless global chrome navigation is used instead
    ILMEnabled: false, # Index Lifecycle Management (ILM) functionalities disabled, not supported by serverless Elasticsearch
    ESQLEnabled: false, # ES|QL disabled, not supported by serverless Elasticsearch
  }
```

### Consume the settings

#### Server 
- Plugin parsed `ConfigType`:
`this.config.settings.ESQLEnabled`

#### UI
- Plugin attribute: 
`this.configSettings.ESQLEnabled`.
- Components can access it from Kibana services:
`useKibana().services.configSettings.ESQLEnabled;`

---------

Co-authored-by: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com>
This commit is contained in:
Sergi Massaneda 2023-10-09 15:11:51 +02:00 committed by GitHub
parent 6e548c0ce9
commit c7df950bd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 161 additions and 68 deletions

View file

@ -17,6 +17,12 @@ xpack.securitySolutionServerless.productTypes:
{ product_line: 'endpoint', product_tier: 'complete' },
]
xpack.securitySolution.offeringSettings: {
sideNavEnabled: false, # Internal security side navigation disabled, the serverless global chrome navigation is used instead
ILMEnabled: false, # Index Lifecycle Management (ILM) functionalities disabled, not supported by serverless Elasticsearch
ESQLEnabled: false, # ES|QL disabled, not supported by serverless Elasticsearch
}
## Set the home route
uiSettings.overrides.defaultRoute: /app/security/get_started
@ -33,10 +39,6 @@ xpack.fleet.internal.registry.spec.max: '3.0'
# xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false
# xpack.fleet.internal.registry.spec.min: '3.0'
# Serverless security specific options
xpack.securitySolution.enableExperimental:
- esqlRulesDisabled
xpack.ml.ad.enabled: true
xpack.ml.dfa.enabled: true
xpack.ml.nlp.enabled: false

View file

@ -316,6 +316,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.spaces.allowFeatureVisibility (any)',
'xpack.securitySolution.enableExperimental (array)',
'xpack.securitySolution.prebuiltRulesPackageVersion (string)',
'xpack.securitySolution.offeringSettings (record)',
'xpack.snapshot_restore.slm_ui.enabled (boolean)',
'xpack.snapshot_restore.ui.enabled (boolean)',
'xpack.stack_connectors.enableExperimental (array)',

View file

@ -0,0 +1,65 @@
/*
* 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 interface ConfigSettings {
/**
* Security solution internal side navigation enabled
*/
sideNavEnabled: boolean;
/**
* Index Lifecycle Management (ILM) feature enabled.
*/
ILMEnabled: boolean;
/**
* ESQL queries enabled.
*/
ESQLEnabled: boolean;
}
/**
* A list of allowed values that can be override in `xpack.securitySolution.offeringSettings`.
* This object is then used to validate and parse the value entered.
*/
export const defaultSettings: ConfigSettings = Object.freeze({
sideNavEnabled: true,
ILMEnabled: true,
ESQLEnabled: true,
});
type ConfigSettingsKey = keyof ConfigSettings;
/**
* Parses the string value used in `xpack.securitySolution.offeringSettings` kibana configuration,
*
* @param offeringSettings
*/
export const parseConfigSettings = (
offeringSettings: Record<string, boolean>
): { settings: ConfigSettings; invalid: string[] } => {
const configSettings: Partial<ConfigSettings> = {};
const invalidKeys: string[] = [];
for (const optionKey in offeringSettings) {
if (defaultSettings[optionKey as ConfigSettingsKey] == null) {
invalidKeys.push(optionKey);
} else {
configSettings[optionKey as ConfigSettingsKey] = offeringSettings[optionKey];
}
}
const settings: ConfigSettings = Object.freeze({
...defaultSettings,
...configSettings,
});
return {
settings,
invalid: invalidKeys,
};
};
export const getDefaultConfigSettings = (): ConfigSettings => ({ ...defaultSettings });

View file

@ -6,19 +6,20 @@
*/
import { renderHook } from '@testing-library/react-hooks';
import { BehaviorSubject } from 'rxjs';
import { useSecuritySolutionNavigation } from './use_security_solution_navigation';
jest.mock('../breadcrumbs', () => ({
useBreadcrumbsNav: () => jest.fn(),
}));
const mockIsSidebarEnabled$ = new BehaviorSubject(true);
const mockIsSideNavEnabled = jest.fn(() => true);
jest.mock('../../../lib/kibana/kibana_react', () => {
return {
useKibana: () => ({
services: {
isSidebarEnabled$: mockIsSidebarEnabled$.asObservable(),
configSettings: {
sideNavEnabled: mockIsSideNavEnabled(),
},
},
}),
};
@ -26,7 +27,6 @@ jest.mock('../../../lib/kibana/kibana_react', () => {
describe('Security Solution Navigation', () => {
beforeEach(() => {
mockIsSidebarEnabled$.next(true);
jest.clearAllMocks();
});
@ -44,7 +44,7 @@ describe('Security Solution Navigation', () => {
});
it('should return undefined props when disabled', () => {
mockIsSidebarEnabled$.next(false);
mockIsSideNavEnabled.mockReturnValueOnce(false);
const { result } = renderHook(useSecuritySolutionNavigation);
expect(result.current).toEqual(undefined);
});

View file

@ -12,7 +12,6 @@
*/
import React from 'react';
import useObservable from 'react-use/lib/useObservable';
import { i18n } from '@kbn/i18n';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import { useKibana } from '../../../lib/kibana';
@ -24,12 +23,11 @@ const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mai
});
export const useSecuritySolutionNavigation = (): KibanaPageTemplateProps['solutionNav'] => {
const { isSidebarEnabled$ } = useKibana().services;
const isSidebarEnabled = useObservable(isSidebarEnabled$);
const { sideNavEnabled } = useKibana().services.configSettings;
useBreadcrumbsNav();
if (!isSidebarEnabled) {
if (!sideNavEnabled) {
return undefined;
}

View file

@ -52,6 +52,7 @@ import { NavigationProvider } from '@kbn/security-solution-navigation';
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
import { savedSearchPluginMock } from '@kbn/saved-search-plugin/public/mocks';
import { contractStartServicesMock } from '../../../mocks';
import { getDefaultConfigSettings } from '../../../../common/config_settings';
const mockUiSettings: Record<string, unknown> = {
[DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' },
@ -126,6 +127,7 @@ export const createStartServicesMock = (
return {
...core,
...contractStartServicesMock,
configSettings: getDefaultConfigSettings(),
apm,
cases,
unifiedSearch,

View file

@ -19,6 +19,7 @@ export interface ServerApiError {
export interface SecuritySolutionUiConfigType {
enableExperimental: string[];
prebuiltRulesPackageVersion?: string;
offeringSettings: Record<string, boolean>;
}
/**

View file

@ -15,10 +15,20 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex
jest.mock('../../../../common/lib/kibana');
jest.mock('../../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false),
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false), // enabled (esqlRulesDisabled = false)
}));
const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
const mockESQLEnabled = jest.fn(() => true);
jest.mock('../../../../common/lib/kibana', () => {
const useKibana = jest.requireActual('../../../../common/lib/kibana').useKibana;
return {
useKibana: () => ({
services: { ...useKibana().services, configSettings: { ESQLEnabled: mockESQLEnabled() } },
}),
};
});
describe('SelectRuleType', () => {
it('renders correctly', () => {
const Component = () => {
@ -187,8 +197,27 @@ describe('SelectRuleType', () => {
expect(wrapper.find('[data-test-subj="esqlRuleType"]').exists()).toBeTruthy();
});
it('should not render "esql" rule type if its feature disabled', () => {
useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
it('should not render "esql" rule type if esql setting is disabled', () => {
mockESQLEnabled.mockReturnValueOnce(false);
const Component = () => {
const field = useFormFieldMock();
return (
<SelectRuleType
field={field}
describedByIds={[]}
isUpdateView={false}
hasValidLicense={true}
isMlAdmin={true}
/>
);
};
const wrapper = shallow(<Component />);
expect(wrapper.find('[data-test-subj="esqlRuleType"]').exists()).toBeFalsy();
});
it('should not render "esql" rule type if the feature flag is disabled', () => {
useIsExperimentalFeatureEnabledMock.mockReturnValue(true); // disabled (esqlRulesDisabled = true)
const Component = () => {
const field = useFormFieldMock();

View file

@ -23,6 +23,7 @@ import * as i18n from './translations';
import { MlCardDescription } from './ml_card_description';
import { TechnicalPreviewBadge } from '../technical_preview_badge';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useKibana } from '../../../../common/lib/kibana';
interface SelectRuleTypeProps {
describedByIds: string[];
@ -49,7 +50,9 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = memo(
const setNewTerms = useCallback(() => setType('new_terms'), [setType]);
const setEsql = useCallback(() => setType('esql'), [setType]);
const isEsqlFeatureEnabled = !useIsExperimentalFeatureEnabled('esqlRulesDisabled');
const isEsqlSettingEnabled = useKibana().services.configSettings.ESQLEnabled;
const isEsqlFeatureFlagEnabled = !useIsExperimentalFeatureEnabled('esqlRulesDisabled');
const isEsqlFeatureEnabled = isEsqlSettingEnabled && isEsqlFeatureFlagEnabled;
const eqlSelectableConfig = useMemo(
() => ({

View file

@ -16,10 +16,8 @@ const upselling = new UpsellingService();
export const contractStartServicesMock: ContractStartServices = {
extraRoutes$: of([]),
isSidebarEnabled$: of(true),
getComponent$: jest.fn(),
upselling,
dataQualityPanelConfig: undefined,
};
const setupMock = (): PluginSetup => ({
@ -27,12 +25,10 @@ const setupMock = (): PluginSetup => ({
experimentalFeatures: allowedExperimentalValues, // default values
setAppLinksSwitcher: jest.fn(),
setDeepLinksFormatter: jest.fn(),
setDataQualityPanelConfig: jest.fn(),
});
const startMock = (): PluginStart => ({
getNavLinks$: jest.fn(() => new BehaviorSubject<NavigationLink[]>([])),
setIsSidebarEnabled: jest.fn(),
setComponents: jest.fn(),
getBreadcrumbsNav$: jest.fn(
() => new BehaviorSubject<BreadcrumbsNav>({ leading: [], trailing: [] })

View file

@ -8,8 +8,6 @@
import type { SecuritySubPlugin } from '../app/types';
import { routes } from './routes';
export * from './types';
export class Overview {
public setup() {}

View file

@ -8,7 +8,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { of } from 'rxjs';
import { useKibana as mockUseKibana } from '../../common/lib/kibana/__mocks__';
import { TestProviders } from '../../common/mock';
@ -16,7 +15,6 @@ import { DataQuality } from './data_quality';
import { HOT, WARM, UNMANAGED } from './translations';
const mockedUseKibana = mockUseKibana();
const mockIsILMAvailable = of(true);
jest.mock('../../common/components/landing_page');
jest.mock('../../common/lib/kibana', () => {
@ -51,7 +49,7 @@ jest.mock('../../common/lib/kibana', () => {
useCasesAddToNewCaseFlyout: jest.fn(),
},
},
isILMAvailable$: mockIsILMAvailable,
configSettings: { ILMEnabled: true },
},
}),
useUiSetting$: () => ['0,0.[000]'],

View file

@ -54,10 +54,8 @@ import type {
ReportDataQualityCheckAllCompletedParams,
ReportDataQualityIndexCheckedParams,
} from '../../common/lib/telemetry';
import type { DataQualityPanelConfig } from '../types';
const LOCAL_STORAGE_KEY = 'dataQualityDashboardLastChecked';
const defaultDataQualityPanelConfig: DataQualityPanelConfig = { isILMAvailable: true };
const comboBoxStyle: React.CSSProperties = {
width: '322px',
@ -158,8 +156,8 @@ const DataQualityComponent: React.FC = () => {
const [selectedOptions, setSelectedOptions] = useState<EuiComboBoxOptionOption[]>(defaultOptions);
const { indicesExist, loading: isSourcererLoading, selectedPatterns } = useSourcererDataView();
const { signalIndexName, loading: isSignalIndexNameLoading } = useSignalIndex();
const { dataQualityPanelConfig = defaultDataQualityPanelConfig, cases } = useKibana().services;
const { isILMAvailable } = dataQualityPanelConfig;
const { configSettings, cases } = useKibana().services;
const isILMAvailable = configSettings.ILMEnabled;
const [startDate, setStartTime] = useState<string>();
const [endDate, setEndTime] = useState<string>();
@ -173,7 +171,7 @@ const DataQualityComponent: React.FC = () => {
};
useEffect(() => {
if (isILMAvailable === false) {
if (!isILMAvailable) {
setStartTime(DEFAULT_START_TIME);
setEndTime(DEFAULT_END_TIME);
}

View file

@ -1,10 +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 interface DataQualityPanelConfig {
isILMAvailable: boolean;
}

View file

@ -57,6 +57,7 @@ import { LazyEndpointCustomAssetsExtension } from './management/pages/policy/vie
import type { SecurityAppStore } from './common/store/types';
import { PluginContract } from './plugin_contract';
import { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service';
import { parseConfigSettings, type ConfigSettings } from '../common/config_settings';
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
/**
@ -84,6 +85,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
private telemetry: TelemetryService;
readonly experimentalFeatures: ExperimentalFeatures;
readonly configSettings: ConfigSettings;
private queryService: QueryService = new QueryService();
private nowProvider: NowProvider = new NowProvider();
@ -92,6 +94,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
this.experimentalFeatures = parseExperimentalConfigValue(
this.config.enableExperimental || []
).features;
this.configSettings = parseConfigSettings(this.config.offeringSettings ?? {}).settings;
this.kibanaVersion = initializerContext.env.packageInfo.version;
this.kibanaBranch = initializerContext.env.packageInfo.branch;
this.prebuiltRulesPackageVersion = this.config.prebuiltRulesPackageVersion;
@ -185,6 +188,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
...coreStart,
...startPlugins,
...this.contract.getStartServices(),
configSettings: this.configSettings,
apm,
savedObjectsTagging: savedObjectsTaggingOss.getTaggingApi(),
setHeaderActionMenu: params.setHeaderActionMenu,

View file

@ -9,7 +9,6 @@ import { BehaviorSubject } from 'rxjs';
import type { RouteProps } from 'react-router-dom';
import { UpsellingService } from '@kbn/security-solution-upselling/service';
import type { ContractStartServices, PluginSetup, PluginStart } from './types';
import type { DataQualityPanelConfig } from './overview/types';
import type { AppLinksSwitcher } from './common/links';
import type { DeepLinksFormatter } from './common/links/deep_links';
import type { ExperimentalFeatures } from '../common/experimental_features';
@ -18,17 +17,14 @@ import { breadcrumbsNav$ } from './common/breadcrumbs';
import { ContractComponentsService } from './contract_components';
export class PluginContract {
public isSidebarEnabled$: BehaviorSubject<boolean>;
public componentsService: ContractComponentsService;
public upsellingService: UpsellingService;
public extraRoutes$: BehaviorSubject<RouteProps[]>;
public appLinksSwitcher: AppLinksSwitcher;
public deepLinksFormatter?: DeepLinksFormatter;
public dataQualityPanelConfig?: DataQualityPanelConfig;
constructor(private readonly experimentalFeatures: ExperimentalFeatures) {
this.extraRoutes$ = new BehaviorSubject<RouteProps[]>([]);
this.isSidebarEnabled$ = new BehaviorSubject<boolean>(true);
this.componentsService = new ContractComponentsService();
this.upsellingService = new UpsellingService();
this.appLinksSwitcher = (appLinks) => appLinks;
@ -37,10 +33,8 @@ export class PluginContract {
public getStartServices(): ContractStartServices {
return {
extraRoutes$: this.extraRoutes$.asObservable(),
isSidebarEnabled$: this.isSidebarEnabled$.asObservable(),
getComponent$: this.componentsService.getComponent$.bind(this.componentsService),
upselling: this.upsellingService,
dataQualityPanelConfig: this.dataQualityPanelConfig,
};
}
@ -54,9 +48,6 @@ export class PluginContract {
setDeepLinksFormatter: (deepLinksFormatter) => {
this.deepLinksFormatter = deepLinksFormatter;
},
setDataQualityPanelConfig: (dataQualityPanelConfig) => {
this.dataQualityPanelConfig = dataQualityPanelConfig;
},
};
}
@ -64,8 +55,6 @@ export class PluginContract {
return {
getNavLinks$: () => navLinks$,
setExtraRoutes: (extraRoutes) => this.extraRoutes$.next(extraRoutes),
setIsSidebarEnabled: (isSidebarEnabled: boolean) =>
this.isSidebarEnabled$.next(isSidebarEnabled),
setComponents: (components) => {
this.componentsService.setComponents(components);
},

View file

@ -81,8 +81,8 @@ import type { BreadcrumbsNav } from './common/breadcrumbs/types';
import type { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service';
import type { ExperimentalFeatures } from '../common/experimental_features';
import type { DeepLinksFormatter } from './common/links/deep_links';
import type { DataQualityPanelConfig } from './overview/types';
import type { SetComponents, GetComponent$ } from './contract_components';
import type { ConfigSettings } from '../common/config_settings';
export interface SetupPlugins {
cloud?: CloudSetup;
@ -147,15 +147,14 @@ export interface StartPluginsDependencies extends StartPlugins {
export interface ContractStartServices {
extraRoutes$: Observable<RouteProps[]>;
isSidebarEnabled$: Observable<boolean>;
getComponent$: GetComponent$;
upselling: UpsellingService;
dataQualityPanelConfig: DataQualityPanelConfig | undefined;
}
export type StartServices = CoreStart &
StartPlugins &
ContractStartServices & {
configSettings: ConfigSettings;
storage: Storage;
sessionStorage: Storage;
apm: ApmBase;
@ -181,13 +180,11 @@ export interface PluginSetup {
experimentalFeatures: ExperimentalFeatures;
setAppLinksSwitcher: (appLinksSwitcher: AppLinksSwitcher) => void;
setDeepLinksFormatter: (deepLinksFormatter: DeepLinksFormatter) => void;
setDataQualityPanelConfig: (dataQualityPanelConfig: DataQualityPanelConfig) => void;
}
export interface PluginStart {
getNavLinks$: () => Observable<NavigationLink[]>;
setExtraRoutes: (extraRoutes: RouteProps[]) => void;
setIsSidebarEnabled: (isSidebarEnabled: boolean) => void;
setComponents: SetComponents;
getBreadcrumbsNav$: () => Observable<BreadcrumbsNav>;
getUpselling: () => UpsellingService;

View file

@ -8,6 +8,7 @@
import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants';
import type { ExperimentalFeatures } from '../common/experimental_features';
import { parseExperimentalConfigValue } from '../common/experimental_features';
import { getDefaultConfigSettings } from '../common/config_settings';
import type { ConfigType } from './config';
export const createMockConfig = (): ConfigType => {
@ -26,7 +27,7 @@ export const createMockConfig = (): ConfigType => {
alertMergeStrategy: 'missingFields',
alertIgnoreFields: [],
maxUploadResponseActionFileBytes: 26214400,
settings: getDefaultConfigSettings(),
experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features,
enabled: true,
};

View file

@ -11,6 +11,7 @@ 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 { parseExperimentalConfigValue } from '../common/experimental_features';
import { parseConfigSettings, type ConfigSettings } from '../common/config_settings';
export const configSchema = schema.object({
maxRuleImportExportSize: schema.number({ defaultValue: 10000 }),
@ -121,12 +122,24 @@ export const configSchema = schema.object({
defaultValue: 26214400, // 25MB,
max: 104857600, // 100MB,
}),
/**
* Defines the settings for a specific offering of the Security Solution app.
* They override the default values.
* @example
* xpack.securitySolution.offeringSettings: {
* "ILMEnabled": false,
* }
*/
offeringSettings: schema.recordOf(schema.string(), schema.boolean(), {
defaultValue: {},
}),
});
export type ConfigSchema = TypeOf<typeof configSchema>;
export type ConfigType = ConfigSchema & {
export type ConfigType = Omit<ConfigSchema, 'offeringSettings'> & {
experimentalFeatures: ExperimentalFeatures;
settings: ConfigSettings;
};
export const createConfig = (context: PluginInitializerContext): ConfigType => {
@ -146,8 +159,20 @@ ${invalid.map((key) => ` - ${key}`).join('\n')}
`);
}
const { invalid: invalidConfigSettings, settings } = parseConfigSettings(
pluginConfig.offeringSettings
);
if (invalidConfigSettings.length) {
logger.warn(`Unsupported "xpack.securitySolution.offeringSettings" values detected.
The following configuration values are no longer supported and should be removed from the kibana configuration file:
${invalidConfigSettings.map((key) => ` - ${key}`).join('\n')}
`);
}
return {
...pluginConfig,
experimentalFeatures,
settings,
};
};

View file

@ -21,6 +21,7 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
exposeToBrowser: {
enableExperimental: true,
prebuiltRulesPackageVersion: true,
offeringSettings: true,
},
schema: configSchema,
deprecations: ({ renameFromRoot, unused }) => [

View file

@ -421,7 +421,7 @@ export const previewRulesRoute = async (
);
break;
case 'esql':
if (config.experimentalFeatures.esqlRulesDisabled) {
if (!config.settings.ESQLEnabled || config.experimentalFeatures.esqlRulesDisabled) {
throw Error('ES|QL rule type is not supported');
}
const esqlAlertType = previewRuleTypeWrapper(createEsqlAlertType(ruleOptions));

View file

@ -282,7 +282,7 @@ export class Plugin implements ISecuritySolutionPlugin {
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper(securityRuleTypeOptions);
plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(ruleOptions)));
if (!experimentalFeatures.esqlRulesDisabled) {
if (config.settings.ESQLEnabled && !experimentalFeatures.esqlRulesDisabled) {
plugins.alerting.registerType(securityRuleTypeWrapper(createEsqlAlertType(ruleOptions)));
}
plugins.alerting.registerType(

View file

@ -28,11 +28,8 @@ export class SecuritySolutionEssPlugin
{
public setup(
_core: CoreSetup,
setupDeps: SecuritySolutionEssPluginSetupDeps
_setupDeps: SecuritySolutionEssPluginSetupDeps
): SecuritySolutionEssPluginSetup {
const { securitySolution } = setupDeps;
securitySolution.setDataQualityPanelConfig({ isILMAvailable: true });
return {};
}

View file

@ -35,8 +35,7 @@ export const setupNavigation = (
};
export const startNavigation = (services: Services, config: ServerlessSecurityPublicConfig) => {
const { serverless, securitySolution, management } = services;
securitySolution.setIsSidebarEnabled(false);
const { serverless, management } = services;
serverless.setProjectHome(APP_PATH);
const projectNavigationTree = new ProjectNavigationTree(services);

View file

@ -29,7 +29,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
...svlSharedConfig.get('kbnTestServer.serverArgs'),
'--serverless=security',
'--xpack.encryptedSavedObjects.encryptionKey="abcdefghijklmnopqrstuvwxyz123456"',
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['esqlRulesDisabled'])}`,
`--xpack.securitySolutionServerless.productTypes=${JSON.stringify([
{ product_line: 'security', product_tier: 'complete' },
{ product_line: 'endpoint', product_tier: 'complete' },