mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Serverless][Security Solution][Endpoint] Restrict endpoint exceptions on serverless via plugin sub-features (#164107)
### What this PR changes
branched from elastic/kibana/pull/163759
- Introduces new AppFeatures package `@kbn/security-solution-features`
with the common logic and `AppFeatureService` to apply offering specific
configurations for Security Solution features independently for
Serverless and ESS. This logic is replacing the earlier `AppFeatures` in
order to introduce new Kibana feature privileges for serverless PLIs so
that new Kibana privileges introduced for serverless PLIs do not
affect/show up as new Kibana feature privileges in ESS.
- Gates endpoint exceptions on alerts/rules based on serverless PLI
configurations. On serverless `Endpoint exceptions` should be
accessible/seen only on endpoint essentials/complete.
New AppFeatures logic architecture diagram:

**Note:** Corresponding API changes related to endpoint exceptions will
be in a new PR, along with the last set of UX changes for hiding the
`Endpoint exceptions` tab from the Rules details page.
### How to review
- Setup for _Servlerless_
- Run `yarn es snapshot` on a terminal window to start ES.
- Copy `config/serverless.security.yml` to
`config/serverless.security.dev.yml`
- Run `yarn serverless-security --no-base-path` on another terminal
window to start kibana in serverless mode
- Run `node
x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_emulator.js
--asSuperuser` on a new window and then select `1` to `Load Endoints`
and then `1` to `Run` the loader script. This will load some fake
agents/alerts data to test with.
### Tests (Serverless)
- with
`{ product_line: 'security', product_tier: 'essentials' }` or `{
product_line: 'security', product_tier: 'complete' }`
and
`{ product_line: 'endpoint', product_tier: 'essentials' }` or `{
product_line: 'endpoint', product_tier: 'complete' }`
1. Navigate to Rules>Shared exception lists via
`http://localhost:5601/app/security/exceptions`
2. Test that you can see `Endpoint Security Exception List` card on the
shared exception lists page.
3. Navigate to `Alerts` page via `app/security/alerts`, you should see
endpoint alerts. If not, then click on `Manage Rules` and then
disable/enable `Endpoint Security` rules. That should trigger alerts to
show up on the Alerts table.
4. Click on `View Details` button under `Actions` column. Once the
flyout is visible, click on `Take Action` and verify that `Add Endpoint
exception` is visible/enabled/clickable on the menu.
5. Click on `More actions` button under `Actions` column and verify that
`Add Endpoint exception` is visible/enabled/clickable on the menu.
6. Click on `Investigate in timeline` button under `Actions` column;
when the timeline view is visible and the alert item is displayed, click
on buttons mentioned in 4. and 5. above and verify the same.
7. Navigate to `Rules`>`DetectionRules`>`Endpoint Security` rule under
the `Rules` table. Select the `Alerts` tab.
8. Click and verify `View details`,`More actions` and `Investigate in
timeline` buttons same as in 4., 5., 6. above.
9. You should be able to see the `Endpoint exceptions` tab as well.
Click and verify that you can see the tab's content.
- with
`{ product_line: 'security', product_tier: 'essentials' }` or `{
product_line: 'security', product_tier: 'complete' }`
1. Edit `config/serverless.security.dev.yml` so that `endpoint` product
line item is commented out.
2. Test that you can not see `Endpoint Security Exception List` card on
the shared exception lists page.
3. Items 4. 5. 6. as above but the menu items should be disabled. This
can be verified with fake data only as with a real endpoint, endpoint
alerts are actually not visible at all.
### Tests (ESS)
On the ESS side, endpoint exceptions are not affected by this change and
work as usual based on index privileges.
---------
Co-authored-by: semd <sergi.massaneda@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: YulNaumenko <jo.naumenko@gmail.com>
Co-authored-by: Pablo Neves Machado <pablo.nevesmachado@elastic.co>
Co-authored-by: Pablo Machado <machadoum@gmail.com>
This commit is contained in:
parent
73469bfb11
commit
6e367d94c9
107 changed files with 2200 additions and 1305 deletions
|
@ -1180,6 +1180,7 @@ module.exports = {
|
|||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'x-pack/packages/security-solution/features/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/packages/security-solution/navigation/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/security_solution_ess/**/*.{js,mjs,ts,tsx}',
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -600,6 +600,7 @@ x-pack/plugins/searchprofiler @elastic/platform-deployment-management
|
|||
x-pack/test/security_api_integration/packages/helpers @elastic/kibana-security
|
||||
x-pack/plugins/security @elastic/kibana-security
|
||||
x-pack/plugins/security_solution_ess @elastic/security-solution
|
||||
x-pack/packages/security-solution/features @elastic/security-threat-hunting-explore
|
||||
x-pack/test/cases_api_integration/common/plugins/security_solution @elastic/response-ops
|
||||
x-pack/packages/security-solution/navigation @elastic/security-threat-hunting-explore
|
||||
x-pack/plugins/security_solution @elastic/security-solution
|
||||
|
|
|
@ -605,6 +605,7 @@
|
|||
"@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler",
|
||||
"@kbn/security-plugin": "link:x-pack/plugins/security",
|
||||
"@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess",
|
||||
"@kbn/security-solution-features": "link:x-pack/packages/security-solution/features",
|
||||
"@kbn/security-solution-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/security_solution",
|
||||
"@kbn/security-solution-navigation": "link:x-pack/packages/security-solution/navigation",
|
||||
"@kbn/security-solution-plugin": "link:x-pack/plugins/security_solution",
|
||||
|
|
|
@ -121,7 +121,7 @@ pageLoadAssetSize:
|
|||
security: 81771
|
||||
securitySolution: 66738
|
||||
securitySolutionEss: 16573
|
||||
securitySolutionServerless: 40000
|
||||
securitySolutionServerless: 45000
|
||||
serverless: 16573
|
||||
serverlessObservability: 68747
|
||||
serverlessSearch: 71995
|
||||
|
|
|
@ -151,3 +151,23 @@ export type ArrayElement<A> = A extends ReadonlyArray<infer T> ? T : never;
|
|||
export type WithRequiredProperty<Type, Key extends keyof Type> = Omit<Type, Key> & {
|
||||
[Property in Key]-?: Type[Property];
|
||||
};
|
||||
|
||||
// Recursive partial object type. inspired by EUI RecursivePartial
|
||||
export type RecursivePartial<T> = {
|
||||
[P in keyof T]?: T[P] extends NonAny[]
|
||||
? T[P]
|
||||
: T[P] extends readonly NonAny[]
|
||||
? T[P]
|
||||
: T[P] extends Array<infer U>
|
||||
? Array<RecursivePartial<U>>
|
||||
: T[P] extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<RecursivePartial<U>>
|
||||
: T[P] extends Set<infer V>
|
||||
? Set<RecursivePartial<V>>
|
||||
: T[P] extends Map<infer K, infer V>
|
||||
? Map<K, RecursivePartial<V>>
|
||||
: T[P] extends NonAny
|
||||
? T[P]
|
||||
: RecursivePartial<T[P]>;
|
||||
};
|
||||
type NonAny = number | boolean | string | symbol | null;
|
||||
|
|
|
@ -1194,6 +1194,8 @@
|
|||
"@kbn/security-plugin/*": ["x-pack/plugins/security/*"],
|
||||
"@kbn/security-solution-ess": ["x-pack/plugins/security_solution_ess"],
|
||||
"@kbn/security-solution-ess/*": ["x-pack/plugins/security_solution_ess/*"],
|
||||
"@kbn/security-solution-features": ["x-pack/packages/security-solution/features"],
|
||||
"@kbn/security-solution-features/*": ["x-pack/packages/security-solution/features/*"],
|
||||
"@kbn/security-solution-fixtures-plugin": ["x-pack/test/cases_api_integration/common/plugins/security_solution"],
|
||||
"@kbn/security-solution-fixtures-plugin/*": ["x-pack/test/cases_api_integration/common/plugins/security_solution/*"],
|
||||
"@kbn/security-solution-navigation": ["x-pack/packages/security-solution/navigation"],
|
||||
|
|
4
x-pack/packages/security-solution/features/README.mdx
Normal file
4
x-pack/packages/security-solution/features/README.mdx
Normal file
|
@ -0,0 +1,4 @@
|
|||
## Security Solution App Features
|
||||
|
||||
This package provides resources to be used for Security Solution app features
|
||||
|
10
x-pack/packages/security-solution/features/app_features.ts
Normal file
10
x-pack/packages/security-solution/features/app_features.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 { getSecurityFeature } from './src/security';
|
||||
export { getCasesFeature } from './src/cases';
|
||||
export { getAssistantFeature } from './src/assistant';
|
12
x-pack/packages/security-solution/features/config.ts
Normal file
12
x-pack/packages/security-solution/features/config.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 { securityDefaultAppFeaturesConfig } from './src/security/app_feature_config';
|
||||
export { getCasesDefaultAppFeaturesConfig } from './src/cases/app_feature_config';
|
||||
export { assistantDefaultAppFeaturesConfig } from './src/assistant/app_feature_config';
|
||||
|
||||
export { createEnabledAppFeaturesConfigMap } from './src/helpers';
|
7
x-pack/packages/security-solution/features/index.ts
Normal file
7
x-pack/packages/security-solution/features/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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 * from './src/types';
|
12
x-pack/packages/security-solution/features/jest.config.js
Normal file
12
x-pack/packages/security-solution/features/jest.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/security-solution/features'],
|
||||
};
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { AppFeatures } from './app_features';
|
||||
export * from './src/app_features_keys';
|
5
x-pack/packages/security-solution/features/kibana.jsonc
Normal file
5
x-pack/packages/security-solution/features/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/security-solution-features",
|
||||
"owner": "@elastic/security-threat-hunting-explore"
|
||||
}
|
6
x-pack/packages/security-solution/features/package.json
Normal file
6
x-pack/packages/security-solution/features/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/security-solution-features",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
7
x-pack/packages/security-solution/features/privileges.ts
Normal file
7
x-pack/packages/security-solution/features/privileges.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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 { AppFeaturesPrivilegeId, AppFeaturesPrivileges } from './src/app_features_privileges';
|
|
@ -6,60 +6,48 @@
|
|||
*/
|
||||
|
||||
export enum AppFeatureSecurityKey {
|
||||
/**
|
||||
* Enables Advanced Insights (Entity Risk, GenAI)
|
||||
*/
|
||||
/** Enables Advanced Insights (Entity Risk, GenAI) */
|
||||
advancedInsights = 'advanced_insights',
|
||||
|
||||
/**
|
||||
* Enables Investigation guide in Timeline
|
||||
*/
|
||||
investigationGuide = 'investigation_guide',
|
||||
|
||||
/**
|
||||
* Enables access to the Endpoint List and associated views that allows management of hosts
|
||||
* running endpoint security
|
||||
*/
|
||||
endpointHostManagement = 'endpoint_host_management',
|
||||
|
||||
/**
|
||||
* Enables endpoint policy views that enables user to manage endpoint security policies
|
||||
*/
|
||||
endpointPolicyManagement = 'endpoint_policy_management',
|
||||
|
||||
/**
|
||||
* Enables Endpoint Policy protections (like Malware, Ransomware, etc)
|
||||
*/
|
||||
endpointPolicyProtections = 'endpoint_policy_protections',
|
||||
|
||||
/**
|
||||
* Enables management of all endpoint related artifacts (ex. Trusted Applications, Event Filters,
|
||||
* Host Isolation Exceptions, Blocklist.
|
||||
*/
|
||||
endpointArtifactManagement = 'endpoint_artifact_management',
|
||||
|
||||
/**
|
||||
* Enables all of endpoint's supported response actions - like host isolation, file operations,
|
||||
* process operations, command execution, etc.
|
||||
*/
|
||||
endpointResponseActions = 'endpoint_response_actions',
|
||||
|
||||
/**
|
||||
* Enables Threat Intelligence
|
||||
*/
|
||||
threatIntelligence = 'threat-intelligence',
|
||||
|
||||
/**
|
||||
* Enables Osquery Response Actions
|
||||
*/
|
||||
osqueryAutomatedResponseActions = 'osquery_automated_response_actions',
|
||||
}
|
||||
|
||||
export enum AppFeatureAssistantKey {
|
||||
/**
|
||||
* Enables Elastic AI Assistant
|
||||
* Enables managing endpoint exceptions on rules and alerts
|
||||
*/
|
||||
assistant = 'assistant',
|
||||
endpointExceptions = 'endpointExceptions',
|
||||
}
|
||||
|
||||
export enum AppFeatureCasesKey {
|
||||
|
@ -69,14 +57,46 @@ export enum AppFeatureCasesKey {
|
|||
casesConnectors = 'cases_connectors',
|
||||
}
|
||||
|
||||
// Merges the two enums.
|
||||
export type AppFeatureKey = AppFeatureSecurityKey | AppFeatureCasesKey | AppFeatureAssistantKey;
|
||||
export type AppFeatureKeys = AppFeatureKey[];
|
||||
export enum AppFeatureAssistantKey {
|
||||
/**
|
||||
* Enables Elastic AI Assistant
|
||||
*/
|
||||
assistant = 'assistant',
|
||||
}
|
||||
|
||||
// We need to merge the value and the type and export both to replicate how enum works.
|
||||
// Merges the two enums.
|
||||
export const AppFeatureKey = {
|
||||
...AppFeatureSecurityKey,
|
||||
...AppFeatureCasesKey,
|
||||
...AppFeatureAssistantKey,
|
||||
};
|
||||
// We need to merge the value and the type and export both to replicate how enum works.
|
||||
export type AppFeatureKeyType = AppFeatureSecurityKey | AppFeatureCasesKey | AppFeatureAssistantKey;
|
||||
|
||||
export const ALL_APP_FEATURE_KEYS = Object.freeze(Object.values(AppFeatureKey));
|
||||
|
||||
/** Sub-features IDs for Security */
|
||||
export enum SecuritySubFeatureId {
|
||||
endpointList = 'endpointListSubFeature',
|
||||
endpointExceptions = 'endpointExceptionsSubFeature',
|
||||
trustedApplications = 'trustedApplicationsSubFeature',
|
||||
hostIsolationExceptions = 'hostIsolationExceptionsSubFeature',
|
||||
blocklist = 'blocklistSubFeature',
|
||||
eventFilters = 'eventFiltersSubFeature',
|
||||
policyManagement = 'policyManagementSubFeature',
|
||||
responseActionsHistory = 'responseActionsHistorySubFeature',
|
||||
hostIsolation = 'hostIsolationSubFeature',
|
||||
processOperations = 'processOperationsSubFeature',
|
||||
fileOperations = 'fileOperationsSubFeature',
|
||||
executeAction = 'executeActionSubFeature',
|
||||
}
|
||||
|
||||
/** Sub-features IDs for Cases */
|
||||
export enum CasesSubFeatureId {
|
||||
deleteCases = 'deleteCasesSubFeature',
|
||||
}
|
||||
|
||||
/** Sub-features IDs for Security Assistant */
|
||||
export enum AssistantSubFeatureId {
|
||||
createConversation = 'createConversationSubFeature',
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { APP_ID } from './constants';
|
||||
|
||||
export enum AppFeaturesPrivilegeId {
|
||||
endpointExceptions = 'endpoint_exceptions',
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the mapping of the privileges that are registered
|
||||
* using a different Kibana feature configuration (sub-feature, main feature privilege, etc)
|
||||
* in each offering type (ess, serverless)
|
||||
*/
|
||||
export const AppFeaturesPrivileges = {
|
||||
[AppFeaturesPrivilegeId.endpointExceptions]: {
|
||||
all: {
|
||||
ui: ['showEndpointExceptions', 'crudEndpointExceptions'],
|
||||
api: [`${APP_ID}-showEndpointExceptions`, `${APP_ID}-crudEndpointExceptions`],
|
||||
},
|
||||
read: {
|
||||
ui: ['showEndpointExceptions'],
|
||||
api: [`${APP_ID}-showEndpointExceptions`],
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AssistantSubFeatureId } from '../app_features_keys';
|
||||
import { AppFeatureAssistantKey } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Assistant Kibana Feature app.
|
||||
* These are the configs that are shared between both offering types (ess and serverless).
|
||||
* They can be extended on each offering plugin to register privileges using different way on each offering type.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const assistantDefaultAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
[AppFeatureAssistantKey.assistant]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['ai-assistant'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { AssistantSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import { getAssistantBaseKibanaFeature } from './kibana_features';
|
||||
import {
|
||||
getAssistantBaseKibanaSubFeatureIds,
|
||||
assistantSubFeaturesMap,
|
||||
} from './kibana_sub_features';
|
||||
|
||||
export const getAssistantFeature = (): AppFeatureParams<AssistantSubFeatureId> => ({
|
||||
baseKibanaFeature: getAssistantBaseKibanaFeature(),
|
||||
baseKibanaSubFeatureIds: getAssistantBaseKibanaSubFeatureIds(),
|
||||
subFeaturesMap: assistantSubFeaturesMap,
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import { type BaseKibanaFeatureConfig } from '../types';
|
||||
import { APP_ID, ASSISTANT_FEATURE_ID } from '../constants';
|
||||
|
||||
export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({
|
||||
id: ASSISTANT_FEATURE_ID,
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.linkSecuritySolutionAssistantTitle',
|
||||
{
|
||||
defaultMessage: 'Elastic AI Assistant',
|
||||
}
|
||||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
minimumLicense: 'enterprise',
|
||||
privileges: {
|
||||
all: {
|
||||
api: [],
|
||||
app: [ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: [],
|
||||
},
|
||||
read: {
|
||||
// No read-only mode currently supported
|
||||
disabled: true,
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: [],
|
||||
},
|
||||
},
|
||||
});
|
|
@ -12,13 +12,13 @@ import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
|||
// @ts-expect-error unused variable
|
||||
const createConversationSubFeature: SubFeatureConfig = {
|
||||
name: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.assistant.createConversationSubFeatureName',
|
||||
'securitySolutionPackages.features.featureRegistry.assistant.createConversationSubFeatureName',
|
||||
{
|
||||
defaultMessage: 'Create Conversations',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.assistant.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.assistant.description',
|
||||
{ defaultMessage: 'Create custom conversations.' }
|
||||
),
|
||||
privilegeGroups: [
|
||||
|
@ -29,7 +29,7 @@ const createConversationSubFeature: SubFeatureConfig = {
|
|||
api: [],
|
||||
id: 'create_conversation',
|
||||
name: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.assistant.createConversationSubFeatureDetails',
|
||||
'securitySolutionPackages.features.featureRegistry.assistant.createConversationSubFeatureDetails',
|
||||
{
|
||||
defaultMessage: 'Create conversations',
|
||||
}
|
||||
|
@ -50,7 +50,19 @@ export enum AssistantSubFeatureId {
|
|||
createConversation = 'createConversationSubFeature',
|
||||
}
|
||||
|
||||
// Defines all the ordered Security Assistant subFeatures available
|
||||
/**
|
||||
* Sub-features that will always be available for Security Assistant
|
||||
* regardless of the product type.
|
||||
*/
|
||||
export const getAssistantBaseKibanaSubFeatureIds = (): AssistantSubFeatureId[] => [
|
||||
// This is a sample sub-feature that can be used for future implementations
|
||||
// AssistantSubFeatureId.createConversation,
|
||||
];
|
||||
|
||||
/**
|
||||
* Defines all the Security Assistant subFeatures available.
|
||||
* The order of the subFeatures is the order they will be displayed
|
||||
*/
|
||||
export const assistantSubFeaturesMap = Object.freeze(
|
||||
new Map<AssistantSubFeatureId, SubFeatureConfig>([
|
||||
// This is a sample sub-feature that can be used for future implementations
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 { AppFeatureCasesKey } from '../app_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { DefaultCasesAppFeaturesConfig } from './types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Cases Kibana Feature app.
|
||||
* These are the configs that are shared between both offering types (ess and serverless).
|
||||
* They can be extended on each offering plugin to register privileges using different way on each offering type.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const getCasesDefaultAppFeaturesConfig = ({
|
||||
apiTags,
|
||||
uiCapabilities,
|
||||
}: {
|
||||
apiTags: { connectors: string };
|
||||
uiCapabilities: { connectors: string };
|
||||
}): DefaultCasesAppFeaturesConfig => ({
|
||||
[AppFeatureCasesKey.casesConnectors]: {
|
||||
privileges: {
|
||||
all: {
|
||||
api: [apiTags.connectors],
|
||||
ui: [uiCapabilities.connectors],
|
||||
cases: {
|
||||
push: [APP_ID],
|
||||
},
|
||||
},
|
||||
read: {
|
||||
api: [apiTags.connectors],
|
||||
ui: [uiCapabilities.connectors],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { CasesSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import { getCasesBaseKibanaFeature } from './kibana_features';
|
||||
import { getCasesBaseKibanaSubFeatureIds, getCasesSubFeaturesMap } from './kibana_sub_features';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
||||
export const getCasesFeature = (
|
||||
params: CasesFeatureParams
|
||||
): AppFeatureParams<CasesSubFeatureId> => ({
|
||||
baseKibanaFeature: getCasesBaseKibanaFeature(params),
|
||||
baseKibanaSubFeatureIds: getCasesBaseKibanaSubFeatureIds(),
|
||||
subFeaturesMap: getCasesSubFeaturesMap(params),
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import type { BaseKibanaFeatureConfig } from '../types';
|
||||
import { APP_ID, CASES_FEATURE_ID } from '../constants';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
||||
export const getCasesBaseKibanaFeature = ({
|
||||
uiCapabilities,
|
||||
apiTags,
|
||||
savedObjects,
|
||||
}: CasesFeatureParams): BaseKibanaFeatureConfig => {
|
||||
return {
|
||||
id: CASES_FEATURE_ID,
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.linkSecuritySolutionCaseTitle',
|
||||
{
|
||||
defaultMessage: 'Cases',
|
||||
}
|
||||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: [APP_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
api: apiTags.all,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
create: [APP_ID],
|
||||
read: [APP_ID],
|
||||
update: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [...savedObjects.files],
|
||||
read: [...savedObjects.files],
|
||||
},
|
||||
ui: uiCapabilities.all,
|
||||
},
|
||||
read: {
|
||||
api: apiTags.read,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
read: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [...savedObjects.files],
|
||||
},
|
||||
ui: uiCapabilities.read,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { CasesSubFeatureId } from '../app_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
||||
/**
|
||||
* Sub-features that will always be available for Security Cases
|
||||
* regardless of the product type.
|
||||
*/
|
||||
export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [
|
||||
CasesSubFeatureId.deleteCases,
|
||||
];
|
||||
|
||||
/**
|
||||
* Defines all the Security Assistant subFeatures available.
|
||||
* The order of the subFeatures is the order they will be displayed
|
||||
*/
|
||||
export const getCasesSubFeaturesMap = ({
|
||||
uiCapabilities,
|
||||
apiTags,
|
||||
savedObjects,
|
||||
}: CasesFeatureParams) => {
|
||||
const deleteCasesSubFeature: SubFeatureConfig = {
|
||||
name: i18n.translate('securitySolutionPackages.features.featureRegistry.deleteSubFeatureName', {
|
||||
defaultMessage: 'Delete',
|
||||
}),
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
api: apiTags.delete,
|
||||
id: 'cases_delete',
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.deleteSubFeatureDetails',
|
||||
{
|
||||
defaultMessage: 'Delete cases and comments',
|
||||
}
|
||||
),
|
||||
includeIn: 'all',
|
||||
savedObject: {
|
||||
all: [...savedObjects.files],
|
||||
read: [...savedObjects.files],
|
||||
},
|
||||
cases: {
|
||||
delete: [APP_ID],
|
||||
},
|
||||
ui: uiCapabilities.delete,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return new Map<CasesSubFeatureId, SubFeatureConfig>([
|
||||
[CasesSubFeatureId.deleteCases, deleteCasesSubFeature],
|
||||
]);
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CasesUiCapabilities, CasesApiTags } from '@kbn/cases-plugin/common';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
|
||||
export interface CasesFeatureParams {
|
||||
uiCapabilities: CasesUiCapabilities;
|
||||
apiTags: CasesApiTags;
|
||||
savedObjects: { files: string[] };
|
||||
}
|
||||
|
||||
export type DefaultCasesAppFeaturesConfig = Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
>;
|
22
x-pack/packages/security-solution/features/src/constants.ts
Normal file
22
x-pack/packages/security-solution/features/src/constants.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Same as the plugin id defined by Security Solution
|
||||
export const APP_ID = 'securitySolution' as const;
|
||||
export const SERVER_APP_ID = 'siem' as const;
|
||||
|
||||
export const CASES_FEATURE_ID = 'securitySolutionCases' as const;
|
||||
export const ASSISTANT_FEATURE_ID = 'securitySolutionAssistant' as const;
|
||||
|
||||
// Same as the plugin id defined by Cloud Security Posture
|
||||
export const CLOUD_POSTURE_APP_ID = 'csp' as const;
|
||||
|
||||
/**
|
||||
* Id for the notifications alerting type
|
||||
* @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function
|
||||
*/
|
||||
export const LEGACY_NOTIFICATIONS_ID = `siem.notifications` as const;
|
30
x-pack/packages/security-solution/features/src/helpers.ts
Normal file
30
x-pack/packages/security-solution/features/src/helpers.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys, AppFeatureKeyType, AppFeatureKibanaConfig } from './types';
|
||||
|
||||
/**
|
||||
* Creates the AppFeaturesConfig Map from the given appFeatures object and a set of enabled appFeatures keys.
|
||||
*/
|
||||
export const createEnabledAppFeaturesConfigMap = <
|
||||
K extends AppFeatureKeyType,
|
||||
T extends string = string
|
||||
>(
|
||||
appFeatures: Record<K, AppFeatureKibanaConfig<T>>,
|
||||
enabledAppFeaturesKeys: AppFeatureKeys
|
||||
) => {
|
||||
return new Map(
|
||||
Object.entries<AppFeatureKibanaConfig<T>>(appFeatures).reduce<
|
||||
Array<[K, AppFeatureKibanaConfig<T>]>
|
||||
>((acc, [key, value]) => {
|
||||
if (enabledAppFeaturesKeys.includes(key as K)) {
|
||||
acc.push([key as K, value]);
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
};
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 { AppFeatureSecurityKey, SecuritySubFeatureId } from '../app_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { DefaultSecurityAppFeaturesConfig } from './types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Solution Kibana Feature app.
|
||||
* These are the configs that are shared between both offering types (ess and serverless).
|
||||
* They can be extended on each offering plugin to register privileges using different way on each offering type.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig = {
|
||||
[AppFeatureSecurityKey.advancedInsights]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`${APP_ID}-entity-analytics`],
|
||||
},
|
||||
read: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`${APP_ID}-entity-analytics`],
|
||||
},
|
||||
},
|
||||
},
|
||||
[AppFeatureSecurityKey.investigationGuide]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['investigation-guide'],
|
||||
},
|
||||
read: {
|
||||
ui: ['investigation-guide'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.threatIntelligence]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['threat-intelligence'],
|
||||
api: [`${APP_ID}-threat-intelligence`],
|
||||
},
|
||||
read: {
|
||||
ui: ['threat-intelligence'],
|
||||
api: [`${APP_ID}-threat-intelligence`],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointHostManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.endpointList],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointPolicyManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.policyManagement],
|
||||
},
|
||||
|
||||
// Adds no additional kibana feature controls
|
||||
[AppFeatureSecurityKey.endpointPolicyProtections]: {},
|
||||
|
||||
[AppFeatureSecurityKey.endpointArtifactManagement]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.trustedApplications,
|
||||
SecuritySubFeatureId.blocklist,
|
||||
SecuritySubFeatureId.eventFilters,
|
||||
],
|
||||
subFeaturesPrivileges: [
|
||||
{
|
||||
id: 'host_isolation_exceptions_all',
|
||||
api: [`${APP_ID}-accessHostIsolationExceptions`, `${APP_ID}-writeHostIsolationExceptions`],
|
||||
ui: ['accessHostIsolationExceptions', 'writeHostIsolationExceptions'],
|
||||
},
|
||||
{
|
||||
id: 'host_isolation_exceptions_read',
|
||||
api: [`${APP_ID}-accessHostIsolationExceptions`],
|
||||
ui: ['accessHostIsolationExceptions'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointResponseActions]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.hostIsolationExceptions,
|
||||
SecuritySubFeatureId.responseActionsHistory,
|
||||
SecuritySubFeatureId.hostIsolation,
|
||||
SecuritySubFeatureId.processOperations,
|
||||
SecuritySubFeatureId.fileOperations,
|
||||
SecuritySubFeatureId.executeAction,
|
||||
],
|
||||
subFeaturesPrivileges: [
|
||||
{
|
||||
id: 'host_isolation_all',
|
||||
api: [`${APP_ID}-writeHostIsolation`],
|
||||
ui: ['writeHostIsolation'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.osqueryAutomatedResponseActions]: {},
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { SecuritySubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import { getSecurityBaseKibanaFeature } from './kibana_features';
|
||||
import { securitySubFeaturesMap, getSecurityBaseKibanaSubFeatureIds } from './kibana_sub_features';
|
||||
import type { SecurityFeatureParams } from './types';
|
||||
|
||||
export const getSecurityFeature = (
|
||||
params: SecurityFeatureParams
|
||||
): AppFeatureParams<SecuritySubFeatureId> => ({
|
||||
baseKibanaFeature: getSecurityBaseKibanaFeature(params),
|
||||
baseKibanaSubFeatureIds: getSecurityBaseKibanaSubFeatureIds(params),
|
||||
subFeaturesMap: securitySubFeaturesMap,
|
||||
});
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
|
||||
import {
|
||||
EQL_RULE_TYPE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
NEW_TERMS_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
SAVED_QUERY_RULE_TYPE_ID,
|
||||
THRESHOLD_RULE_TYPE_ID,
|
||||
} from '@kbn/securitysolution-rules';
|
||||
import type { BaseKibanaFeatureConfig } from '../types';
|
||||
import { APP_ID, SERVER_APP_ID, LEGACY_NOTIFICATIONS_ID, CLOUD_POSTURE_APP_ID } from '../constants';
|
||||
import type { SecurityFeatureParams } from './types';
|
||||
|
||||
const SECURITY_RULE_TYPES = [
|
||||
LEGACY_NOTIFICATIONS_ID,
|
||||
EQL_RULE_TYPE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
SAVED_QUERY_RULE_TYPE_ID,
|
||||
THRESHOLD_RULE_TYPE_ID,
|
||||
NEW_TERMS_RULE_TYPE_ID,
|
||||
];
|
||||
|
||||
export const getSecurityBaseKibanaFeature = ({
|
||||
savedObjects,
|
||||
}: SecurityFeatureParams): BaseKibanaFeatureConfig => ({
|
||||
id: SERVER_APP_ID,
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.linkSecuritySolutionTitle',
|
||||
{
|
||||
defaultMessage: 'Security',
|
||||
}
|
||||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
alerting: SECURITY_RULE_TYPES,
|
||||
privileges: {
|
||||
all: {
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
api: [
|
||||
APP_ID,
|
||||
'lists-all',
|
||||
'lists-read',
|
||||
'lists-summary',
|
||||
'rac',
|
||||
'cloud-security-posture-all',
|
||||
'cloud-security-posture-read',
|
||||
],
|
||||
savedObject: {
|
||||
all: ['alert', ...savedObjects],
|
||||
read: [],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
alert: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
ui: ['show', 'crud'],
|
||||
},
|
||||
read: {
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
api: [APP_ID, 'lists-read', 'rac', 'cloud-security-posture-read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [...savedObjects],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
read: SECURITY_RULE_TYPES,
|
||||
},
|
||||
alert: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
ui: ['show'],
|
||||
},
|
||||
},
|
||||
});
|
|
@ -8,21 +8,27 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC } from '@kbn/securitysolution-list-constants';
|
||||
import { APP_ID } from '../../../common';
|
||||
import { AppFeaturesPrivilegeId, AppFeaturesPrivileges } from '../app_features_privileges';
|
||||
import { SecuritySubFeatureId } from '../app_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { SecurityFeatureParams } from './types';
|
||||
|
||||
const endpointListSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.endpointList.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointList.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Endpoint List access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.endpointList', {
|
||||
defaultMessage: 'Endpoint List',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointList',
|
||||
{
|
||||
defaultMessage: 'Endpoint List',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.endpointList.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointList.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Displays all hosts running Elastic Defend and their relevant integration details.',
|
||||
|
@ -61,16 +67,19 @@ const endpointListSubFeature: SubFeatureConfig = {
|
|||
const trustedApplicationsSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.trustedApplications.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Trusted Applications access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.trustedApplications', {
|
||||
defaultMessage: 'Trusted Applications',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications',
|
||||
{
|
||||
defaultMessage: 'Trusted Applications',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.trustedApplications.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Helps mitigate conflicts with other software, usually other antivirus or endpoint security applications.',
|
||||
|
@ -115,19 +124,19 @@ const trustedApplicationsSubFeature: SubFeatureConfig = {
|
|||
const hostIsolationExceptionsSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolationExceptions.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Host Isolation Exceptions access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolationExceptions',
|
||||
{
|
||||
defaultMessage: 'Host Isolation Exceptions',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolationExceptions.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Add specific IP addresses that isolated hosts are still allowed to communicate with, even when isolated from the rest of the network.',
|
||||
|
@ -172,16 +181,16 @@ const hostIsolationExceptionsSubFeature: SubFeatureConfig = {
|
|||
const blocklistSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.blockList.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.blockList.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Blocklist access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.blockList', {
|
||||
name: i18n.translate('securitySolutionPackages.features.featureRegistry.subFeatures.blockList', {
|
||||
defaultMessage: 'Blocklist',
|
||||
}),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.blockList.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.blockList.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Extend Elastic Defend’s protection against malicious processes and protect against potentially harmful applications.',
|
||||
|
@ -226,16 +235,19 @@ const blocklistSubFeature: SubFeatureConfig = {
|
|||
const eventFiltersSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.eventFilters.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.eventFilters.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Event Filters access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.eventFilters', {
|
||||
defaultMessage: 'Event Filters',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.eventFilters',
|
||||
{
|
||||
defaultMessage: 'Event Filters',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.eventFilters.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.eventFilters.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Filter out endpoint events that you do not need or want stored in Elasticsearch.',
|
||||
|
@ -280,16 +292,19 @@ const eventFiltersSubFeature: SubFeatureConfig = {
|
|||
const policyManagementSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.policyManagement.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.policyManagement.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Policy Management access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.policyManagement', {
|
||||
defaultMessage: 'Elastic Defend Policy Management',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.policyManagement',
|
||||
{
|
||||
defaultMessage: 'Elastic Defend Policy Management',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.policyManagement.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.policyManagement.description',
|
||||
{
|
||||
defaultMessage:
|
||||
'Access the Elastic Defend integration policy to configure protections, event collection, and advanced policy features.',
|
||||
|
@ -329,19 +344,19 @@ const policyManagementSubFeature: SubFeatureConfig = {
|
|||
const responseActionsHistorySubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.responseActionsHistory.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Response Actions History access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.responseActionsHistory',
|
||||
{
|
||||
defaultMessage: 'Response Actions History',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.responseActionsHistory.description',
|
||||
{
|
||||
defaultMessage: 'Access the history of response actions performed on endpoints.',
|
||||
}
|
||||
|
@ -379,16 +394,19 @@ const responseActionsHistorySubFeature: SubFeatureConfig = {
|
|||
const hostIsolationSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.hostIsolation.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolation.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Host Isolation access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.hostIsolation', {
|
||||
defaultMessage: 'Host Isolation',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolation',
|
||||
{
|
||||
defaultMessage: 'Host Isolation',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.hostIsolation.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.hostIsolation.description',
|
||||
{ defaultMessage: 'Perform the "isolate" and "release" response actions.' }
|
||||
),
|
||||
privilegeGroups: [
|
||||
|
@ -396,6 +414,7 @@ const hostIsolationSubFeature: SubFeatureConfig = {
|
|||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
api: [`${APP_ID}-writeHostIsolationRelease`],
|
||||
id: 'host_isolation_all',
|
||||
includeIn: 'none',
|
||||
name: 'All',
|
||||
|
@ -403,11 +422,6 @@ const hostIsolationSubFeature: SubFeatureConfig = {
|
|||
all: [],
|
||||
read: [],
|
||||
},
|
||||
// FYI: The current set of values below (`api`, `ui`) cover only `release` response action.
|
||||
// There is a second set of values for API and UI that are added later if `endpointResponseActions`
|
||||
// appFeature is enabled. Needed to ensure that in a downgrade of license condition,
|
||||
// users are still able to un-isolate a host machine.
|
||||
api: [`${APP_ID}-writeHostIsolationRelease`],
|
||||
ui: ['writeHostIsolationRelease'],
|
||||
},
|
||||
],
|
||||
|
@ -418,16 +432,19 @@ const hostIsolationSubFeature: SubFeatureConfig = {
|
|||
const processOperationsSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.processOperations.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.processOperations.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Process Operations access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.processOperations', {
|
||||
defaultMessage: 'Process Operations',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.processOperations',
|
||||
{
|
||||
defaultMessage: 'Process Operations',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.processOperations.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.processOperations.description',
|
||||
{
|
||||
defaultMessage: 'Perform process-related response actions in the response console.',
|
||||
}
|
||||
|
@ -454,16 +471,19 @@ const processOperationsSubFeature: SubFeatureConfig = {
|
|||
const fileOperationsSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.fileOperations.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.fileOperations.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for File Operations access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.fileOperations', {
|
||||
defaultMessage: 'File Operations',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.fileOperations',
|
||||
{
|
||||
defaultMessage: 'File Operations',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.fileOperations.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.fileOperations.description',
|
||||
{
|
||||
defaultMessage: 'Perform file-related response actions in the response console.',
|
||||
}
|
||||
|
@ -493,16 +513,19 @@ const fileOperationsSubFeature: SubFeatureConfig = {
|
|||
const executeActionSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.executeOperations.privilegesTooltip',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.executeOperations.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Execute Operations access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.subFeatures.executeOperations', {
|
||||
defaultMessage: 'Execute Operations',
|
||||
}),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.executeOperations',
|
||||
{
|
||||
defaultMessage: 'Execute Operations',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.subFeatures.executeOperations.description',
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.executeOperations.description',
|
||||
{
|
||||
// TODO: Update this description before 8.8 FF
|
||||
defaultMessage: 'Perform script execution on the endpoint.',
|
||||
|
@ -528,24 +551,71 @@ const executeActionSubFeature: SubFeatureConfig = {
|
|||
],
|
||||
};
|
||||
|
||||
export enum SecuritySubFeatureId {
|
||||
endpointList = 'endpointListSubFeature',
|
||||
trustedApplications = 'trustedApplicationsSubFeature',
|
||||
hostIsolationExceptions = 'hostIsolationExceptionsSubFeature',
|
||||
blocklist = 'blocklistSubFeature',
|
||||
eventFilters = 'eventFiltersSubFeature',
|
||||
policyManagement = 'policyManagementSubFeature',
|
||||
responseActionsHistory = 'responseActionsHistorySubFeature',
|
||||
hostIsolation = 'hostIsolationSubFeature',
|
||||
processOperations = 'processOperationsSubFeature',
|
||||
fileOperations = 'fileOperationsSubFeature',
|
||||
executeAction = 'executeActionSubFeature',
|
||||
}
|
||||
const endpointExceptionsSubFeature: SubFeatureConfig = {
|
||||
requireAllSpaces: true,
|
||||
privilegesTooltip: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointExceptions.privilegesTooltip',
|
||||
{
|
||||
defaultMessage: 'All Spaces is required for Endpoint Exceptions access.',
|
||||
}
|
||||
),
|
||||
name: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointExceptions',
|
||||
{
|
||||
defaultMessage: 'Endpoint Exceptions',
|
||||
}
|
||||
),
|
||||
description: i18n.translate(
|
||||
'securitySolutionPackages.features.featureRegistry.subFeatures.endpointExceptions.description',
|
||||
{
|
||||
defaultMessage: 'Use Endpoint Exceptions (this is a test sub-feature).',
|
||||
}
|
||||
),
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: 'endpoint_exceptions_all',
|
||||
includeIn: 'all',
|
||||
name: 'All',
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
...AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions].all,
|
||||
},
|
||||
{
|
||||
id: 'endpoint_exceptions_read',
|
||||
includeIn: 'read',
|
||||
name: 'Read',
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
...AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions].read,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Defines all the ordered Security subFeatures available
|
||||
/**
|
||||
* Sub-features that will always be available for Security
|
||||
* regardless of the product type.
|
||||
*/
|
||||
export const getSecurityBaseKibanaSubFeatureIds = (
|
||||
{ experimentalFeatures }: SecurityFeatureParams // currently un-used, but left here as a convenience for possible future use
|
||||
): SecuritySubFeatureId[] => [SecuritySubFeatureId.hostIsolation];
|
||||
|
||||
/**
|
||||
* Defines all the Security Assistant subFeatures available.
|
||||
* The order of the subFeatures is the order they will be displayed
|
||||
*/
|
||||
export const securitySubFeaturesMap = Object.freeze(
|
||||
new Map<SecuritySubFeatureId, SubFeatureConfig>([
|
||||
[SecuritySubFeatureId.endpointList, endpointListSubFeature],
|
||||
[SecuritySubFeatureId.endpointExceptions, endpointExceptionsSubFeature],
|
||||
[SecuritySubFeatureId.trustedApplications, trustedApplicationsSubFeature],
|
||||
[SecuritySubFeatureId.hostIsolationExceptions, hostIsolationExceptionsSubFeature],
|
||||
[SecuritySubFeatureId.blocklist, blocklistSubFeature],
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureSecurityKey, SecuritySubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
|
||||
export interface SecurityFeatureParams {
|
||||
experimentalFeatures: Record<string, boolean>;
|
||||
savedObjects: string[];
|
||||
}
|
||||
|
||||
export type DefaultSecurityAppFeaturesConfig = Omit<
|
||||
Record<AppFeatureSecurityKey, AppFeatureKibanaConfig<SecuritySubFeatureId>>,
|
||||
AppFeatureSecurityKey.endpointExceptions
|
||||
// | add not default security app features here
|
||||
>;
|
60
x-pack/packages/security-solution/features/src/types.ts
Normal file
60
x-pack/packages/security-solution/features/src/types.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
KibanaFeatureConfig,
|
||||
SubFeatureConfig,
|
||||
SubFeaturePrivilegeConfig,
|
||||
} from '@kbn/features-plugin/common';
|
||||
import type { RecursivePartial } from '@kbn/utility-types';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKeyType,
|
||||
AppFeatureSecurityKey,
|
||||
AssistantSubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
SecuritySubFeatureId,
|
||||
} from './app_features_keys';
|
||||
|
||||
export type { AppFeatureKeyType };
|
||||
export type AppFeatureKeys = AppFeatureKeyType[];
|
||||
|
||||
// Features types
|
||||
export type BaseKibanaFeatureConfig = Omit<KibanaFeatureConfig, 'subFeatures'>;
|
||||
export type SubFeaturesPrivileges = RecursivePartial<SubFeaturePrivilegeConfig>;
|
||||
export type AppFeatureKibanaConfig<T extends string = string> =
|
||||
RecursivePartial<BaseKibanaFeatureConfig> & {
|
||||
subFeatureIds?: T[];
|
||||
subFeaturesPrivileges?: SubFeaturesPrivileges[];
|
||||
};
|
||||
export type AppFeaturesConfig<T extends string = string> = Map<
|
||||
AppFeatureKeyType,
|
||||
AppFeatureKibanaConfig<T>
|
||||
>;
|
||||
|
||||
export type AppFeaturesSecurityConfig = Map<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
>;
|
||||
export type AppFeaturesCasesConfig = Map<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
>;
|
||||
|
||||
export type AppFeaturesAssistantConfig = Map<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
>;
|
||||
|
||||
export type AppSubFeaturesMap<T extends string = string> = Map<T, SubFeatureConfig>;
|
||||
|
||||
export interface AppFeatureParams<T extends string = string> {
|
||||
baseKibanaFeature: BaseKibanaFeatureConfig;
|
||||
baseKibanaSubFeatureIds: T[];
|
||||
subFeaturesMap: AppSubFeaturesMap<T>;
|
||||
}
|
22
x-pack/packages/security-solution/features/tsconfig.json
Normal file
22
x-pack/packages/security-solution/features/tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
]
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"kbn_references": [
|
||||
"@kbn/features-plugin",
|
||||
"@kbn/utility-types",
|
||||
"@kbn/i18n",
|
||||
"@kbn/core-application-common",
|
||||
"@kbn/cases-plugin",
|
||||
"@kbn/securitysolution-rules",
|
||||
"@kbn/securitysolution-list-constants",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
|
@ -51,7 +51,6 @@ export async function initDiagnosticsBundle({
|
|||
const kibanaClient = axios.create({
|
||||
baseURL: kbHost ?? kibanaHost,
|
||||
auth,
|
||||
// @ts-expect-error
|
||||
headers: { 'kbn-xsrf': 'true', ...apiKeyHeader },
|
||||
});
|
||||
const apmIndices = await getApmIndices(kibanaClient);
|
||||
|
|
|
@ -50,13 +50,15 @@ export {
|
|||
INTERNAL_BULK_CREATE_ATTACHMENTS_URL,
|
||||
SAVED_OBJECT_TYPES,
|
||||
CASE_COMMENT_SAVED_OBJECT,
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from './constants';
|
||||
|
||||
export type { AttachmentAttributes } from './types/domain';
|
||||
export { ConnectorTypes, AttachmentType, ExternalReferenceStorageType } from './types/domain';
|
||||
export { getCasesFromAlertsUrl, getCaseFindUserActionsUrl, throwErrors } from './api';
|
||||
export { StatusAll } from './ui/types';
|
||||
export { createUICapabilities } from './utils/capabilities';
|
||||
export { getApiTags } from './utils/api_tags';
|
||||
export { createUICapabilities, type CasesUiCapabilities } from './utils/capabilities';
|
||||
export { getApiTags, type CasesApiTags } from './utils/api_tags';
|
||||
export { CaseMetricsFeature } from './types/api';
|
||||
export type { SingleCaseMetricsResponse, CasesMetricsResponse } from './types/api';
|
||||
|
|
|
@ -14,7 +14,13 @@ import { HttpApiTagOperation } from '../constants/types';
|
|||
import type { Owner } from '../constants/types';
|
||||
import { constructFilesHttpOperationTag } from '../files';
|
||||
|
||||
export const getApiTags = (owner: Owner) => {
|
||||
export interface CasesApiTags {
|
||||
all: readonly string[];
|
||||
read: readonly string[];
|
||||
delete: readonly string[];
|
||||
}
|
||||
|
||||
export const getApiTags = (owner: Owner): CasesApiTags => {
|
||||
const create = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Create);
|
||||
const deleteTag = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Delete);
|
||||
const read = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Read);
|
||||
|
|
|
@ -14,11 +14,16 @@ import {
|
|||
UPDATE_CASES_CAPABILITY,
|
||||
} from '../constants';
|
||||
|
||||
export interface CasesUiCapabilities {
|
||||
all: readonly string[];
|
||||
read: readonly string[];
|
||||
delete: readonly string[];
|
||||
}
|
||||
/**
|
||||
* Return the UI capabilities for each type of operation. These strings must match the values defined in the UI
|
||||
* here: x-pack/plugins/cases/public/client/helpers/capabilities.ts
|
||||
*/
|
||||
export const createUICapabilities = () => ({
|
||||
export const createUICapabilities = (): CasesUiCapabilities => ({
|
||||
all: [
|
||||
CREATE_CASES_CAPABILITY,
|
||||
READ_CASES_CAPABILITY,
|
||||
|
|
|
@ -20,8 +20,6 @@ export {
|
|||
} from './constants';
|
||||
export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants';
|
||||
export { allowedExperimentalValues, type ExperimentalFeatures } from './experimental_features';
|
||||
export type { AppFeatureKeys } from './types/app_features';
|
||||
export { AppFeatureKey, ALL_APP_FEATURE_KEYS } from './types/app_features';
|
||||
|
||||
// Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase.
|
||||
// If you're using functions/types/etc... internally it's best to import directly from their paths than expose the functions/types/etc... here.
|
||||
|
|
|
@ -44,6 +44,7 @@ jest.mock('../../lib/kibana', () => {
|
|||
const originalKibanaLib = jest.requireActual('../../lib/kibana');
|
||||
|
||||
return {
|
||||
...originalKibanaLib,
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
application: {
|
||||
|
@ -71,7 +72,6 @@ jest.mock('../../lib/kibana', () => {
|
|||
useNavigateTo: jest.fn().mockReturnValue({
|
||||
navigateTo: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: originalKibanaLib.useGetUserCasesPermissions,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@ import React, { useMemo } from 'react';
|
|||
import { EuiCode, EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useIsMounted } from '@kbn/securitysolution-hook-utils';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { useUpsellingComponent } from '../../../common/hooks/use_upselling';
|
||||
import { AppFeatureKey } from '../../../../common';
|
||||
import { ResponseActionFormField } from './osquery_response_action_form_field';
|
||||
import type { ArrayItem } from '../../../shared_imports';
|
||||
import { UseField } from '../../../shared_imports';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { NOT_AVAILABLE, PERMISSION_DENIED, SHORT_EMPTY_TITLE } from './translations';
|
||||
import { UseField } from '../../../shared_imports';
|
||||
|
||||
interface OsqueryResponseActionProps {
|
||||
item: ArrayItem;
|
||||
|
|
|
@ -57,31 +57,36 @@ const props = {
|
|||
timelineId: 'alerts-page',
|
||||
};
|
||||
|
||||
jest.mock('../../../../common/lib/kibana', () => ({
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
}),
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: { ...mockTimelines },
|
||||
application: {
|
||||
capabilities: { siem: { crud_alerts: true, read_alerts: true } },
|
||||
jest.mock('../../../../common/lib/kibana', () => {
|
||||
const original = jest.requireActual('../../../../common/lib/kibana');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
}),
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: { ...mockTimelines },
|
||||
application: {
|
||||
capabilities: { siem: { crud_alerts: true, read_alerts: true } },
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
},
|
||||
}),
|
||||
useGetUserCasesPermissions: jest.fn().mockReturnValue({
|
||||
all: true,
|
||||
create: true,
|
||||
read: true,
|
||||
update: true,
|
||||
delete: true,
|
||||
push: true,
|
||||
}),
|
||||
}));
|
||||
}),
|
||||
useGetUserCasesPermissions: jest.fn().mockReturnValue({
|
||||
all: true,
|
||||
create: true,
|
||||
read: true,
|
||||
update: true,
|
||||
delete: true,
|
||||
push: true,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../containers/detection_engine/alerts/use_alerts_privileges', () => ({
|
||||
useAlertsPrivileges: jest.fn().mockReturnValue({ hasIndexWrite: true, hasKibanaCRUD: true }),
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { EuiButtonIcon, EuiPopover, EuiToolTip, EuiContextMenu } from '@elastic/eui';
|
||||
import { EuiButtonIcon, EuiContextMenu, EuiPopover, EuiToolTip } from '@elastic/eui';
|
||||
import { indexOf } from 'lodash';
|
||||
import type { ConnectedProps } from 'react-redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -36,7 +36,7 @@ import { useSignalIndex } from '../../../containers/detection_engine/alerts/use_
|
|||
import { EventFiltersFlyout } from '../../../../management/pages/event_filters/view/components/event_filters_flyout';
|
||||
import { useAlertsActions } from './use_alerts_actions';
|
||||
import { useExceptionFlyout } from './use_add_exception_flyout';
|
||||
import { useExceptionActions } from './use_add_exception_actions';
|
||||
import { useAlertExceptionActions } from './use_add_exception_actions';
|
||||
import { useEventFilterModal } from './use_event_filter_modal';
|
||||
import type { Status } from '../../../../../common/api/detection_engine';
|
||||
import { ATTACH_ALERT_TO_CASE_FOR_ROW } from '../../../../timelines/components/timeline/body/translations';
|
||||
|
@ -196,7 +196,7 @@ const AlertContextMenuComponent: React.FC<AlertContextMenuProps & PropsFromRedux
|
|||
closePopover();
|
||||
}, [closePopover, onAddEventFilterClick]);
|
||||
|
||||
const { exceptionActionItems } = useExceptionActions({
|
||||
const { exceptionActionItems } = useAlertExceptionActions({
|
||||
isEndpointAlert: isAlertFromEndpointAlert({ ecsData: ecsRowData }),
|
||||
onAddExceptionTypeClick: handleOnAddExceptionTypeClick,
|
||||
});
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { useListsConfig } from '../../../containers/detection_engine/lists/use_lists_config';
|
||||
import { useHasSecurityCapability } from '../../../../helper_hooks';
|
||||
import { useUserData } from '../../user_info';
|
||||
import { ACTION_ADD_ENDPOINT_EXCEPTION, ACTION_ADD_EXCEPTION } from '../translations';
|
||||
import type { AlertTableContextMenuItem } from '../types';
|
||||
|
@ -64,3 +66,33 @@ export const useExceptionActions = ({
|
|||
|
||||
return { exceptionActionItems };
|
||||
};
|
||||
|
||||
export const useAlertExceptionActions = ({
|
||||
isEndpointAlert,
|
||||
onAddExceptionTypeClick,
|
||||
}: UseExceptionActionProps) => {
|
||||
const { exceptionActionItems } = useExceptionActions({
|
||||
isEndpointAlert,
|
||||
onAddExceptionTypeClick,
|
||||
});
|
||||
|
||||
const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration } =
|
||||
useListsConfig();
|
||||
const canReadEndpointExceptions = useHasSecurityCapability('crudEndpointExceptions');
|
||||
|
||||
const canWriteEndpointExceptions = useMemo(
|
||||
() => !listsConfigLoading && !needsListsConfiguration && canReadEndpointExceptions,
|
||||
[canReadEndpointExceptions, listsConfigLoading, needsListsConfiguration]
|
||||
);
|
||||
// Endpoint exceptions are available for:
|
||||
// Serverless Endpoint Essentials/Complete PLI and
|
||||
// on ESS Security Kibana sub-feature Endpoint Exceptions (enabled when Security feature is enabled)
|
||||
if (!canWriteEndpointExceptions) {
|
||||
return {
|
||||
exceptionActionItems: exceptionActionItems.map((item) => {
|
||||
return { ...item, disabled: item.name === ACTION_ADD_ENDPOINT_EXCEPTION };
|
||||
}),
|
||||
};
|
||||
}
|
||||
return { exceptionActionItems };
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
|
|||
import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
|
||||
import { TableId } from '@kbn/securitysolution-data-table';
|
||||
import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
|
||||
import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step';
|
||||
import {
|
||||
AlertsCasesTourSteps,
|
||||
|
@ -17,9 +18,8 @@ import {
|
|||
} from '../../../common/components/guided_onboarding_tour/tour_config';
|
||||
import { isActiveTimeline } from '../../../helpers';
|
||||
import { useResponderActionItem } from '../endpoint_responder';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
|
||||
import { TAKE_ACTION } from '../alerts_table/additional_filters_action/translations';
|
||||
import { useExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions';
|
||||
import { useAlertExceptionActions } from '../alerts_table/timeline_actions/use_add_exception_actions';
|
||||
import { useAlertsActions } from '../alerts_table/timeline_actions/use_alerts_actions';
|
||||
import { useInvestigateInTimeline } from '../alerts_table/timeline_actions/use_investigate_in_timeline';
|
||||
|
||||
|
@ -157,7 +157,7 @@ export const TakeActionDropdown = React.memo(
|
|||
[onAddExceptionTypeClick]
|
||||
);
|
||||
|
||||
const { exceptionActionItems } = useExceptionActions({
|
||||
const { exceptionActionItems } = useAlertExceptionActions({
|
||||
isEndpointAlert: isAlertFromEndpointAlert({ ecsData }),
|
||||
onAddExceptionTypeClick: handleOnAddExceptionTypeClick,
|
||||
});
|
||||
|
|
|
@ -5,41 +5,41 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useEffect, useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { EuiSearchBarProps } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiButtonIcon,
|
||||
EuiContextMenuItem,
|
||||
EuiContextMenuPanel,
|
||||
EuiPagination,
|
||||
EuiPopover,
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiPageHeader,
|
||||
EuiHorizontalRule,
|
||||
EuiPageHeader,
|
||||
EuiPagination,
|
||||
EuiPopover,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import type { NamespaceType, ExceptionListFilter } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { ExceptionListFilter, NamespaceType } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { useApi, useExceptionLists } from '@kbn/securitysolution-list-hooks';
|
||||
import { ViewerStatus, EmptyViewerState } from '@kbn/securitysolution-exception-list-components';
|
||||
import { EmptyViewerState, ViewerStatus } from '@kbn/securitysolution-exception-list-components';
|
||||
|
||||
import { useHasSecurityCapability } from '../../../helper_hooks';
|
||||
import { AutoDownload } from '../../../common/components/auto_download/auto_download';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
||||
|
||||
import * as i18n from '../../translations/shared_list';
|
||||
import {
|
||||
ExceptionsTableUtilityBar,
|
||||
ListsSearchBar,
|
||||
ExceptionsListCard,
|
||||
ImportExceptionListFlyout,
|
||||
CreateSharedListFlyout,
|
||||
ExceptionsListCard,
|
||||
ExceptionsTableUtilityBar,
|
||||
ImportExceptionListFlyout,
|
||||
ListsSearchBar,
|
||||
} from '../../components';
|
||||
import { useAllExceptionLists } from '../../hooks/use_all_exception_lists';
|
||||
import { ReferenceErrorModal } from '../../../detections/components/value_lists_management_flyout/reference_error_modal';
|
||||
|
@ -82,9 +82,15 @@ const SORT_FIELDS: Array<{ field: string; label: string; defaultOrder: 'asc' | '
|
|||
export const SharedLists = React.memo(() => {
|
||||
const [{ loading: userInfoLoading, canUserCRUD, canUserREAD }] = useUserData();
|
||||
|
||||
const { loading: listsConfigLoading } = useListsConfig();
|
||||
const { loading: listsConfigLoading, needsConfiguration: needsListsConfiguration } =
|
||||
useListsConfig();
|
||||
const loading = userInfoLoading || listsConfigLoading;
|
||||
const canShowEndpointExceptions = useHasSecurityCapability('showEndpointExceptions');
|
||||
|
||||
const canAccessEndpointExceptions = useMemo(
|
||||
() => !listsConfigLoading && !needsListsConfiguration && canShowEndpointExceptions,
|
||||
[canShowEndpointExceptions, listsConfigLoading, needsListsConfiguration]
|
||||
);
|
||||
const {
|
||||
services: {
|
||||
http,
|
||||
|
@ -103,6 +109,13 @@ export const SharedLists = React.memo(() => {
|
|||
|
||||
const [viewerStatus, setViewStatus] = useState<ViewerStatus | null>(ViewerStatus.LOADING);
|
||||
|
||||
const exceptionListTypes = useMemo(() => {
|
||||
const lists = [ExceptionListTypeEnum.DETECTION];
|
||||
if (canAccessEndpointExceptions) {
|
||||
lists.push(ExceptionListTypeEnum.ENDPOINT);
|
||||
}
|
||||
return lists;
|
||||
}, [canAccessEndpointExceptions]);
|
||||
const [
|
||||
loadingExceptions,
|
||||
exceptions,
|
||||
|
@ -115,7 +128,7 @@ export const SharedLists = React.memo(() => {
|
|||
errorMessage: i18n.ERROR_EXCEPTION_LISTS,
|
||||
filterOptions: {
|
||||
...filters,
|
||||
types: [ExceptionListTypeEnum.DETECTION, ExceptionListTypeEnum.ENDPOINT],
|
||||
types: exceptionListTypes,
|
||||
},
|
||||
http,
|
||||
namespaceTypes: ['single', 'agnostic'],
|
||||
|
|
|
@ -15,8 +15,8 @@ import { mockAlertDetailsData } from '../../../../../common/components/event_det
|
|||
import type { TimelineEventsDetailsItem } from '../../../../../../common/search_strategy';
|
||||
import {
|
||||
KibanaServices,
|
||||
useKibana,
|
||||
useGetUserCasesPermissions,
|
||||
useKibana,
|
||||
} from '../../../../../common/lib/kibana';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
|
||||
|
@ -132,6 +132,15 @@ describe('event details footer component', () => {
|
|||
query: jest.fn(),
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
application: {
|
||||
...coreStartMock.application,
|
||||
capabilities: {
|
||||
...coreStartMock.application.capabilities,
|
||||
siem: {
|
||||
crudEndpointExceptions: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import { TestProviders } from '../../../../../common/mock';
|
|||
|
||||
import { EventColumnView } from './event_column_view';
|
||||
import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
|
||||
import { TimelineTabs, TimelineId } from '../../../../../../common/types/timeline';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
|
||||
import { TimelineType } from '../../../../../../common/api/timeline';
|
||||
import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
|
@ -47,6 +47,7 @@ jest.mock('../../../../../common/lib/kibana', () => {
|
|||
const originalModule = jest.requireActual('../../../../../common/lib/kibana');
|
||||
|
||||
return {
|
||||
...originalModule,
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: { ...mockTimelines },
|
||||
|
@ -71,7 +72,6 @@ jest.mock('../../../../../common/lib/kibana', () => {
|
|||
addWarning: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: originalModule.useGetUserCasesPermissions,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import type {
|
|||
import type { PluginStartContract as AlertsPluginStartContract } from '@kbn/alerting-plugin/server';
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { FleetActionsClientInterface } from '@kbn/fleet-plugin/server/services/actions/types';
|
||||
import type { AppFeatures } from '../lib/app_features';
|
||||
import {
|
||||
getPackagePolicyCreateCallback,
|
||||
getPackagePolicyUpdateCallback,
|
||||
|
@ -43,6 +42,7 @@ import { calculateEndpointAuthz } from '../../common/endpoint/service/authz';
|
|||
import type { FeatureUsageService } from './services/feature_usage/service';
|
||||
import type { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
import type { ActionCreateService } from './services/actions/create/types';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
|
||||
export interface EndpointAppContextServiceSetupContract {
|
||||
securitySolutionRequestContextFactory: IRequestContextFactory;
|
||||
|
@ -70,7 +70,7 @@ export interface EndpointAppContextServiceStartContract {
|
|||
actionCreateService: ActionCreateService | undefined;
|
||||
cloud: CloudSetup;
|
||||
esClient: ElasticsearchClient;
|
||||
appFeatures: AppFeatures;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +108,7 @@ export class EndpointAppContextService {
|
|||
featureUsageService,
|
||||
endpointMetadataService,
|
||||
esClient,
|
||||
appFeatures,
|
||||
appFeaturesService,
|
||||
} = dependencies;
|
||||
|
||||
registerIngestCallback(
|
||||
|
@ -121,7 +121,7 @@ export class EndpointAppContextService {
|
|||
licenseService,
|
||||
exceptionListsClient,
|
||||
cloud,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -139,7 +139,7 @@ export class EndpointAppContextService {
|
|||
endpointMetadataService,
|
||||
cloud,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -9,29 +9,32 @@ import { createMockEndpointAppContextServiceStartContract } from '../mocks';
|
|||
import type { Logger } from '@kbn/logging';
|
||||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
import type { AppFeatures } from '../../lib/app_features';
|
||||
import { createAppFeaturesMock } from '../../lib/app_features/mocks';
|
||||
import { ALL_APP_FEATURE_KEYS } from '../../../common';
|
||||
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { turnOffPolicyProtectionsIfNotSupported } from './turn_off_policy_protections';
|
||||
import { FleetPackagePolicyGenerator } from '../../../common/endpoint/data_generators/fleet_package_policy_generator';
|
||||
import type { PolicyData } from '../../../common/endpoint/types';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { PromiseResolvedValue } from '../../../common/endpoint/types/utility_types';
|
||||
import { ensureOnlyEventCollectionIsAllowed } from '../../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../../lib/app_features_service/mocks';
|
||||
|
||||
describe('Turn Off Policy Protections Migration', () => {
|
||||
let esClient: ElasticsearchClient;
|
||||
let fleetServices: EndpointInternalFleetServicesInterface;
|
||||
let appFeatures: AppFeatures;
|
||||
let appFeatureService: AppFeaturesService;
|
||||
let logger: Logger;
|
||||
|
||||
const callTurnOffPolicyProtections = () =>
|
||||
turnOffPolicyProtectionsIfNotSupported(esClient, fleetServices, appFeatures, logger);
|
||||
turnOffPolicyProtectionsIfNotSupported(esClient, fleetServices, appFeatureService, logger);
|
||||
|
||||
beforeEach(() => {
|
||||
const endpointContextStartContract = createMockEndpointAppContextServiceStartContract();
|
||||
|
||||
({ esClient, appFeatures, logger } = endpointContextStartContract);
|
||||
({ esClient, logger } = endpointContextStartContract);
|
||||
|
||||
appFeatureService = endpointContextStartContract.appFeaturesService;
|
||||
fleetServices = endpointContextStartContract.endpointFleetServicesFactory.asInternalUser();
|
||||
});
|
||||
|
||||
|
@ -70,7 +73,7 @@ describe('Turn Off Policy Protections Migration', () => {
|
|||
policyGenerator = new FleetPackagePolicyGenerator('seed');
|
||||
const packagePolicyListSrv = fleetServices.packagePolicy.list as jest.Mock;
|
||||
|
||||
appFeatures = createAppFeaturesMock(
|
||||
appFeatureService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
|
||||
|
|
|
@ -8,20 +8,20 @@
|
|||
import type { Logger, ElasticsearchClient } from '@kbn/core/server';
|
||||
import type { UpdatePackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
isPolicySetToEventCollectionOnly,
|
||||
ensureOnlyEventCollectionIsAllowed,
|
||||
} from '../../../common/endpoint/models/policy_config_helpers';
|
||||
import type { PolicyData } from '../../../common/endpoint/types';
|
||||
import { AppFeatureSecurityKey } from '../../../common/types/app_features';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
import type { AppFeatures } from '../../lib/app_features';
|
||||
import { getPolicyDataForUpdate } from '../../../common/endpoint/service/policy';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
|
||||
export const turnOffPolicyProtectionsIfNotSupported = async (
|
||||
esClient: ElasticsearchClient,
|
||||
fleetServices: EndpointInternalFleetServicesInterface,
|
||||
appFeaturesService: AppFeatures,
|
||||
appFeaturesService: AppFeaturesService,
|
||||
logger: Logger
|
||||
): Promise<void> => {
|
||||
const log = logger.get('endpoint', 'policyProtections');
|
||||
|
|
|
@ -17,12 +17,12 @@ import {
|
|||
savedObjectsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type {
|
||||
KibanaRequest,
|
||||
RouteConfig,
|
||||
SavedObjectsClientContract,
|
||||
RequestHandler,
|
||||
IRouter,
|
||||
KibanaRequest,
|
||||
RequestHandler,
|
||||
RouteConfig,
|
||||
RouteMethod,
|
||||
SavedObjectsClientContract,
|
||||
} from '@kbn/core/server';
|
||||
import { listMock } from '@kbn/lists-plugin/server/mocks';
|
||||
import { securityMock } from '@kbn/security-plugin/server/mocks';
|
||||
|
@ -30,14 +30,14 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks';
|
|||
import { cloudMock } from '@kbn/cloud-plugin/server/mocks';
|
||||
import type { FleetStartContract } from '@kbn/fleet-plugin/server';
|
||||
import {
|
||||
createPackagePolicyServiceMock,
|
||||
createFleetActionsClientMock,
|
||||
createFleetFromHostFilesClientMock,
|
||||
createFleetToHostFilesClientMock,
|
||||
createMessageSigningServiceMock,
|
||||
createMockAgentPolicyService,
|
||||
createMockAgentService,
|
||||
createMockPackageService,
|
||||
createMessageSigningServiceMock,
|
||||
createFleetFromHostFilesClientMock,
|
||||
createFleetToHostFilesClientMock,
|
||||
createFleetActionsClientMock,
|
||||
createPackagePolicyServiceMock,
|
||||
} from '@kbn/fleet-plugin/server/mocks';
|
||||
import { createFleetAuthzMock } from '@kbn/fleet-plugin/common/mocks';
|
||||
import type { RequestFixtureOptions, RouterMock } from '@kbn/core-http-router-server-mocks';
|
||||
|
@ -45,7 +45,7 @@ import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-ser
|
|||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
import { casesPluginMock } from '@kbn/cases-plugin/server/mocks';
|
||||
import { createCasesClientMock } from '@kbn/cases-plugin/server/client/mocks';
|
||||
import type { VersionedRouteConfig, AddVersionOpts } from '@kbn/core-http-server';
|
||||
import type { AddVersionOpts, VersionedRouteConfig } from '@kbn/core-http-server';
|
||||
import { createActionCreateServiceMock } from './services/actions/mocks';
|
||||
import { getEndpointAuthzInitialStateMock } from '../../common/endpoint/service/authz/mocks';
|
||||
import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
|
@ -71,7 +71,7 @@ import type { EndpointAuthz } from '../../common/endpoint/types/authz';
|
|||
import { EndpointFleetServicesFactory } from './services/fleet';
|
||||
import { createLicenseServiceMock } from '../../common/license/mocks';
|
||||
import { createFeatureUsageServiceMock } from './services/feature_usage/mocks';
|
||||
import { createAppFeaturesMock } from '../lib/app_features/mocks';
|
||||
import { createAppFeaturesServiceMock } from '../lib/app_features_service/mocks';
|
||||
|
||||
/**
|
||||
* Creates a mocked EndpointAppContext.
|
||||
|
@ -165,7 +165,12 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
savedObjectsStart
|
||||
);
|
||||
const experimentalFeatures = config.experimentalFeatures;
|
||||
const appFeatures = createAppFeaturesMock(undefined, experimentalFeatures, undefined, logger);
|
||||
const appFeaturesService = createAppFeaturesServiceMock(
|
||||
undefined,
|
||||
experimentalFeatures,
|
||||
undefined,
|
||||
logger
|
||||
);
|
||||
|
||||
packagePolicyService.list.mockImplementation(async (_, options) => {
|
||||
return {
|
||||
|
@ -215,7 +220,7 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
actionCreateService: undefined,
|
||||
createFleetActionsClient: jest.fn((_) => fleetActionsClientMock),
|
||||
esClient: elasticsearchClientMock.createElasticsearchClient(),
|
||||
appFeatures,
|
||||
appFeaturesService,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
elasticsearchServiceMock,
|
||||
loggingSystemMock,
|
||||
savedObjectsClientMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
|
@ -16,20 +16,20 @@ import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks';
|
|||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import { listMock } from '@kbn/lists-plugin/server/mocks';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import {
|
||||
createPackagePolicyWithManifestMock,
|
||||
createPackagePolicyWithInitialManifestMock,
|
||||
getMockManifest,
|
||||
getMockArtifactsWithDiff,
|
||||
createPackagePolicyWithManifestMock,
|
||||
getEmptyMockArtifacts,
|
||||
getMockArtifactsWithDiff,
|
||||
getMockManifest,
|
||||
} from '../../../lib/artifacts/mocks';
|
||||
import { createEndpointArtifactClientMock, getManifestClientMock } from '../mocks';
|
||||
import type { ManifestManagerContext } from './manifest_manager';
|
||||
import { ManifestManager } from './manifest_manager';
|
||||
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
|
||||
import { createAppFeaturesMock } from '../../../../lib/app_features/mocks';
|
||||
import type { AppFeatureKeys } from '../../../../../common/types/app_features';
|
||||
import type { AppFeatures } from '../../../../lib/app_features/app_features';
|
||||
import { createAppFeaturesServiceMock } from '../../../../lib/app_features_service/mocks';
|
||||
import type { AppFeaturesService } from '../../../../lib/app_features_service/app_features_service';
|
||||
|
||||
export const createExceptionListResponse = (data: ExceptionListItemSchema[], total?: number) => ({
|
||||
data,
|
||||
|
@ -71,7 +71,7 @@ export interface ManifestManagerMockOptions {
|
|||
exceptionListClient: ExceptionListClient;
|
||||
packagePolicyService: jest.Mocked<PackagePolicyClient>;
|
||||
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
appFeatures: AppFeatures;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
}
|
||||
|
||||
export const buildManifestManagerMockOptions = (
|
||||
|
@ -83,7 +83,7 @@ export const buildManifestManagerMockOptions = (
|
|||
exceptionListClient: listMock.getExceptionListClient(savedObjectMock),
|
||||
packagePolicyService: createPackagePolicyServiceMock(),
|
||||
savedObjectsClient: savedObjectMock,
|
||||
appFeatures: createAppFeaturesMock(customAppFeatures),
|
||||
appFeaturesService: createAppFeaturesServiceMock(customAppFeatures),
|
||||
...opts,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
|
||||
ENDPOINT_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
|
||||
import type { PackagePolicy } from '@kbn/fleet-plugin/common/types/models';
|
||||
|
@ -27,10 +27,10 @@ import {
|
|||
toArtifactRecords,
|
||||
} from '../../../lib/artifacts/mocks';
|
||||
import {
|
||||
ManifestConstants,
|
||||
getArtifactId,
|
||||
translateToEndpointExceptions,
|
||||
Manifest,
|
||||
ManifestConstants,
|
||||
translateToEndpointExceptions,
|
||||
} from '../../../lib/artifacts';
|
||||
|
||||
import {
|
||||
|
@ -43,7 +43,7 @@ import type { EndpointArtifactClientInterface } from '../artifact_client';
|
|||
import { InvalidInternalManifestError } from '../errors';
|
||||
import { EndpointError } from '../../../../../common/endpoint/errors';
|
||||
import type { Artifact } from '@kbn/fleet-plugin/server';
|
||||
import { AppFeatureSecurityKey } from '../../../../../common/types/app_features';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types/src/response/exception_list_item_schema';
|
||||
|
||||
const getArtifactObject = (artifact: InternalArtifactSchema) =>
|
||||
|
@ -257,7 +257,7 @@ describe('ManifestManager', () => {
|
|||
|
||||
(
|
||||
manifestManagerContext.artifactClient as jest.Mocked<EndpointArtifactClientInterface>
|
||||
).listArtifacts.mockImplementation(async (id) => {
|
||||
).listArtifacts.mockImplementation(async () => {
|
||||
// report the MACOS Exceptions artifact as not found
|
||||
return {
|
||||
items: [
|
||||
|
|
|
@ -6,43 +6,46 @@
|
|||
*/
|
||||
|
||||
import semver from 'semver';
|
||||
import { isEqual, isEmpty, chunk, keyBy } from 'lodash';
|
||||
import { chunk, isEmpty, isEqual, keyBy } from 'lodash';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { type Logger, type SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import {
|
||||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
ENDPOINT_BLOCKLISTS_LIST_ID,
|
||||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
|
||||
ENDPOINT_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import type { ListResult, PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { AppFeatureKey } from '../../../../../common/types/app_features';
|
||||
import type { AppFeatures } from '../../../../lib/app_features';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { AppFeaturesService } from '../../../../lib/app_features_service/app_features_service';
|
||||
import type { ExperimentalFeatures } from '../../../../../common';
|
||||
import type { ManifestSchemaVersion } from '../../../../../common/endpoint/schema/common';
|
||||
import type { ManifestSchema } from '../../../../../common/endpoint/schema/manifest';
|
||||
import { manifestDispatchSchema } from '../../../../../common/endpoint/schema/manifest';
|
||||
import {
|
||||
manifestDispatchSchema,
|
||||
type ManifestSchema,
|
||||
} from '../../../../../common/endpoint/schema/manifest';
|
||||
|
||||
import type { ArtifactListId } from '../../../lib/artifacts';
|
||||
import {
|
||||
ArtifactConstants,
|
||||
type ArtifactListId,
|
||||
buildArtifact,
|
||||
convertExceptionsToEndpointFormat,
|
||||
getAllItemsFromEndpointExceptionList,
|
||||
getArtifactId,
|
||||
Manifest,
|
||||
convertExceptionsToEndpointFormat,
|
||||
} from '../../../lib/artifacts';
|
||||
import type {
|
||||
InternalArtifactCompleteSchema,
|
||||
WrappedTranslatedExceptionList,
|
||||
|
||||
import {
|
||||
internalArtifactCompleteSchema,
|
||||
type InternalArtifactCompleteSchema,
|
||||
type WrappedTranslatedExceptionList,
|
||||
} from '../../../schemas/artifacts';
|
||||
import { internalArtifactCompleteSchema } from '../../../schemas/artifacts';
|
||||
import type { EndpointArtifactClientInterface } from '../artifact_client';
|
||||
import { ManifestClient } from '../manifest_client';
|
||||
import type { ExperimentalFeatures } from '../../../../../common/experimental_features';
|
||||
import { InvalidInternalManifestError } from '../errors';
|
||||
import { wrapErrorIfNeeded } from '../../../utils';
|
||||
import { EndpointError } from '../../../../../common/endpoint/errors';
|
||||
|
@ -99,7 +102,7 @@ export interface ManifestManagerContext {
|
|||
experimentalFeatures: ExperimentalFeatures;
|
||||
packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
esClient: ElasticsearchClient;
|
||||
appFeatures: AppFeatures;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
}
|
||||
|
||||
const getArtifactIds = (manifest: ManifestSchema) =>
|
||||
|
@ -121,7 +124,7 @@ export class ManifestManager {
|
|||
protected cachedExceptionsListsByOs: Map<string, ExceptionListItemSchema[]>;
|
||||
protected packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
protected esClient: ElasticsearchClient;
|
||||
protected appFeatures: AppFeatures;
|
||||
protected appFeaturesService: AppFeaturesService;
|
||||
|
||||
constructor(context: ManifestManagerContext) {
|
||||
this.artifactClient = context.artifactClient;
|
||||
|
@ -135,7 +138,7 @@ export class ManifestManager {
|
|||
this.packagerTaskPackagePolicyUpdateBatchSize =
|
||||
context.packagerTaskPackagePolicyUpdateBatchSize;
|
||||
this.esClient = context.esClient;
|
||||
this.appFeatures = context.appFeatures;
|
||||
this.appFeaturesService = context.appFeaturesService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,9 +170,9 @@ export class ManifestManager {
|
|||
let itemsByListId: ExceptionListItemSchema[] = [];
|
||||
if (
|
||||
(listId === ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID &&
|
||||
this.appFeatures.isEnabled(AppFeatureKey.endpointResponseActions)) ||
|
||||
this.appFeaturesService.isEnabled(AppFeatureKey.endpointResponseActions)) ||
|
||||
(listId !== ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID &&
|
||||
this.appFeatures.isEnabled(AppFeatureKey.endpointArtifactManagement))
|
||||
this.appFeaturesService.isEnabled(AppFeatureKey.endpointArtifactManagement))
|
||||
) {
|
||||
itemsByListId = await getAllItemsFromEndpointExceptionList({
|
||||
elClient,
|
||||
|
|
|
@ -25,11 +25,12 @@ import {
|
|||
import { buildManifestManagerMock } from '../endpoint/services/artifacts/manifest_manager/manifest_manager.mock';
|
||||
import {
|
||||
getPackagePolicyCreateCallback,
|
||||
getPackagePolicyPostCreateCallback,
|
||||
getPackagePolicyDeleteCallback,
|
||||
getPackagePolicyPostCreateCallback,
|
||||
getPackagePolicyUpdateCallback,
|
||||
} from './fleet_integration';
|
||||
import type { KibanaRequest } from '@kbn/core/server';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
import { requestContextFactoryMock } from '../request_context_factory.mock';
|
||||
import type { EndpointAppContextServiceStartContract } from '../endpoint/endpoint_app_context_services';
|
||||
|
@ -54,9 +55,8 @@ import { createMockPolicyData } from '../endpoint/services/feature_usage/mocks';
|
|||
import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../common/endpoint/service/artifacts/constants';
|
||||
import { ENDPOINT_EVENT_FILTERS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { disableProtections } from '../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeatures } from '../lib/app_features';
|
||||
import { createAppFeaturesMock } from '../lib/app_features/mocks';
|
||||
import { ALL_APP_FEATURE_KEYS } from '../../common';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../lib/app_features_service/mocks';
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: (): string => 'NEW_UUID',
|
||||
|
@ -77,7 +77,7 @@ describe('ingest_integration tests ', () => {
|
|||
});
|
||||
const generator = new EndpointDocGenerator();
|
||||
const cloudService = cloudMock.createSetup();
|
||||
let appFeatures: AppFeatures;
|
||||
let appFeaturesService: AppFeaturesService;
|
||||
|
||||
beforeEach(() => {
|
||||
endpointAppContextMock = createMockEndpointAppContextServiceStartContract();
|
||||
|
@ -86,7 +86,7 @@ describe('ingest_integration tests ', () => {
|
|||
licenseEmitter = new Subject();
|
||||
licenseService = new LicenseService();
|
||||
licenseService.start(licenseEmitter);
|
||||
appFeatures = endpointAppContextMock.appFeatures;
|
||||
appFeaturesService = endpointAppContextMock.appFeaturesService;
|
||||
|
||||
jest
|
||||
.spyOn(endpointAppContextMock.endpointMetadataService, 'getFleetEndpointPackagePolicy')
|
||||
|
@ -143,7 +143,7 @@ describe('ingest_integration tests ', () => {
|
|||
licenseService,
|
||||
exceptionListClient,
|
||||
cloudService,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
|
||||
return callback(
|
||||
|
@ -395,7 +395,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -414,7 +414,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -448,7 +448,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -463,7 +463,7 @@ describe('ingest_integration tests ', () => {
|
|||
});
|
||||
|
||||
it('should turn off protections if endpointPolicyProtections appFeature is disabled', async () => {
|
||||
appFeatures = createAppFeaturesMock(
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
const callback = getPackagePolicyUpdateCallback(
|
||||
|
@ -473,7 +473,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
|
||||
const updatedPolicy = await callback(
|
||||
|
@ -552,7 +552,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
|
||||
|
@ -589,7 +589,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeatures
|
||||
appFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
// values should be updated
|
||||
|
|
|
@ -22,12 +22,11 @@ import type {
|
|||
} from '@kbn/fleet-plugin/common';
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { InfoResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { AppFeatureSecurityKey } from '../../common/types/app_features';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
isPolicySetToEventCollectionOnly,
|
||||
ensureOnlyEventCollectionIsAllowed,
|
||||
} from '../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeatures } from '../lib/app_features';
|
||||
import type { NewPolicyData, PolicyConfig } from '../../common/endpoint/types';
|
||||
import type { LicenseService } from '../../common/license';
|
||||
import type { ManifestManager } from '../endpoint/services';
|
||||
|
@ -44,6 +43,7 @@ import { notifyProtectionFeatureUsage } from './notify_protection_feature_usage'
|
|||
import type { AnyPolicyCreateConfig } from './types';
|
||||
import { ENDPOINT_INTEGRATION_CONFIG_KEY } from './constants';
|
||||
import { createEventFilters } from './handlers/create_event_filters';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
|
||||
const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
||||
packagePolicy: T
|
||||
|
@ -81,7 +81,7 @@ export const getPackagePolicyCreateCallback = (
|
|||
licenseService: LicenseService,
|
||||
exceptionsClient: ExceptionListClient | undefined,
|
||||
cloud: CloudSetup,
|
||||
appFeatures: AppFeatures
|
||||
appFeatures: AppFeaturesService
|
||||
): PostPackagePolicyCreateCallback => {
|
||||
return async (
|
||||
newPackagePolicy,
|
||||
|
@ -186,7 +186,7 @@ export const getPackagePolicyUpdateCallback = (
|
|||
endpointMetadataService: EndpointMetadataService,
|
||||
cloud: CloudSetup,
|
||||
esClient: ElasticsearchClient,
|
||||
appFeatures: AppFeatures
|
||||
appFeatures: AppFeaturesService
|
||||
): PutPackagePolicyUpdateCallback => {
|
||||
return async (newPackagePolicy: NewPackagePolicy): Promise<UpdatePackagePolicy> => {
|
||||
if (!isEndpointPackagePolicy(newPackagePolicy)) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Subject } from 'rxjs';
|
|||
import type { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/server/mocks';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { LicenseService } from '../../../common/license';
|
||||
import { createDefaultPolicy } from './create_default_policy';
|
||||
import { ProtectionModes } from '../../../common/endpoint/types';
|
||||
|
@ -19,9 +20,8 @@ import type {
|
|||
PolicyCreateCloudConfig,
|
||||
PolicyCreateEndpointConfig,
|
||||
} from '../types';
|
||||
import type { AppFeatures } from '../../lib/app_features';
|
||||
import { createAppFeaturesMock } from '../../lib/app_features/mocks';
|
||||
import { ALL_APP_FEATURE_KEYS } from '../../../common';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../../lib/app_features_service/mocks';
|
||||
|
||||
describe('Create Default Policy tests ', () => {
|
||||
const cloud = cloudMock.createSetup();
|
||||
|
@ -31,7 +31,7 @@ describe('Create Default Policy tests ', () => {
|
|||
const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold', uid: '' } });
|
||||
let licenseEmitter: Subject<ILicense>;
|
||||
let licenseService: LicenseService;
|
||||
let appFeatures: AppFeatures;
|
||||
let appFeaturesService: AppFeaturesService;
|
||||
|
||||
const createDefaultPolicyCallback = async (
|
||||
config: AnyPolicyCreateConfig | undefined
|
||||
|
@ -39,7 +39,7 @@ describe('Create Default Policy tests ', () => {
|
|||
const esClientInfo = await elasticsearchServiceMock.createClusterClient().asInternalUser.info();
|
||||
esClientInfo.cluster_name = '';
|
||||
esClientInfo.cluster_uuid = '';
|
||||
return createDefaultPolicy(licenseService, config, cloud, esClientInfo, appFeatures);
|
||||
return createDefaultPolicy(licenseService, config, cloud, esClientInfo, appFeaturesService);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -47,7 +47,7 @@ describe('Create Default Policy tests ', () => {
|
|||
licenseService = new LicenseService();
|
||||
licenseService.start(licenseEmitter);
|
||||
licenseEmitter.next(Platinum); // set license level to platinum
|
||||
appFeatures = createAppFeaturesMock();
|
||||
appFeaturesService = createAppFeaturesServiceMock();
|
||||
});
|
||||
|
||||
describe('When no config is set', () => {
|
||||
|
@ -211,7 +211,7 @@ describe('Create Default Policy tests ', () => {
|
|||
});
|
||||
|
||||
it('should set policy to event collection only if endpointPolicyProtections appFeature is disabled', async () => {
|
||||
appFeatures = createAppFeaturesMock(
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { InfoResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { AppFeatureSecurityKey } from '../../../common/types/app_features';
|
||||
import type { AppFeatures } from '../../lib/app_features';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
policyFactory as policyConfigFactory,
|
||||
policyFactoryWithoutPaidFeatures as policyConfigFactoryWithoutPaidFeatures,
|
||||
|
@ -26,6 +25,7 @@ import {
|
|||
disableProtections,
|
||||
ensureOnlyEventCollectionIsAllowed,
|
||||
} from '../../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
|
||||
/**
|
||||
* Create the default endpoint policy based on the current license and configuration type
|
||||
|
@ -35,7 +35,7 @@ export const createDefaultPolicy = (
|
|||
config: AnyPolicyCreateConfig | undefined,
|
||||
cloud: CloudSetup,
|
||||
esClientInfo: InfoResponse,
|
||||
appFeatures: AppFeatures
|
||||
appFeatures: AppFeaturesService
|
||||
): PolicyConfig => {
|
||||
// Pass license and cloud information to use in Policy creation
|
||||
const factoryPolicy = policyConfigFactory(
|
||||
|
|
|
@ -1,175 +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 { AppFeatures } from '.';
|
||||
import type { AppFeatureKeys, ExperimentalFeatures } from '../../../common';
|
||||
import type { PluginSetupContract } from '@kbn/features-plugin/server';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
|
||||
const SECURITY_BASE_CONFIG = {
|
||||
foo: 'foo',
|
||||
};
|
||||
|
||||
const SECURITY_APP_FEATURE_CONFIG = {
|
||||
'test-base-feature': {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-capability'],
|
||||
api: ['test-capability'],
|
||||
},
|
||||
read: {
|
||||
ui: ['test-capability'],
|
||||
api: ['test-capability'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const CASES_BASE_CONFIG = {
|
||||
bar: 'bar',
|
||||
};
|
||||
|
||||
const CASES_APP_FEATURE_CONFIG = {
|
||||
'test-cases-feature': {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-cases-capability'],
|
||||
api: ['test-cases-capability'],
|
||||
},
|
||||
read: {
|
||||
ui: ['test-cases-capability'],
|
||||
api: ['test-cases-capability'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ASSISTANT_BASE_CONFIG = {
|
||||
bar: 'bar',
|
||||
};
|
||||
|
||||
const ASSISTANT_APP_FEATURE_CONFIG = {
|
||||
'test-assistant-feature': {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-assistant-capability'],
|
||||
api: ['test-assistant-capability'],
|
||||
},
|
||||
read: {
|
||||
ui: ['test-assistant-capability'],
|
||||
api: ['test-assistant-capability'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('./security_kibana_features', () => {
|
||||
return {
|
||||
getSecurityBaseKibanaFeature: jest.fn(() => SECURITY_BASE_CONFIG),
|
||||
getSecurityBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
|
||||
getSecurityAppFeaturesConfig: jest.fn(() => SECURITY_APP_FEATURE_CONFIG),
|
||||
};
|
||||
});
|
||||
jest.mock('./security_kibana_sub_features', () => {
|
||||
return {
|
||||
securitySubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./security_cases_kibana_features', () => {
|
||||
return {
|
||||
getCasesBaseKibanaFeature: jest.fn(() => CASES_BASE_CONFIG),
|
||||
getCasesBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
|
||||
getCasesAppFeaturesConfig: jest.fn(() => CASES_APP_FEATURE_CONFIG),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./security_cases_kibana_sub_features', () => {
|
||||
return {
|
||||
casesSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./security_assistant_kibana_features', () => {
|
||||
return {
|
||||
getAssistantBaseKibanaFeature: jest.fn(() => ASSISTANT_BASE_CONFIG),
|
||||
getAssistantBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
|
||||
getAssistantAppFeaturesConfig: jest.fn(() => ASSISTANT_APP_FEATURE_CONFIG),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./security_assistant_kibana_sub_features', () => {
|
||||
return {
|
||||
assistantSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
};
|
||||
});
|
||||
|
||||
describe('AppFeatures', () => {
|
||||
it('should register enabled kibana features', () => {
|
||||
const featuresSetup = {
|
||||
registerKibanaFeature: jest.fn(),
|
||||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatureKeys = ['test-base-feature'] as unknown as AppFeatureKeys;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
[] as unknown as ExperimentalFeatures
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.set(appFeatureKeys);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...SECURITY_BASE_CONFIG,
|
||||
...SECURITY_APP_FEATURE_CONFIG['test-base-feature'],
|
||||
subFeatures: [{ baz: 'baz' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should register enabled cases features', () => {
|
||||
const featuresSetup = {
|
||||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatureKeys = ['test-cases-feature'] as unknown as AppFeatureKeys;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
[] as unknown as ExperimentalFeatures
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.set(appFeatureKeys);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...CASES_BASE_CONFIG,
|
||||
...CASES_APP_FEATURE_CONFIG['test-cases-feature'],
|
||||
subFeatures: [{ baz: 'baz' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should register enabled assistant features', () => {
|
||||
const featuresSetup = {
|
||||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatureKeys = ['test-assistant-feature'] as unknown as AppFeatureKeys;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
[] as unknown as ExperimentalFeatures
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.set(appFeatureKeys);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...ASSISTANT_BASE_CONFIG,
|
||||
...ASSISTANT_APP_FEATURE_CONFIG['test-assistant-feature'],
|
||||
subFeatures: [{ baz: 'baz' }],
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,144 +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 type { Logger } from '@kbn/core/server';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import type { AppFeatureKey, AppFeatureKeys, ExperimentalFeatures } from '../../../common';
|
||||
import type { AppFeatureKibanaConfig, AppFeaturesConfig } from './types';
|
||||
import {
|
||||
getSecurityAppFeaturesConfig,
|
||||
getSecurityBaseKibanaFeature,
|
||||
getSecurityBaseKibanaSubFeatureIds,
|
||||
} from './security_kibana_features';
|
||||
import {
|
||||
getCasesBaseKibanaFeature,
|
||||
getCasesAppFeaturesConfig,
|
||||
getCasesBaseKibanaSubFeatureIds,
|
||||
} from './security_cases_kibana_features';
|
||||
import { AppFeaturesConfigMerger } from './app_features_config_merger';
|
||||
import { casesSubFeaturesMap } from './security_cases_kibana_sub_features';
|
||||
import { securitySubFeaturesMap } from './security_kibana_sub_features';
|
||||
import { assistantSubFeaturesMap } from './security_assistant_kibana_sub_features';
|
||||
import {
|
||||
getAssistantAppFeaturesConfig,
|
||||
getAssistantBaseKibanaFeature,
|
||||
getAssistantBaseKibanaSubFeatureIds,
|
||||
} from './security_assistant_kibana_features';
|
||||
|
||||
export class AppFeatures {
|
||||
private securityFeatureConfigMerger: AppFeaturesConfigMerger;
|
||||
private assistantFeatureConfigMerger: AppFeaturesConfigMerger;
|
||||
private casesFeatureConfigMerger: AppFeaturesConfigMerger;
|
||||
private appFeatures?: Set<AppFeatureKey>;
|
||||
private featuresSetup?: FeaturesPluginSetup;
|
||||
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly experimentalFeatures: ExperimentalFeatures
|
||||
) {
|
||||
this.securityFeatureConfigMerger = new AppFeaturesConfigMerger(
|
||||
this.logger,
|
||||
securitySubFeaturesMap
|
||||
);
|
||||
this.casesFeatureConfigMerger = new AppFeaturesConfigMerger(this.logger, casesSubFeaturesMap);
|
||||
this.assistantFeatureConfigMerger = new AppFeaturesConfigMerger(
|
||||
this.logger,
|
||||
assistantSubFeaturesMap
|
||||
);
|
||||
}
|
||||
|
||||
public init(featuresSetup: FeaturesPluginSetup) {
|
||||
this.featuresSetup = featuresSetup;
|
||||
}
|
||||
|
||||
public set(appFeatureKeys: AppFeatureKeys) {
|
||||
if (this.appFeatures) {
|
||||
throw new Error('AppFeatures has already been initialized');
|
||||
}
|
||||
this.appFeatures = new Set(appFeatureKeys);
|
||||
this.registerEnabledKibanaFeatures();
|
||||
}
|
||||
|
||||
public isEnabled(appFeatureKey: AppFeatureKey): boolean {
|
||||
if (!this.appFeatures) {
|
||||
throw new Error('AppFeatures has not been initialized');
|
||||
}
|
||||
return this.appFeatures.has(appFeatureKey);
|
||||
}
|
||||
|
||||
protected registerEnabledKibanaFeatures() {
|
||||
if (this.featuresSetup == null) {
|
||||
throw new Error(
|
||||
'Cannot sync kibana features as featuresSetup is not present. Did you call init?'
|
||||
);
|
||||
}
|
||||
// register main security Kibana features
|
||||
const securityBaseKibanaFeature = getSecurityBaseKibanaFeature();
|
||||
const securityBaseKibanaSubFeatureIds = getSecurityBaseKibanaSubFeatureIds(
|
||||
this.experimentalFeatures
|
||||
);
|
||||
const enabledSecurityAppFeaturesConfigs = this.getEnabledAppFeaturesConfigs(
|
||||
getSecurityAppFeaturesConfig(this.experimentalFeatures)
|
||||
);
|
||||
const completeAppFeatureConfig = this.securityFeatureConfigMerger.mergeAppFeatureConfigs(
|
||||
securityBaseKibanaFeature,
|
||||
securityBaseKibanaSubFeatureIds,
|
||||
enabledSecurityAppFeaturesConfigs
|
||||
);
|
||||
|
||||
this.logger.debug(JSON.stringify(completeAppFeatureConfig));
|
||||
|
||||
this.featuresSetup.registerKibanaFeature(completeAppFeatureConfig);
|
||||
|
||||
// register security cases Kibana features
|
||||
const securityCasesBaseKibanaFeature = getCasesBaseKibanaFeature();
|
||||
const securityCasesBaseKibanaSubFeatureIds = getCasesBaseKibanaSubFeatureIds();
|
||||
const enabledCasesAppFeaturesConfigs = this.getEnabledAppFeaturesConfigs(
|
||||
getCasesAppFeaturesConfig()
|
||||
);
|
||||
const completeCasesAppFeatureConfig = this.casesFeatureConfigMerger.mergeAppFeatureConfigs(
|
||||
securityCasesBaseKibanaFeature,
|
||||
securityCasesBaseKibanaSubFeatureIds,
|
||||
enabledCasesAppFeaturesConfigs
|
||||
);
|
||||
|
||||
this.logger.info(JSON.stringify(completeCasesAppFeatureConfig));
|
||||
|
||||
this.featuresSetup.registerKibanaFeature(completeCasesAppFeatureConfig);
|
||||
|
||||
// register security assistant Kibana features
|
||||
const securityAssistantBaseKibanaFeature = getAssistantBaseKibanaFeature();
|
||||
const securityAssistantBaseKibanaSubFeatureIds = getAssistantBaseKibanaSubFeatureIds();
|
||||
const enabledAssistantAppFeaturesConfigs = this.getEnabledAppFeaturesConfigs(
|
||||
getAssistantAppFeaturesConfig()
|
||||
);
|
||||
const completeAssistantAppFeatureConfig =
|
||||
this.assistantFeatureConfigMerger.mergeAppFeatureConfigs(
|
||||
securityAssistantBaseKibanaFeature,
|
||||
securityAssistantBaseKibanaSubFeatureIds,
|
||||
enabledAssistantAppFeaturesConfigs
|
||||
);
|
||||
|
||||
this.logger.info(JSON.stringify(completeAssistantAppFeatureConfig));
|
||||
|
||||
this.featuresSetup.registerKibanaFeature(completeAssistantAppFeatureConfig);
|
||||
}
|
||||
|
||||
private getEnabledAppFeaturesConfigs(
|
||||
appFeaturesConfigs: Partial<AppFeaturesConfig>
|
||||
): AppFeatureKibanaConfig[] {
|
||||
return Object.entries(appFeaturesConfigs).reduce<AppFeatureKibanaConfig[]>(
|
||||
(acc, [appFeatureKey, appFeatureConfig]) => {
|
||||
if (this.isEnabled(appFeatureKey as AppFeatureKey)) {
|
||||
acc.push(appFeatureConfig);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 type { Logger } from '@kbn/core/server';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
import { AppFeatures } from './app_features';
|
||||
import type { AppFeatureKeys, ExperimentalFeatures } from '../../../common';
|
||||
import { ALL_APP_FEATURE_KEYS, allowedExperimentalValues } from '../../../common';
|
||||
|
||||
class AppFeaturesMock extends AppFeatures {
|
||||
protected registerEnabledKibanaFeatures() {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
|
||||
export const createAppFeaturesMock = (
|
||||
/** What features keys should be enabled. Default is all */
|
||||
enabledFeatureKeys: AppFeatureKeys = [...ALL_APP_FEATURE_KEYS],
|
||||
experimentalFeatures: ExperimentalFeatures = { ...allowedExperimentalValues },
|
||||
featuresPluginSetupContract: FeaturesPluginSetup = featuresPluginMock.createSetup(),
|
||||
logger: Logger = loggingSystemMock.create().get('appFeatureMock')
|
||||
) => {
|
||||
const appFeatures = new AppFeaturesMock(logger, experimentalFeatures);
|
||||
|
||||
appFeatures.init(featuresPluginSetupContract);
|
||||
|
||||
if (enabledFeatureKeys) {
|
||||
appFeatures.set(enabledFeatureKeys);
|
||||
}
|
||||
|
||||
return appFeatures;
|
||||
};
|
|
@ -1,74 +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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import type { AppFeaturesAssistantConfig, BaseKibanaFeatureConfig } from './types';
|
||||
import { APP_ID, ASSISTANT_FEATURE_ID } from '../../../common/constants';
|
||||
import { AppFeatureAssistantKey } from '../../../common/types/app_features';
|
||||
import type { AssistantSubFeatureId } from './security_assistant_kibana_sub_features';
|
||||
|
||||
export const getAssistantBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({
|
||||
id: ASSISTANT_FEATURE_ID,
|
||||
name: i18n.translate(
|
||||
'xpack.securitySolution.featureRegistry.linkSecuritySolutionAssistantTitle',
|
||||
{
|
||||
defaultMessage: 'Elastic AI Assistant',
|
||||
}
|
||||
),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
minimumLicense: 'enterprise',
|
||||
privileges: {
|
||||
all: {
|
||||
api: [],
|
||||
app: [ASSISTANT_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: [],
|
||||
},
|
||||
read: {
|
||||
// No read-only mode currently supported
|
||||
disabled: true,
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const getAssistantBaseKibanaSubFeatureIds = (): AssistantSubFeatureId[] => [
|
||||
// This is a sample sub-feature that can be used for future implementations
|
||||
// AssistantSubFeatureId.createConversation,
|
||||
];
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Assistant feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const getAssistantAppFeaturesConfig = (): AppFeaturesAssistantConfig => ({
|
||||
[AppFeatureAssistantKey.assistant]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['ai-assistant'],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,118 +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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import {
|
||||
createUICapabilities as createCasesUICapabilities,
|
||||
getApiTags as getCasesApiTags,
|
||||
} from '@kbn/cases-plugin/common';
|
||||
import {
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
import type { AppFeaturesCasesConfig, BaseKibanaFeatureConfig } from './types';
|
||||
import { APP_ID, CASES_FEATURE_ID } from '../../../common/constants';
|
||||
import { CasesSubFeatureId } from './security_cases_kibana_sub_features';
|
||||
import { AppFeatureCasesKey } from '../../../common/types/app_features';
|
||||
|
||||
const casesCapabilities = createCasesUICapabilities();
|
||||
const casesApiTags = getCasesApiTags(APP_ID);
|
||||
|
||||
export const getCasesBaseKibanaFeature = (): BaseKibanaFeatureConfig => {
|
||||
// On SecuritySolution essentials cases does not have the connector feature
|
||||
const casesAllUICapabilities = casesCapabilities.all.filter(
|
||||
(capability) => capability !== CASES_CONNECTORS_CAPABILITY
|
||||
);
|
||||
|
||||
const casesReadUICapabilities = casesCapabilities.read.filter(
|
||||
(capability) => capability !== CASES_CONNECTORS_CAPABILITY
|
||||
);
|
||||
|
||||
const casesAllAPICapabilities = casesApiTags.all.filter(
|
||||
(capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG
|
||||
);
|
||||
|
||||
const casesReadAPICapabilities = casesApiTags.read.filter(
|
||||
(capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG
|
||||
);
|
||||
|
||||
return {
|
||||
id: CASES_FEATURE_ID,
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle', {
|
||||
defaultMessage: 'Cases',
|
||||
}),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: [APP_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
api: casesAllAPICapabilities,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
create: [APP_ID],
|
||||
read: [APP_ID],
|
||||
update: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [...filesSavedObjectTypes],
|
||||
read: [...filesSavedObjectTypes],
|
||||
},
|
||||
ui: casesAllUICapabilities,
|
||||
},
|
||||
read: {
|
||||
api: casesReadAPICapabilities,
|
||||
app: [CASES_FEATURE_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
cases: {
|
||||
read: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [...filesSavedObjectTypes],
|
||||
},
|
||||
ui: casesReadUICapabilities,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getCasesBaseKibanaSubFeatureIds = (): CasesSubFeatureId[] => [
|
||||
CasesSubFeatureId.deleteCases,
|
||||
];
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Cases app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Cases feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Cases subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Cases subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const getCasesAppFeaturesConfig = (): AppFeaturesCasesConfig => ({
|
||||
[AppFeatureCasesKey.casesConnectors]: {
|
||||
privileges: {
|
||||
all: {
|
||||
api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges
|
||||
ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges
|
||||
cases: {
|
||||
push: [APP_ID], // Add cases connector push privileges
|
||||
},
|
||||
},
|
||||
read: {
|
||||
api: [GET_CONNECTORS_CONFIGURE_API_TAG], // Add cases connector get connectors API privileges
|
||||
ui: [CASES_CONNECTORS_CAPABILITY], // Add cases connector UI privileges
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,58 +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 { i18n } from '@kbn/i18n';
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import {
|
||||
createUICapabilities as createCasesUICapabilities,
|
||||
getApiTags as getCasesApiTags,
|
||||
} from '@kbn/cases-plugin/common';
|
||||
import { APP_ID } from '../../../common/constants';
|
||||
|
||||
const casesCapabilities = createCasesUICapabilities();
|
||||
const casesApiTags = getCasesApiTags(APP_ID);
|
||||
|
||||
const deleteCasesSubFeature: SubFeatureConfig = {
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.deleteSubFeatureName', {
|
||||
defaultMessage: 'Delete',
|
||||
}),
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
api: casesApiTags.delete,
|
||||
id: 'cases_delete',
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.deleteSubFeatureDetails', {
|
||||
defaultMessage: 'Delete cases and comments',
|
||||
}),
|
||||
includeIn: 'all',
|
||||
savedObject: {
|
||||
all: [...filesSavedObjectTypes],
|
||||
read: [...filesSavedObjectTypes],
|
||||
},
|
||||
cases: {
|
||||
delete: [APP_ID],
|
||||
},
|
||||
ui: casesCapabilities.delete,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export enum CasesSubFeatureId {
|
||||
deleteCases = 'deleteCasesSubFeature',
|
||||
}
|
||||
|
||||
// Defines all the ordered Security Cases subFeatures available
|
||||
export const casesSubFeaturesMap = Object.freeze(
|
||||
new Map<CasesSubFeatureId, SubFeatureConfig>([
|
||||
[CasesSubFeatureId.deleteCases, deleteCasesSubFeature],
|
||||
])
|
||||
);
|
|
@ -1,239 +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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common';
|
||||
import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC } from '@kbn/securitysolution-list-constants';
|
||||
import {
|
||||
EQL_RULE_TYPE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
NEW_TERMS_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
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 { AppFeaturesSecurityConfig, BaseKibanaFeatureConfig } from './types';
|
||||
import { AppFeatureSecurityKey } from '../../../common/types/app_features';
|
||||
|
||||
// Same as the plugin id defined by Cloud Security Posture
|
||||
const CLOUD_POSTURE_APP_ID = 'csp';
|
||||
// Same as the saved-object type for rules defined by Cloud Security Posture
|
||||
const CLOUD_POSTURE_SAVED_OBJECT_RULE_TYPE = 'csp_rule';
|
||||
|
||||
const SECURITY_RULE_TYPES = [
|
||||
LEGACY_NOTIFICATIONS_ID,
|
||||
EQL_RULE_TYPE_ID,
|
||||
INDICATOR_RULE_TYPE_ID,
|
||||
ML_RULE_TYPE_ID,
|
||||
QUERY_RULE_TYPE_ID,
|
||||
SAVED_QUERY_RULE_TYPE_ID,
|
||||
THRESHOLD_RULE_TYPE_ID,
|
||||
NEW_TERMS_RULE_TYPE_ID,
|
||||
];
|
||||
|
||||
export const getSecurityBaseKibanaFeature = (): BaseKibanaFeatureConfig => ({
|
||||
id: SERVER_APP_ID,
|
||||
name: i18n.translate('xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle', {
|
||||
defaultMessage: 'Security',
|
||||
}),
|
||||
order: 1100,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
alerting: SECURITY_RULE_TYPES,
|
||||
privileges: {
|
||||
all: {
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
api: [
|
||||
APP_ID,
|
||||
'lists-all',
|
||||
'lists-read',
|
||||
'lists-summary',
|
||||
'rac',
|
||||
'cloud-security-posture-all',
|
||||
'cloud-security-posture-read',
|
||||
],
|
||||
savedObject: {
|
||||
all: [
|
||||
'alert',
|
||||
'exception-list',
|
||||
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
...savedObjectTypes,
|
||||
CLOUD_POSTURE_SAVED_OBJECT_RULE_TYPE,
|
||||
],
|
||||
read: [],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
alert: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
ui: ['show', 'crud'],
|
||||
},
|
||||
read: {
|
||||
app: [APP_ID, CLOUD_POSTURE_APP_ID, 'kibana'],
|
||||
catalogue: [APP_ID],
|
||||
api: [APP_ID, 'lists-read', 'rac', 'cloud-security-posture-read'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [
|
||||
'exception-list',
|
||||
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
...savedObjectTypes,
|
||||
CLOUD_POSTURE_SAVED_OBJECT_RULE_TYPE,
|
||||
],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
read: SECURITY_RULE_TYPES,
|
||||
},
|
||||
alert: {
|
||||
all: SECURITY_RULE_TYPES,
|
||||
},
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
ui: ['show'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the list of Security SubFeature IDs that should be loaded and available in
|
||||
* kibana regardless of PLI or License level.
|
||||
* @param _
|
||||
*/
|
||||
export const getSecurityBaseKibanaSubFeatureIds = (
|
||||
_: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use
|
||||
): SecuritySubFeatureId[] => [SecuritySubFeatureId.hostIsolation];
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const getSecurityAppFeaturesConfig = (
|
||||
_: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use
|
||||
): AppFeaturesSecurityConfig => {
|
||||
return {
|
||||
[AppFeatureSecurityKey.advancedInsights]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`${APP_ID}-entity-analytics`],
|
||||
},
|
||||
read: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`${APP_ID}-entity-analytics`],
|
||||
},
|
||||
},
|
||||
},
|
||||
[AppFeatureSecurityKey.investigationGuide]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['investigation-guide'],
|
||||
},
|
||||
read: {
|
||||
ui: ['investigation-guide'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.threatIntelligence]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['threat-intelligence'],
|
||||
api: [`${APP_ID}-threat-intelligence`],
|
||||
},
|
||||
read: {
|
||||
ui: ['threat-intelligence'],
|
||||
api: [`${APP_ID}-threat-intelligence`],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointHostManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.endpointList],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointPolicyManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.policyManagement],
|
||||
},
|
||||
|
||||
// Adds no additional kibana feature controls
|
||||
[AppFeatureSecurityKey.endpointPolicyProtections]: {},
|
||||
|
||||
[AppFeatureSecurityKey.endpointArtifactManagement]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.trustedApplications,
|
||||
SecuritySubFeatureId.blocklist,
|
||||
SecuritySubFeatureId.eventFilters,
|
||||
],
|
||||
subFeaturesPrivileges: [
|
||||
{
|
||||
id: 'host_isolation_exceptions_all',
|
||||
api: [
|
||||
`${APP_ID}-accessHostIsolationExceptions`,
|
||||
`${APP_ID}-writeHostIsolationExceptions`,
|
||||
],
|
||||
ui: ['accessHostIsolationExceptions', 'writeHostIsolationExceptions'],
|
||||
},
|
||||
{
|
||||
id: 'host_isolation_exceptions_read',
|
||||
api: [`${APP_ID}-accessHostIsolationExceptions`],
|
||||
ui: ['accessHostIsolationExceptions'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointResponseActions]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.hostIsolationExceptions,
|
||||
|
||||
SecuritySubFeatureId.responseActionsHistory,
|
||||
SecuritySubFeatureId.processOperations,
|
||||
SecuritySubFeatureId.fileOperations,
|
||||
SecuritySubFeatureId.executeAction,
|
||||
],
|
||||
subFeaturesPrivileges: [
|
||||
// Adds the privilege to Isolate hosts to the already loaded `host_isolation_all`
|
||||
// sub-feature (always loaded), which included the `release` privilege already
|
||||
{
|
||||
id: 'host_isolation_all',
|
||||
api: [`${APP_ID}-writeHostIsolation`],
|
||||
ui: ['writeHostIsolation'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.osqueryAutomatedResponseActions]: {},
|
||||
};
|
||||
};
|
|
@ -1,39 +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 type { KibanaFeatureConfig, SubFeaturePrivilegeConfig } from '@kbn/features-plugin/common';
|
||||
import type { AppFeatureKey } from '../../../common';
|
||||
import type {
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureAssistantKey,
|
||||
} from '../../../common/types/app_features';
|
||||
import type { RecursivePartial } from '../../../common/utility_types';
|
||||
|
||||
export type BaseKibanaFeatureConfig = Omit<KibanaFeatureConfig, 'subFeatures'>;
|
||||
export type SubFeaturesPrivileges = RecursivePartial<SubFeaturePrivilegeConfig>;
|
||||
export type AppFeatureKibanaConfig<T extends string = string> =
|
||||
RecursivePartial<BaseKibanaFeatureConfig> & {
|
||||
subFeatureIds?: T[];
|
||||
subFeaturesPrivileges?: SubFeaturesPrivileges[];
|
||||
};
|
||||
export type AppFeaturesConfig<T extends string = string> = Record<
|
||||
AppFeatureKey,
|
||||
AppFeatureKibanaConfig<T>
|
||||
>;
|
||||
export type AppFeaturesSecurityConfig<T extends string = string> = Record<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<T>
|
||||
>;
|
||||
export type AppFeaturesCasesConfig<T extends string = string> = Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<T>
|
||||
>;
|
||||
export type AppFeaturesAssistantConfig<T extends string = string> = Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<T>
|
||||
>;
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PluginSetupContract } from '@kbn/features-plugin/server';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { AppFeatures } from './app_features';
|
||||
import type {
|
||||
AppFeatureKeyType,
|
||||
AppFeaturesConfig,
|
||||
AppSubFeaturesMap,
|
||||
BaseKibanaFeatureConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
|
||||
const category = {
|
||||
id: 'security',
|
||||
label: 'Security app category',
|
||||
};
|
||||
|
||||
const baseKibanaFeature: BaseKibanaFeatureConfig = {
|
||||
id: 'FEATURE_ID',
|
||||
name: 'Base Feature',
|
||||
order: 1100,
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
privileges: {
|
||||
all: {
|
||||
api: ['api-read', 'api-write'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['write', 'read'],
|
||||
},
|
||||
read: {
|
||||
api: ['api-read'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['read'],
|
||||
},
|
||||
},
|
||||
category,
|
||||
};
|
||||
|
||||
const privileges = {
|
||||
privileges: {
|
||||
all: {
|
||||
api: ['api-read', 'api-write', 'test-capability'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['write', 'read', 'test-capability'],
|
||||
},
|
||||
read: {
|
||||
api: ['api-read', 'test-capability'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['read', 'test-capability'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const SECURITY_APP_FEATURE_CONFIG: AppFeaturesConfig<string> = new Map();
|
||||
SECURITY_APP_FEATURE_CONFIG.set('test-base-feature' as AppFeatureKeyType, {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-capability'],
|
||||
api: ['test-capability'],
|
||||
},
|
||||
read: {
|
||||
ui: ['test-capability'],
|
||||
api: ['test-capability'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CASES_BASE_CONFIG = {
|
||||
privileges: {
|
||||
all: {
|
||||
api: ['api-read', 'api-write', 'test-cases-capability'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['write', 'read', 'test-cases-capability'],
|
||||
},
|
||||
read: {
|
||||
api: ['api-read', 'test-cases-capability'],
|
||||
app: ['FEATURE_ID', 'kibana'],
|
||||
catalogue: ['APP_ID'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['read', 'test-cases-capability'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const CASES_APP_FEATURE_CONFIG: AppFeaturesConfig<string> = new Map();
|
||||
CASES_APP_FEATURE_CONFIG.set('test-cases-feature' as AppFeatureKeyType, {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-cases-capability'],
|
||||
api: ['test-cases-capability'],
|
||||
},
|
||||
read: {
|
||||
ui: ['test-cases-capability'],
|
||||
api: ['test-cases-capability'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const securityKibanaSubFeatures = {
|
||||
securitySubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
};
|
||||
|
||||
const securityCasesKibanaSubFeatures = {
|
||||
casesSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
};
|
||||
|
||||
describe('AppFeatures', () => {
|
||||
it('should register enabled kibana features', () => {
|
||||
const featuresSetup = {
|
||||
registerKibanaFeature: jest.fn(),
|
||||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
securityKibanaSubFeatures.securitySubFeaturesMap as unknown as AppSubFeaturesMap<string>,
|
||||
baseKibanaFeature,
|
||||
['subFeature1']
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(SECURITY_APP_FEATURE_CONFIG);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...baseKibanaFeature,
|
||||
...SECURITY_APP_FEATURE_CONFIG.get('test-base-feature' as AppFeatureKeyType),
|
||||
...privileges,
|
||||
subFeatures: [{ baz: 'baz' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should register enabled cases features', () => {
|
||||
const featuresSetup = {
|
||||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
securityCasesKibanaSubFeatures.casesSubFeaturesMap as unknown as AppSubFeaturesMap<string>,
|
||||
baseKibanaFeature,
|
||||
['subFeature1']
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(CASES_APP_FEATURE_CONFIG);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...baseKibanaFeature,
|
||||
...CASES_APP_FEATURE_CONFIG.get('test-cases-feature' as AppFeatureKeyType),
|
||||
subFeatures: [{ baz: 'baz' }],
|
||||
...CASES_BASE_CONFIG,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import type {
|
||||
AppFeatureKeyType,
|
||||
AppFeaturesConfig,
|
||||
AppSubFeaturesMap,
|
||||
BaseKibanaFeatureConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import { AppFeaturesConfigMerger } from './app_features_config_merger';
|
||||
|
||||
export class AppFeatures<T extends string = string, S extends string = string> {
|
||||
private featureConfigMerger: AppFeaturesConfigMerger;
|
||||
private appFeatures?: Set<AppFeatureKeyType>;
|
||||
private featuresSetup?: FeaturesPluginSetup;
|
||||
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
subFeaturesMap: AppSubFeaturesMap<S>,
|
||||
private readonly baseKibanaFeature: BaseKibanaFeatureConfig,
|
||||
private readonly baseKibanaSubFeatureIds: T[]
|
||||
) {
|
||||
this.featureConfigMerger = new AppFeaturesConfigMerger(this.logger, subFeaturesMap);
|
||||
}
|
||||
|
||||
public init(featuresSetup: FeaturesPluginSetup) {
|
||||
this.featuresSetup = featuresSetup;
|
||||
}
|
||||
|
||||
public setConfig(config: AppFeaturesConfig<S>) {
|
||||
if (this.appFeatures) {
|
||||
throw new Error('AppFeatures has already been registered');
|
||||
}
|
||||
this.registerEnabledKibanaFeatures(config);
|
||||
}
|
||||
|
||||
private registerEnabledKibanaFeatures(appFeatureConfig: AppFeaturesConfig) {
|
||||
if (this.featuresSetup == null) {
|
||||
throw new Error(
|
||||
'Cannot sync kibana features as featuresSetup is not present. Did you call init?'
|
||||
);
|
||||
}
|
||||
|
||||
const completeAppFeatureConfig = this.featureConfigMerger.mergeAppFeatureConfigs(
|
||||
this.baseKibanaFeature,
|
||||
this.baseKibanaSubFeatureIds,
|
||||
Array.from(appFeatureConfig.values())
|
||||
);
|
||||
|
||||
this.logger.debug(JSON.stringify(completeAppFeatureConfig));
|
||||
|
||||
this.featuresSetup.registerKibanaFeature(completeAppFeatureConfig);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { AppFeaturesConfigMerger } from './app_features_config_merger';
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { AppFeatureKibanaConfig } from './types';
|
||||
import type { AppFeatureKibanaConfig } from '@kbn/security-solution-features';
|
||||
import type { KibanaFeatureConfig, SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
|
||||
const category = {
|
|
@ -5,14 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { cloneDeep, mergeWith, isArray, uniq } from 'lodash';
|
||||
import { cloneDeep, isArray, mergeWith, uniq } from 'lodash';
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { KibanaFeatureConfig, SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
BaseKibanaFeatureConfig,
|
||||
SubFeaturesPrivileges,
|
||||
} from './types';
|
||||
} from '@kbn/security-solution-features';
|
||||
|
||||
export class AppFeaturesConfigMerger<T extends string = string> {
|
||||
constructor(
|
||||
|
@ -23,6 +23,7 @@ export class AppFeaturesConfigMerger<T extends string = string> {
|
|||
/**
|
||||
* Merges `appFeaturesConfigs` into `kibanaFeatureConfig`.
|
||||
* @param kibanaFeatureConfig the KibanaFeatureConfig to merge into
|
||||
* @param kibanaSubFeatureIds
|
||||
* @param appFeaturesConfigs the AppFeatureKibanaConfig to merge
|
||||
* @returns mergedKibanaFeatureConfig the merged KibanaFeatureConfig
|
||||
* */
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import {
|
||||
getAssistantFeature,
|
||||
getCasesFeature,
|
||||
getSecurityFeature,
|
||||
} from '@kbn/security-solution-features/app_features';
|
||||
import type { ExperimentalFeatures } from '../../../common';
|
||||
import { AppFeatures } from './app_features';
|
||||
import type { AppFeaturesConfigurator } from './types';
|
||||
import { securityDefaultSavedObjects } from './security_saved_objects';
|
||||
import { casesApiTags, casesUiCapabilities } from './cases_privileges';
|
||||
|
||||
export class AppFeaturesService {
|
||||
private securityAppFeatures: AppFeatures;
|
||||
private casesAppFeatures: AppFeatures;
|
||||
private securityAssistantAppFeatures: AppFeatures;
|
||||
private appFeatures?: Set<AppFeatureKeyType>;
|
||||
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly experimentalFeatures: ExperimentalFeatures
|
||||
) {
|
||||
const securityFeature = getSecurityFeature({
|
||||
savedObjects: securityDefaultSavedObjects,
|
||||
experimentalFeatures: this.experimentalFeatures,
|
||||
});
|
||||
this.securityAppFeatures = new AppFeatures(
|
||||
this.logger,
|
||||
securityFeature.subFeaturesMap,
|
||||
securityFeature.baseKibanaFeature,
|
||||
securityFeature.baseKibanaSubFeatureIds
|
||||
);
|
||||
|
||||
const casesFeature = getCasesFeature({
|
||||
uiCapabilities: casesUiCapabilities,
|
||||
apiTags: casesApiTags,
|
||||
savedObjects: { files: filesSavedObjectTypes },
|
||||
});
|
||||
this.casesAppFeatures = new AppFeatures(
|
||||
this.logger,
|
||||
casesFeature.subFeaturesMap,
|
||||
casesFeature.baseKibanaFeature,
|
||||
casesFeature.baseKibanaSubFeatureIds
|
||||
);
|
||||
|
||||
const assistantFeature = getAssistantFeature();
|
||||
this.securityAssistantAppFeatures = new AppFeatures(
|
||||
this.logger,
|
||||
assistantFeature.subFeaturesMap,
|
||||
assistantFeature.baseKibanaFeature,
|
||||
assistantFeature.baseKibanaSubFeatureIds
|
||||
);
|
||||
}
|
||||
|
||||
public init(featuresSetup: FeaturesPluginSetup) {
|
||||
this.securityAppFeatures.init(featuresSetup);
|
||||
this.casesAppFeatures.init(featuresSetup);
|
||||
this.securityAssistantAppFeatures.init(featuresSetup);
|
||||
}
|
||||
|
||||
public setAppFeaturesConfigurator(configurator: AppFeaturesConfigurator) {
|
||||
const securityAppFeaturesConfig = configurator.security(this.experimentalFeatures);
|
||||
this.securityAppFeatures.setConfig(securityAppFeaturesConfig);
|
||||
|
||||
const casesAppFeaturesConfig = configurator.cases();
|
||||
this.casesAppFeatures.setConfig(casesAppFeaturesConfig);
|
||||
|
||||
const securityAssistantAppFeaturesConfig = configurator.securityAssistant();
|
||||
this.securityAssistantAppFeatures.setConfig(securityAssistantAppFeaturesConfig);
|
||||
|
||||
this.appFeatures = new Set<AppFeatureKeyType>(
|
||||
Object.freeze([
|
||||
...securityAppFeaturesConfig.keys(),
|
||||
...casesAppFeaturesConfig.keys(),
|
||||
...securityAssistantAppFeaturesConfig.keys(),
|
||||
]) as readonly AppFeatureKeyType[]
|
||||
);
|
||||
}
|
||||
|
||||
public isEnabled(appFeatureKey: AppFeatureKeyType): boolean {
|
||||
if (!this.appFeatures) {
|
||||
throw new Error('AppFeatures has not yet been configured');
|
||||
}
|
||||
return this.appFeatures.has(appFeatureKey);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 {
|
||||
createUICapabilities as createCasesUICapabilities,
|
||||
getApiTags as getCasesApiTags,
|
||||
} from '@kbn/cases-plugin/common';
|
||||
import {
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
|
||||
import { APP_ID } from '../../../common/constants';
|
||||
|
||||
const originalCasesUiCapabilities = createCasesUICapabilities();
|
||||
const originalCasesApiTags = getCasesApiTags(APP_ID);
|
||||
|
||||
export const casesUiCapabilities = {
|
||||
...originalCasesUiCapabilities,
|
||||
all: originalCasesUiCapabilities.all.filter(
|
||||
(capability) => capability !== CASES_CONNECTORS_CAPABILITY
|
||||
),
|
||||
read: originalCasesUiCapabilities.read.filter(
|
||||
(capability) => capability !== CASES_CONNECTORS_CAPABILITY
|
||||
),
|
||||
};
|
||||
|
||||
export const casesApiTags = {
|
||||
...originalCasesApiTags,
|
||||
all: originalCasesApiTags.all.filter(
|
||||
(capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG
|
||||
),
|
||||
read: originalCasesApiTags.read.filter(
|
||||
(capability) => capability !== GET_CONNECTORS_CONFIGURE_API_TAG
|
||||
),
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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 { AppFeaturesService } from './app_features_service';
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { allowedExperimentalValues, type ExperimentalFeatures } from '../../../common';
|
||||
import { AppFeaturesService } from './app_features_service';
|
||||
|
||||
const SECURITY_BASE_CONFIG = {
|
||||
foo: 'foo',
|
||||
};
|
||||
|
||||
const CASES_BASE_CONFIG = {
|
||||
bar: 'bar',
|
||||
};
|
||||
|
||||
const ASSISTANT_BASE_CONFIG = {
|
||||
bar: 'bar',
|
||||
};
|
||||
|
||||
jest.mock('@kbn/security-solution-features/app_features', () => ({
|
||||
getSecurityFeature: jest.fn(() => ({
|
||||
baseKibanaFeature: SECURITY_BASE_CONFIG,
|
||||
baseKibanaSubFeatureIds: ['subFeature1'],
|
||||
subFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
})),
|
||||
getCasesFeature: jest.fn(() => ({
|
||||
baseKibanaFeature: CASES_BASE_CONFIG,
|
||||
baseKibanaSubFeatureIds: ['subFeature1'],
|
||||
subFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
|
||||
})),
|
||||
getAssistantFeature: jest.fn(() => ({
|
||||
baseKibanaFeature: ASSISTANT_BASE_CONFIG,
|
||||
baseKibanaSubFeatureIds: [],
|
||||
subFeaturesMap: new Map([]),
|
||||
})),
|
||||
}));
|
||||
|
||||
export const createAppFeaturesServiceMock = (
|
||||
/** What features keys should be enabled. Default is all */
|
||||
enabledFeatureKeys: AppFeatureKeys = [...ALL_APP_FEATURE_KEYS],
|
||||
experimentalFeatures: ExperimentalFeatures = { ...allowedExperimentalValues },
|
||||
featuresPluginSetupContract: FeaturesPluginSetup = featuresPluginMock.createSetup(),
|
||||
logger: Logger = loggingSystemMock.create().get('appFeatureMock')
|
||||
) => {
|
||||
const appFeaturesService = new AppFeaturesService(logger, experimentalFeatures);
|
||||
|
||||
appFeaturesService.init(featuresPluginSetupContract);
|
||||
|
||||
if (enabledFeatureKeys) {
|
||||
appFeaturesService.setAppFeaturesConfigurator({
|
||||
security: jest.fn().mockReturnValue(
|
||||
new Map(
|
||||
enabledFeatureKeys.map((key) => [
|
||||
key,
|
||||
{
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
read: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
)
|
||||
),
|
||||
cases: jest.fn().mockReturnValue(
|
||||
new Map(
|
||||
enabledFeatureKeys.map((key) => [
|
||||
key,
|
||||
{
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
read: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
)
|
||||
),
|
||||
securityAssistant: jest.fn().mockReturnValue(
|
||||
new Map(
|
||||
enabledFeatureKeys.map((key) => [
|
||||
key,
|
||||
{
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
read: {
|
||||
ui: ['entity-analytics'],
|
||||
api: [`test-entity-analytics`],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return appFeaturesService;
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common';
|
||||
import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC } from '@kbn/securitysolution-list-constants';
|
||||
import { savedObjectTypes } from '../../saved_objects';
|
||||
|
||||
// Same as the saved-object type for rules defined by Cloud Security Posture
|
||||
const CLOUD_POSTURE_SAVED_OBJECT_RULE_TYPE = 'csp_rule';
|
||||
|
||||
export const securityDefaultSavedObjects = [
|
||||
'exception-list',
|
||||
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
|
||||
DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
...savedObjectTypes,
|
||||
CLOUD_POSTURE_SAVED_OBJECT_RULE_TYPE,
|
||||
];
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeaturesConfig } from '@kbn/security-solution-features';
|
||||
import type {
|
||||
SecuritySubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import type { ExperimentalFeatures } from '../../../common';
|
||||
|
||||
export interface AppFeaturesConfigurator {
|
||||
security: (experimentalFlags: ExperimentalFeatures) => AppFeaturesConfig<SecuritySubFeatureId>;
|
||||
cases: () => AppFeaturesConfig<CasesSubFeatureId>;
|
||||
securityAssistant: () => AppFeaturesConfig<AssistantSubFeatureId>;
|
||||
}
|
|
@ -20,7 +20,7 @@ import type { ILicense } from '@kbn/licensing-plugin/server';
|
|||
import { turnOffPolicyProtectionsIfNotSupported } from './endpoint/migrations/turn_off_policy_protections';
|
||||
import { endpointSearchStrategyProvider } from './search_strategy/endpoint';
|
||||
import { getScheduleNotificationResponseActionsService } from './lib/detection_engine/rule_response_actions/schedule_notification_response_actions';
|
||||
import { siemGuideId, siemGuideConfig } from '../common/guided_onboarding/siem_guide_config';
|
||||
import { siemGuideConfig, siemGuideId } from '../common/guided_onboarding/siem_guide_config';
|
||||
import {
|
||||
createEqlAlertType,
|
||||
createIndicatorMatchAlertType,
|
||||
|
@ -38,7 +38,7 @@ import { AppClientFactory } from './client';
|
|||
import type { ConfigType } from './config';
|
||||
import { createConfig } from './config';
|
||||
import { initUiSettings } from './ui_settings';
|
||||
import { APP_ID, SERVER_APP_ID, DEFAULT_ALERTS_INDEX } from '../common/constants';
|
||||
import { APP_ID, DEFAULT_ALERTS_INDEX, SERVER_APP_ID } from '../common/constants';
|
||||
import { registerEndpointRoutes } from './endpoint/routes/metadata';
|
||||
import { registerPolicyRoutes } from './endpoint/routes/policy';
|
||||
import { registerActionRoutes } from './endpoint/routes/actions';
|
||||
|
@ -60,13 +60,13 @@ import type { IRuleMonitoringService } from './lib/detection_engine/rule_monitor
|
|||
import { createRuleMonitoringService } from './lib/detection_engine/rule_monitoring';
|
||||
import { EndpointMetadataService } from './endpoint/services/metadata';
|
||||
import type {
|
||||
CreateRuleOptions,
|
||||
CreateQueryRuleAdditionalOptions,
|
||||
CreateRuleOptions,
|
||||
} from './lib/detection_engine/rule_types/types';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import {
|
||||
legacyRulesNotificationAlertType,
|
||||
legacyIsNotificationAlertExecutor,
|
||||
legacyRulesNotificationAlertType,
|
||||
} from './lib/detection_engine/rule_actions_legacy';
|
||||
import {
|
||||
createSecurityRuleTypeWrapper,
|
||||
|
@ -77,13 +77,13 @@ import { RequestContextFactory } from './request_context_factory';
|
|||
|
||||
import type {
|
||||
ISecuritySolutionPlugin,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
SecuritySolutionPluginStartDependencies,
|
||||
PluginInitializerContext,
|
||||
SecuritySolutionPluginCoreSetupDependencies,
|
||||
SecuritySolutionPluginCoreStartDependencies,
|
||||
SecuritySolutionPluginSetup,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
SecuritySolutionPluginStart,
|
||||
PluginInitializerContext,
|
||||
SecuritySolutionPluginStartDependencies,
|
||||
} from './plugin_contract';
|
||||
import { EndpointFleetServicesFactory } from './endpoint/services/fleet';
|
||||
import { featureUsageService } from './endpoint/services/feature_usage';
|
||||
|
@ -96,7 +96,7 @@ import {
|
|||
ENDPOINT_SEARCH_STRATEGY,
|
||||
} from '../common/endpoint/constants';
|
||||
|
||||
import { AppFeatures } from './lib/app_features';
|
||||
import { AppFeaturesService } from './lib/app_features_service/app_features_service';
|
||||
import { registerRiskScoringTask } from './lib/risk_engine/tasks/risk_scoring_task';
|
||||
|
||||
export type { SetupPlugins, StartPlugins, PluginSetup, PluginStart } from './plugin_contract';
|
||||
|
@ -106,7 +106,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
private readonly config: ConfigType;
|
||||
private readonly logger: Logger;
|
||||
private readonly appClientFactory: AppClientFactory;
|
||||
private readonly appFeatures: AppFeatures;
|
||||
private readonly appFeaturesService: AppFeaturesService;
|
||||
|
||||
private readonly ruleMonitoringService: IRuleMonitoringService;
|
||||
private readonly endpointAppContextService = new EndpointAppContextService();
|
||||
|
@ -129,7 +129,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
this.config = serverConfig;
|
||||
this.logger = context.logger.get();
|
||||
this.appClientFactory = new AppClientFactory();
|
||||
this.appFeatures = new AppFeatures(this.logger, this.config.experimentalFeatures);
|
||||
this.appFeaturesService = new AppFeaturesService(this.logger, this.config.experimentalFeatures);
|
||||
|
||||
this.ruleMonitoringService = createRuleMonitoringService(this.config, this.logger);
|
||||
this.telemetryEventsSender = new TelemetryEventsSender(this.logger);
|
||||
|
@ -153,12 +153,12 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
): SecuritySolutionPluginSetup {
|
||||
this.logger.debug('plugin setup');
|
||||
|
||||
const { appClientFactory, appFeatures, pluginContext, config, logger } = this;
|
||||
const { appClientFactory, appFeaturesService, pluginContext, config, logger } = this;
|
||||
const experimentalFeatures = config.experimentalFeatures;
|
||||
|
||||
initSavedObjects(core.savedObjects);
|
||||
initUiSettings(core.uiSettings, experimentalFeatures);
|
||||
appFeatures.init(plugins.features);
|
||||
appFeaturesService.init(plugins.features);
|
||||
|
||||
this.ruleMonitoringService.setup(core, plugins);
|
||||
|
||||
|
@ -403,7 +403,8 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
plugins.guidedOnboarding.registerGuideConfig(siemGuideId, siemGuideConfig);
|
||||
|
||||
return {
|
||||
setAppFeatures: this.appFeatures.set.bind(this.appFeatures),
|
||||
setAppFeaturesConfigurator:
|
||||
appFeaturesService.setAppFeaturesConfigurator.bind(appFeaturesService),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -411,7 +412,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
core: SecuritySolutionPluginCoreStartDependencies,
|
||||
plugins: SecuritySolutionPluginStartDependencies
|
||||
): SecuritySolutionPluginStart {
|
||||
const { config, logger } = this;
|
||||
const { config, logger, appFeaturesService } = this;
|
||||
|
||||
this.ruleMonitoringService.start(core, plugins);
|
||||
|
||||
|
@ -467,7 +468,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
experimentalFeatures: config.experimentalFeatures,
|
||||
packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
appFeatures: this.appFeatures,
|
||||
appFeaturesService,
|
||||
});
|
||||
|
||||
// Migrate artifacts to fleet and then start the manifest task after that is done
|
||||
|
@ -484,7 +485,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
turnOffPolicyProtectionsIfNotSupported(
|
||||
core.elasticsearch.client.asInternalUser,
|
||||
endpointFleetServicesFactory.asInternalUser(),
|
||||
this.appFeatures,
|
||||
appFeaturesService,
|
||||
logger
|
||||
);
|
||||
});
|
||||
|
@ -513,7 +514,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
endpointFleetServicesFactory,
|
||||
security: plugins.security,
|
||||
alerting: plugins.alerting,
|
||||
config: this.config,
|
||||
config,
|
||||
cases: plugins.cases,
|
||||
logger,
|
||||
manifestManager,
|
||||
|
@ -531,7 +532,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
),
|
||||
createFleetActionsClient,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
appFeatures: this.appFeatures,
|
||||
appFeaturesService,
|
||||
});
|
||||
|
||||
this.telemetryReceiver.start(
|
||||
|
|
|
@ -41,7 +41,7 @@ import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/
|
|||
import type { SharePluginStart } from '@kbn/share-plugin/server';
|
||||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
import type { PluginSetup as UnifiedSearchServerPluginSetup } from '@kbn/unified-search-plugin/server';
|
||||
import type { AppFeatures } from './lib/app_features/app_features';
|
||||
import type { AppFeaturesService } from './lib/app_features_service/app_features_service';
|
||||
|
||||
export interface SecuritySolutionPluginSetupDependencies {
|
||||
alerting: AlertingPluginSetup;
|
||||
|
@ -84,9 +84,9 @@ export interface SecuritySolutionPluginStartDependencies {
|
|||
|
||||
export interface SecuritySolutionPluginSetup {
|
||||
/**
|
||||
* Sets the app features that are available to the Security Solution
|
||||
* Sets the configurations for app features that are available to the Security Solution
|
||||
*/
|
||||
setAppFeatures: AppFeatures['set'];
|
||||
setAppFeaturesConfigurator: AppFeaturesService['setAppFeaturesConfigurator'];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
|
|
|
@ -171,6 +171,7 @@
|
|||
"@kbn/navigation-plugin",
|
||||
"@kbn/core-logging-server-mocks",
|
||||
"@kbn/core-lifecycle-browser",
|
||||
"@kbn/security-solution-features",
|
||||
"@kbn/handlebars"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
*/
|
||||
|
||||
import { SecurityPageName } from '@kbn/security-solution-plugin/common';
|
||||
import type { UpsellingService } from '@kbn/security-solution-plugin/public';
|
||||
import type {
|
||||
MessageUpsellings,
|
||||
PageUpsellings,
|
||||
SectionUpsellings,
|
||||
UpsellingMessageId,
|
||||
UpsellingSectionId,
|
||||
} from '@kbn/security-solution-upselling/service/types';
|
||||
UpsellingService,
|
||||
} from '@kbn/security-solution-upselling/service';
|
||||
import type { ILicense, LicenseType } from '@kbn/licensing-plugin/public';
|
||||
import React, { lazy } from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from '@kbn/security-solution-upselling/messages';
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesCasesConfig,
|
||||
AppFeatureKeys,
|
||||
} from '@kbn/security-solution-features';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
getCasesDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
|
||||
import {
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
|
||||
export const getCasesAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesCasesConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(casesAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Cases app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Cases feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Cases subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Cases subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const casesAppFeaturesConfig: Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
> = {
|
||||
...getCasesDefaultAppFeaturesConfig({
|
||||
apiTags: { connectors: GET_CONNECTORS_CONFIGURE_API_TAG },
|
||||
uiCapabilities: { connectors: CASES_CONNECTORS_CAPABILITY },
|
||||
}),
|
||||
// ess-specific app features configs here
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { AppFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/app_features_service/types';
|
||||
import { getCasesAppFeaturesConfigurator } from './cases_app_features_config';
|
||||
import { getSecurityAppFeaturesConfigurator } from './security_app_features_config';
|
||||
import { getSecurityAssistantAppFeaturesConfigurator } from './security_assistant_app_features_config';
|
||||
|
||||
export const getProductAppFeaturesConfigurator = (
|
||||
enabledAppFeatureKeys: AppFeatureKeys
|
||||
): AppFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
cases: getCasesAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ExperimentalFeatures } from '@kbn/security-solution-plugin/common';
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesSecurityConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
AppFeatureSecurityKey,
|
||||
type SecuritySubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
securityDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import {
|
||||
AppFeaturesPrivilegeId,
|
||||
AppFeaturesPrivileges,
|
||||
} from '@kbn/security-solution-features/privileges';
|
||||
|
||||
export const getSecurityAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) =>
|
||||
(
|
||||
_: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use
|
||||
): AppFeaturesSecurityConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(securityAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const securityAppFeaturesConfig: Record<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
> = {
|
||||
...securityDefaultAppFeaturesConfig,
|
||||
[AppFeatureSecurityKey.endpointExceptions]: {
|
||||
privileges: AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions],
|
||||
},
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesAssistantConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
assistantDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
|
||||
export const getSecurityAssistantAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesAssistantConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(assistantAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Assistant app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Assistant feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const assistantAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
...assistantDefaultAppFeaturesConfig,
|
||||
// ess-specific app features configs here
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-plugin/common';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
|
||||
// Just copying all feature keys for now.
|
||||
// We may need a different set of keys in the future if we create serverless-specific appFeatures
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { Plugin, CoreSetup } from '@kbn/core/server';
|
||||
import { getProductAppFeaturesConfigurator } from './app_features';
|
||||
import { DEFAULT_APP_FEATURES } from './constants';
|
||||
|
||||
import type {
|
||||
|
@ -25,7 +26,8 @@ export class SecuritySolutionEssPlugin
|
|||
>
|
||||
{
|
||||
public setup(_coreSetup: CoreSetup, pluginsSetup: SecuritySolutionEssPluginSetupDeps) {
|
||||
pluginsSetup.securitySolution.setAppFeatures(DEFAULT_APP_FEATURES);
|
||||
const appFeaturesConfigurator = getProductAppFeaturesConfigurator(DEFAULT_APP_FEATURES);
|
||||
pluginsSetup.securitySolution.setAppFeaturesConfigurator(appFeaturesConfigurator);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
"@kbn/i18n",
|
||||
"@kbn/cloud-experiments-plugin",
|
||||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/security-solution-features",
|
||||
"@kbn/cases-plugin",
|
||||
"@kbn/security-solution-navigation",
|
||||
"@kbn/licensing-plugin",
|
||||
"@kbn/security-solution-upselling",
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AppFeatureKey, type AppFeatureKeys } from '@kbn/security-solution-plugin/common';
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { SecurityProductLine, SecurityProductTier } from '../config';
|
||||
|
||||
type PliAppFeatures = Readonly<
|
||||
|
@ -24,7 +25,11 @@ export const PLI_APP_FEATURES: PliAppFeatures = {
|
|||
],
|
||||
},
|
||||
endpoint: {
|
||||
essentials: [AppFeatureKey.endpointPolicyProtections, AppFeatureKey.endpointArtifactManagement],
|
||||
essentials: [
|
||||
AppFeatureKey.endpointPolicyProtections,
|
||||
AppFeatureKey.endpointArtifactManagement,
|
||||
AppFeatureKey.endpointExceptions,
|
||||
],
|
||||
complete: [
|
||||
AppFeatureKey.endpointResponseActions,
|
||||
AppFeatureKey.osqueryAutomatedResponseActions,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-plugin/common';
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features/src/types';
|
||||
import type { SecurityProductTypes } from '../config';
|
||||
import { ProductTier } from '../product';
|
||||
import { PLI_APP_FEATURES } from './pli_config';
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { UpsellingService } from '@kbn/security-solution-plugin/public';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-plugin/common';
|
||||
import {
|
||||
registerUpsellings,
|
||||
upsellingMessages,
|
||||
|
@ -15,6 +13,9 @@ import {
|
|||
} from './register_upsellings';
|
||||
import { ProductLine, ProductTier } from '../../common/product';
|
||||
import type { SecurityProductTypes } from '../../common/config';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { mockServices } from '../common/services/__mocks__/services.mock';
|
||||
|
||||
const mockGetProductAppFeatures = jest.fn();
|
||||
jest.mock('../../common/pli/pli_features', () => ({
|
||||
|
@ -40,7 +41,7 @@ describe('registerUpsellings', () => {
|
|||
setMessages,
|
||||
} as unknown as UpsellingService;
|
||||
|
||||
registerUpsellings(upselling, allProductTypes);
|
||||
registerUpsellings(upselling, allProductTypes, mockServices);
|
||||
|
||||
expect(setPages).toHaveBeenCalledTimes(1);
|
||||
expect(setPages).toHaveBeenCalledWith({});
|
||||
|
@ -65,7 +66,7 @@ describe('registerUpsellings', () => {
|
|||
setMessages,
|
||||
} as unknown as UpsellingService;
|
||||
|
||||
registerUpsellings(upselling, allProductTypes);
|
||||
registerUpsellings(upselling, allProductTypes, mockServices);
|
||||
|
||||
const expectedPagesObject = Object.fromEntries(
|
||||
upsellingPages.map(({ pageName }) => [pageName, expect.anything()])
|
||||
|
|
|
@ -4,38 +4,38 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { SecurityPageName, AppFeatureKey } from '@kbn/security-solution-plugin/common';
|
||||
import type {
|
||||
UpsellingService,
|
||||
PageUpsellings,
|
||||
SectionUpsellings,
|
||||
UpsellingSectionId,
|
||||
} from '@kbn/security-solution-plugin/public';
|
||||
import { SecurityPageName } from '@kbn/security-solution-plugin/common';
|
||||
import type {
|
||||
MessageUpsellings,
|
||||
PageUpsellings,
|
||||
SectionUpsellings,
|
||||
UpsellingMessageId,
|
||||
UpsellingSectionId,
|
||||
} from '@kbn/security-solution-upselling/service/types';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import React from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from '@kbn/security-solution-upselling/messages';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { EndpointPolicyProtectionsLazy } from './sections/endpoint_management';
|
||||
import type { SecurityProductTypes } from '../../common/config';
|
||||
import { getProductAppFeatures } from '../../common/pli/pli_features';
|
||||
import {
|
||||
EntityAnalyticsUpsellingLazy,
|
||||
OsqueryResponseActionsUpsellingSectionLazy,
|
||||
ThreatIntelligencePaywallLazy,
|
||||
EntityAnalyticsUpsellingLazy,
|
||||
} from './lazy_upselling';
|
||||
import { getProductTypeByPLI } from './hooks/use_product_type_by_pli';
|
||||
import type { Services } from '../common/services';
|
||||
import { withServicesProvider } from '../common/services';
|
||||
|
||||
interface UpsellingsConfig {
|
||||
pli: AppFeatureKey;
|
||||
pli: AppFeatureKeyType;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
interface UpsellingsMessageConfig {
|
||||
pli: AppFeatureKey;
|
||||
pli: AppFeatureKeyType;
|
||||
message: string;
|
||||
id: UpsellingMessageId;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesCasesConfig,
|
||||
AppFeatureKeys,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
getCasesDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
|
||||
export const getCasesAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesCasesConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(casesAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Cases app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Cases feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Cases subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Cases subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const casesAppFeaturesConfig: Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
> = {
|
||||
...getCasesDefaultAppFeaturesConfig({
|
||||
apiTags: { connectors: GET_CONNECTORS_CONFIGURE_API_TAG },
|
||||
uiCapabilities: { connectors: CASES_CONNECTORS_CAPABILITY },
|
||||
}),
|
||||
// serverless-specific app features configs here
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { AppFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/app_features_service/types';
|
||||
import { getCasesAppFeaturesConfigurator } from './cases_app_features_config';
|
||||
import { getSecurityAppFeaturesConfigurator } from './security_app_features_config';
|
||||
import { getSecurityAssistantAppFeaturesConfigurator } from './security_assistant_app_features_config';
|
||||
|
||||
export const getProductAppFeaturesConfigurator = (
|
||||
enabledAppFeatureKeys: AppFeatureKeys
|
||||
): AppFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
cases: getCasesAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ExperimentalFeatures } from '@kbn/security-solution-plugin/common';
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesSecurityConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
securityDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import { AppFeatureSecurityKey, SecuritySubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
|
||||
export const getSecurityAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) =>
|
||||
(
|
||||
_: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use
|
||||
): AppFeaturesSecurityConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(securityAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const securityAppFeaturesConfig: Record<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
> = {
|
||||
...securityDefaultAppFeaturesConfig,
|
||||
[AppFeatureSecurityKey.endpointExceptions]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.endpointExceptions],
|
||||
},
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesAssistantConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
assistantDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
|
||||
export const getSecurityAssistantAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesAssistantConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(assistantAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Assistant app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
* - `privileges`: the privileges that will be added directly into the main Security Assistant feature.
|
||||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const assistantAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
...assistantDefaultAppFeaturesConfig,
|
||||
// serverless-specific app features configs here
|
||||
};
|
|
@ -24,6 +24,7 @@ import type {
|
|||
} from './types';
|
||||
import { SecurityUsageReportingTask } from './task_manager/usage_reporting_task';
|
||||
import { cloudSecurityMetringTaskProperties } from './cloud_security/cloud_security_metering_task_config';
|
||||
import { getProductAppFeaturesConfigurator } from './app_features';
|
||||
import { METERING_TASK as ENDPOINT_METERING_TASK } from './endpoint/constants/metering';
|
||||
import {
|
||||
endpointMeteringService,
|
||||
|
@ -57,7 +58,10 @@ export class SecuritySolutionServerlessPlugin
|
|||
if (shouldRegister) {
|
||||
const productTypesStr = JSON.stringify(this.config.productTypes, null, 2);
|
||||
this.logger.info(`Security Solution running with product types:\n${productTypesStr}`);
|
||||
pluginsSetup.securitySolution.setAppFeatures(getProductAppFeatures(this.config.productTypes));
|
||||
const appFeaturesConfigurator = getProductAppFeaturesConfigurator(
|
||||
getProductAppFeatures(this.config.productTypes)
|
||||
);
|
||||
pluginsSetup.securitySolution.setAppFeaturesConfigurator(appFeaturesConfigurator);
|
||||
}
|
||||
|
||||
pluginsSetup.ml.setFeaturesEnabled({ ad: true, dfa: true, nlp: false });
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
"public/**/*.tsx",
|
||||
"server/**/*.ts",
|
||||
"../../../typings/**/*"
|
||||
,
|
||||
"../../packages/security-solution/upselling/sections/generic_upselling_section.tsx"
|
||||
],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
|
@ -39,6 +37,8 @@
|
|||
"@kbn/task-manager-plugin",
|
||||
"@kbn/cloud-plugin",
|
||||
"@kbn/cloud-security-posture-plugin",
|
||||
"@kbn/security-solution-features",
|
||||
"@kbn/cases-plugin",
|
||||
"@kbn/fleet-plugin",
|
||||
"@kbn/core-elasticsearch-server",
|
||||
"@kbn/usage-collection-plugin"
|
||||
|
|
|
@ -33186,43 +33186,6 @@
|
|||
"xpack.securitySolution.expandedValue.showTopN.showTopValues": "Afficher les valeurs les plus élevées",
|
||||
"xpack.securitySolution.explore.landing.pageTitle": "Explorer",
|
||||
"xpack.securitySolution.featureCatalogueDescription": "Prévenez, collectez, détectez et traitez les menaces pour une protection unifiée dans toute votre infrastructure.",
|
||||
"xpack.securitySolution.featureRegistry.deleteSubFeatureDetails": "Supprimer les cas et les commentaires",
|
||||
"xpack.securitySolution.featureRegistry.deleteSubFeatureName": "Supprimer",
|
||||
"xpack.securitySolution.featureRegistry.linkSecuritySolutionCaseTitle": "Cas",
|
||||
"xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle": "Sécurité",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.blockList": "Liste noire",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.blockList.description": "Étendez la protection d'Elastic Defend contre les processus malveillants et protégez-vous des applications potentiellement nuisibles.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.blockList.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès à la liste noire.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.endpointList": "Liste de points de terminaison",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.endpointList.description": "Affiche tous les hôtes exécutant Elastic Defend et leurs détails d'intégration associés.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.endpointList.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès à la liste de points de terminaison.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.eventFilters": "Filtres d'événements",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.eventFilters.description": "Excluez les événements de point de terminaison dont vous n'avez pas besoin ou que vous ne souhaitez pas stocker dans Elasticsearch.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.eventFilters.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux filtres d'événements.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.executeOperations": "Exécuter les opérations",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.executeOperations.description": "Effectuez l'exécution de script sur le point de terminaison.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.executeOperations.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux opérations d'exécution.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.fileOperations": "Opérations de fichier",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.fileOperations.description": "Effectuez les actions de réponse liées aux fichiers dans la console de réponse.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.fileOperations.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux opérations de fichier.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolation": "Isolation de l'hôte",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolation.description": "Effectuez les actions de réponse \"isoler\" et \"libérer\".",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolation.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès à l'isolation de l'hôte.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions": "Exceptions d'isolation de l'hôte",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions.description": "Ajoutez des adresses IP spécifiques avec lesquelles les hôtes isolés sont toujours autorisés à communiquer, même lorsqu'ils sont isolés du reste du réseau.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.hostIsolationExceptions.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux exceptions d'isolation de l'hôte.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.policyManagement": "Gestion des politiques Elastic Defend",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.policyManagement.description": "Accédez à la politique d'intégration Elastic Defend pour configurer les protections, la collecte des événements et les fonctionnalités de politique avancées.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.policyManagement.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès à la gestion des politiques.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.processOperations": "Opérations de traitement",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.processOperations.description": "Effectuez les actions de réponse liées aux processus dans la console de réponse.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.processOperations.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux opérations de traitement.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory": "Historique des actions de réponse",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory.description": "Accédez à l'historique des actions de réponse effectuées sur les points de terminaison.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.responseActionsHistory.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès à l'historique des actions de réponse.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.trustedApplications": "Applications de confiance",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.trustedApplications.description": "Aide à atténuer les conflits avec d'autres logiciels, généralement d'autres applications d'antivirus ou de sécurité des points de terminaison.",
|
||||
"xpack.securitySolution.featureRegistry.subFeatures.trustedApplications.privilegesTooltip": "\"Tous les espaces\" est requis pour l'accès aux applications de confiance.",
|
||||
"xpack.securitySolution.fieldBrowser.actionsLabel": "Actions",
|
||||
"xpack.securitySolution.fieldBrowser.categoryLabel": "Catégorie",
|
||||
"xpack.securitySolution.fieldBrowser.createFieldButton": "Créer un champ",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue