[Ai4dSoc][Serverless] Hide Security feature sub-privileges in search_ai_lake tier (#217210)

## Summary

Hides security sub-privileges for ai4soc/search_ai_lake tier.
![Screenshot 2025-04-11 at 10 22
09](https://github.com/user-attachments/assets/6f3294bc-82de-404e-b9d3-22e717d54b65)

### Reasoning for changes added to `x-pack/packages/security`:

Currently, the feature description of Security feature is tied to the
fact that it has a list of sub-privileges. This is true on ESS and
`essentials/complete` serverless tiers.

With the introduction of the lower `search_ai_lake` tier, security
feature would not have any sub-privileges available and thus it does not
make sense to show that description.

The ideal way to handle this would be to load feature privileges config
settings at the plugin level
(security_solution/security_solution_serverless) and set `description`
to `null | undefined` based on the tier, as currently the feature
privileges settings live in [kibana_features file
(v2_features)](795094d8c6/x-pack/solutions/security/packages/features/src/security/v2_features/kibana_features.ts (L72))
(also another set in v1_features) and the plugins only select a set of
those based on the [feature keys
available](d4a33a2b61/x-pack/solutions/security/plugins/security_solution_serverless/common/pli/pli_config.ts)
on each tier. The refactoring to pass in feature configs at the plugin
level (instead of just feature keys) is not in the scope of the work cut
out for RSA conf.

Thus the other simpler approach in this PR is to allow overriding the
description field on the tier specific config file.

## How to Test

1. While on the Kibana root directory, run ES/Kibana on serverless mode
with:

```bash
yarn es serverless --kill --projectType security --kibanaUrl=http://0.0.0.0:5601
```
and on a new window
```bash
yarn serverless-security --no-base-path
```

Enable the AI for SOC tier, by adding the following to your
`serverless.security.dev.yaml` file:

```json5
xpack.securitySolutionServerless.productTypes:
  [
    { product_line: 'ai_soc', product_tier: 'search_ai_lake' },
  ]
```

2. Once Kibana is up and running login in with the `admin` role using
the role dropdown.
3. Navigate to `app/management/roles/edit`
4. Click on `Assign to space` button and assign a space to that role on
the `Assign role to spaces` flyout.
5. Expand the `Security` category and verify that `Security` feature is
listed in the list of features.
6. Also verify that there is neither an accordion icon beside `Security`
feature nor a description text under it about sub-privileges.

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...
This commit is contained in:
Ash 2025-04-16 23:40:09 +02:00 committed by GitHub
parent a015e5f9f7
commit f6ad013220
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 91 additions and 19 deletions

View file

@ -9,6 +9,8 @@ xpack.features.overrides:
### The following features are Security features hidden in Role management UI for this specific tier.
securitySolutionTimeline.hidden: true
securitySolutionNotes.hidden: true
siem.description: null
siemV2.description: null
## Agentless deployment by default
xpack.fleet.agentless.isDefault: true

View file

@ -46,7 +46,7 @@ export interface KibanaFeatureConfig {
/**
* An optional description that will appear as subtext underneath the feature name
*/
description?: string;
description?: string | null;
/**
* The category for this feature.

View file

@ -39,7 +39,7 @@ describe('config schema', () => {
ConfigSchema.validate(
{
overrides: {
featureA: { name: 'new name', hidden: true },
featureA: { name: 'new name', description: 'new description', hidden: true },
featureB: {
order: 100,
category: 'management',
@ -73,6 +73,7 @@ describe('config schema', () => {
Object {
"overrides": Object {
"featureA": Object {
"description": "new description",
"hidden": true,
"name": "new name",
},
@ -114,6 +115,27 @@ describe('config schema', () => {
`);
});
it('can override `description` when it is `null`', () => {
expect(
ConfigSchema.validate(
{
overrides: {
featureA: { description: null },
},
},
{ serverless: true }
)
).toMatchInlineSnapshot(`
Object {
"overrides": Object {
"featureA": Object {
"description": null,
},
},
}
`);
});
it('properly validates category override', () => {
for (const category of Object.keys(DEFAULT_APP_CATEGORIES)) {
expect(

View file

@ -34,6 +34,7 @@ export const ConfigSchema = schema.object({
schema.object({
hidden: schema.maybe(schema.boolean()),
name: schema.maybe(schema.string({ minLength: 1 })),
description: schema.maybe(schema.nullable(schema.string({ minLength: 1 }))),
category: schema.maybe(
schema.string({
validate(categoryName) {

View file

@ -123,6 +123,10 @@ export class FeatureRegistry {
feature.name = featureOverride.name;
}
if (typeof featureOverride.description !== 'undefined') {
feature.description = featureOverride.description;
}
if (featureOverride.category) {
feature.category = DEFAULT_APP_CATEGORIES[featureOverride.category];
}

View file

@ -30,6 +30,10 @@ export enum ProductFeatureSecurityKey {
* running endpoint security
*/
endpointHostManagement = 'endpoint_host_management',
/**
* Enables access to Endpoint host isolation and release actions
*/
endpointHostIsolation = 'endpoint_host_isolation',
/**
* Enables endpoint policy views that enables user to manage endpoint security policies
*/

View file

@ -111,6 +111,10 @@ export const securityDefaultProductFeaturesConfig: DefaultSecurityProductFeature
},
},
[ProductFeatureSecurityKey.endpointHostIsolation]: {
subFeatureIds: [SecuritySubFeatureId.hostIsolation],
},
[ProductFeatureSecurityKey.endpointHostManagement]: {
subFeatureIds: [SecuritySubFeatureId.endpointList],
},

View file

@ -689,7 +689,7 @@ const endpointExceptionsSubFeature = (): SubFeatureConfig => ({
*/
export const getSecurityBaseKibanaSubFeatureIds = (
{ experimentalFeatures }: SecurityFeatureParams // currently un-used, but left here as a convenience for possible future use
): SecuritySubFeatureId[] => [SecuritySubFeatureId.hostIsolation];
): SecuritySubFeatureId[] => [];
/**
* Defines all the Security Assistant subFeatures available.

View file

@ -756,7 +756,7 @@ const globalArtifactManagementSubFeature = (): SubFeatureConfig => ({
*/
export const getSecurityV2BaseKibanaSubFeatureIds = (
{ experimentalFeatures }: SecurityFeatureParams // currently un-used, but left here as a convenience for possible future use
): SecuritySubFeatureId[] => [SecuritySubFeatureId.hostIsolation];
): SecuritySubFeatureId[] => [];
/**
* Defines all the Security Assistant subFeatures available.

View file

@ -22,8 +22,9 @@ export const PLI_PRODUCT_FEATURES: PliProductFeatures = {
ProductFeatureKey.configurations,
ProductFeatureKey.externalDetections,
],
essentials: [ProductFeatureKey.attackDiscovery, ProductFeatureKey.assistant],
complete: [ProductFeatureKey.attackDiscovery, ProductFeatureKey.assistant],
// neither of these tiers are available in ai_soc product line
essentials: [],
complete: [],
},
[ProductLine.security]: {
search_ai_lake: [],
@ -33,6 +34,7 @@ export const PLI_PRODUCT_FEATURES: PliProductFeatures = {
ProductFeatureKey.notes,
ProductFeatureKey.endpointHostManagement,
ProductFeatureKey.endpointPolicyManagement,
ProductFeatureKey.endpointHostIsolation,
],
complete: [
ProductFeatureKey.detections,
@ -40,6 +42,7 @@ export const PLI_PRODUCT_FEATURES: PliProductFeatures = {
ProductFeatureKey.notes,
ProductFeatureKey.endpointHostManagement,
ProductFeatureKey.endpointPolicyManagement,
ProductFeatureKey.endpointHostIsolation,
ProductFeatureKey.advancedInsights,
ProductFeatureKey.assistant,
ProductFeatureKey.attackDiscovery,

View file

@ -34,8 +34,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
])}`,
'--csp.strict=false',
'--csp.warnLegacyBrowsers=false',
'--xpack.features.overrides.securitySolutionTimeline.hidden=true',
'--xpack.features.overrides.securitySolutionNotes.hidden=true',
],
},
testRunner: SecuritySolutionConfigurableCypressTestRunner,

View file

@ -6,8 +6,13 @@
*/
import {
NOTES_SUB_PRIVILEGE,
TIMELINE_SUB_PRIVILEGE,
ATTACK_DISCOVERY_FEATURE,
CASES_FEATURE,
ELASTIC_AI_ASSISTANT_FEATURE,
MACHINE_LEARNING_FEATURE,
NOTES_FEATURE,
SECURITY_FEATURE,
TIMELINE_FEATURE,
} from '../../../screens/custom_roles/assign_to_space_flyout';
import { login } from '../../../tasks/login';
import { visit } from '../../../tasks/navigation';
@ -21,11 +26,31 @@ describe('Custom role creation', { tags: '@serverless' }, () => {
});
describe('Security privileges', () => {
it('should not show `Timelines` and `Notes` sub-privilege', () => {
it('should not show `Security` sub-privileges', () => {
selectAllSpaces();
// should not have timeline/notes sub-privileges
cy.get(TIMELINE_SUB_PRIVILEGE).should('not.exist');
cy.get(NOTES_SUB_PRIVILEGE).should('not.exist');
// should not have Security sub-privileges
cy.get(SECURITY_FEATURE).should('exist');
cy.get(SECURITY_FEATURE).click();
cy.get(`${SECURITY_FEATURE} button.euiAccordion__arrow`).should('not.exist');
});
it('should not show `Timelines` and `Notes` features', () => {
selectAllSpaces();
// should not have Timeline/Notes sub-privileges
cy.get(TIMELINE_FEATURE).should('not.exist');
cy.get(NOTES_FEATURE).should('not.exist');
});
it('should show `Cases`, `Machine Learning`, `Elastic AI Assistant` and `Attack discovery` features', () => {
selectAllSpaces();
// should have Cases sub-privilege
cy.get(CASES_FEATURE).should('exist');
// should have Machine Learning sub-privilege
cy.get(MACHINE_LEARNING_FEATURE).should('exist');
// should have Elastic AI Assistant sub-privilege
cy.get(ELASTIC_AI_ASSISTANT_FEATURE).should('exist');
// should have Attack Discovery sub-privilege
cy.get(ATTACK_DISCOVERY_FEATURE).should('exist');
});
});
});

View file

@ -8,10 +8,19 @@
export const SPACE_SELECTOR_COMBO_BOX = '[data-test-subj="spaceSelectorComboBox"]';
// Privileges
export const SECURITY_PRIVILEGE = '[data-test-subj="featureCategory_securitySolution"]';
export const SECURITY_CATEGORY = '[data-test-subj="featureCategory_securitySolution"]';
// Sub-privileges
export const TIMELINE_SUB_PRIVILEGE =
export const SECURITY_FEATURE = '[data-test-subj="featureCategory_securitySolution_siemV2"]';
export const CASES_FEATURE =
'[data-test-subj="featureCategory_securitySolution_securitySolutionCasesV3"]';
export const MACHINE_LEARNING_FEATURE = '[data-test-subj="featureCategory_securitySolution_ml"]';
export const ELASTIC_AI_ASSISTANT_FEATURE =
'[data-test-subj="featureCategory_securitySolution_securitySolutionAssistant"]';
export const ATTACK_DISCOVERY_FEATURE =
'[data-test-subj="featureCategory_securitySolution_securitySolutionAttackDiscovery"]';
export const TIMELINE_FEATURE =
'[data-test-subj="featureCategory_securitySolution_securitySolutionTimeline"]';
export const NOTES_SUB_PRIVILEGE =
export const NOTES_FEATURE =
'[data-test-subj="featureCategory_securitySolution_securitySolutionNotes"]';

View file

@ -7,7 +7,7 @@
import {
SPACE_SELECTOR_COMBO_BOX,
SECURITY_PRIVILEGE,
SECURITY_CATEGORY,
} from '../screens/custom_roles/assign_to_space_flyout';
import { ASSIGN_TO_SPACE_BUTTON } from '../screens/custom_roles/custom_role_page';
@ -20,5 +20,5 @@ export const selectAllSpaces = (): void => {
cy.get(SPACE_SELECTOR_COMBO_BOX).type('{enter}');
// expand security privileges
cy.get(SECURITY_PRIVILEGE).click();
cy.get(SECURITY_CATEGORY).click();
};