[Security Solution][EDR Workflows] Enable scan response action feature flag for 8.15 (#187849)

## Summary

- enables `responseActionScanEnabled` feature flag
- updates tests that depend on the feature flag
- adds some tests to test with and without FF -> these are coming from
the following PR waiting to be merged to `main`
  - https://github.com/elastic/kibana/pull/187758
  - originally wanted to merge and backport the above PR
- but we got some blocking test failures on `main`, and i'd like to
merge this PR before BC2 is built

### Checklist

Delete any items that are not applicable to this PR.

- [x] [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

---------

Co-authored-by: Ashokaditya <ashokaditya@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Gergő Ábrahám 2024-07-10 16:36:29 +02:00 committed by GitHub
parent 2f6d2b9541
commit fc2898c058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 80 additions and 76 deletions

View file

@ -90,7 +90,7 @@ export const allowedExperimentalValues = Object.freeze({
/**
* Enables scan response action on Endpoint
*/
responseActionScanEnabled: false,
responseActionScanEnabled: true,
/**
* Enables top charts on Alerts Page

View file

@ -39,6 +39,7 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint
import { useGetEndpointActionList as _useGetEndpointActionList } from '../../../hooks/response_actions/use_get_endpoint_action_list';
import { OUTPUT_MESSAGES } from '../translations';
import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator';
import type { ExperimentalFeatures } from '../../../../../common';
const useGetEndpointActionListMock = _useGetEndpointActionList as jest.Mock;
@ -1490,6 +1491,17 @@ describe('Response actions history', () => {
});
describe('Actions filter', () => {
let featureFlags: Partial<ExperimentalFeatures>;
beforeEach(() => {
featureFlags = {
responseActionUploadEnabled: true,
responseActionScanEnabled: false,
};
mockedContext.setExperimentalFlag(featureFlags);
});
const filterPrefix = 'actions-filter';
it('should have a search bar', () => {
@ -1505,7 +1517,10 @@ describe('Response actions history', () => {
});
it('should show a list of actions (without `scan`) when opened', () => {
mockedContext.setExperimentalFlag({ responseActionUploadEnabled: true });
mockedContext.setExperimentalFlag({
...featureFlags,
responseActionScanEnabled: false,
});
render();
const { getByTestId, getAllByTestId } = renderResult;
@ -1529,7 +1544,7 @@ describe('Response actions history', () => {
it('should show a list of actions (with `scan`) when opened', () => {
mockedContext.setExperimentalFlag({
responseActionUploadEnabled: true,
...featureFlags,
responseActionScanEnabled: true,
});
render();

View file

@ -48,6 +48,7 @@ describe('When defining a kibana role for Endpoint security access', { tags: '@e
'Process Operations Perform process-related response actions in the response console.Process Operations sub-feature privilegeAllNone',
'File Operations Perform file-related response actions in the response console.File Operations sub-feature privilegeAllNone',
'Execute Operations Perform script execution response actions in the response console.Execute Operations sub-feature privilegeAllNone',
'Scan Operations Perform folder scan response actions in the response console.Scan Operations sub-feature privilegeAllNone',
]);
});

View file

@ -1,69 +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 { closeAllToasts } from '../tasks/toasts';
import { login, ROLE } from '../tasks/login';
import { loadPage } from '../tasks/common';
describe(
'When defining a kibana role for Endpoint security access',
{
env: {
ftrConfig: {
kbnServerArgs: [
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'responseActionScanEnabled',
])}`,
],
},
},
tags: '@ess',
},
() => {
const getAllSubFeatureRows = (): Cypress.Chainable<JQuery<HTMLElement>> => {
return cy
.get('#featurePrivilegeControls_siem')
.findByTestSubj('mutexSubFeaturePrivilegeControl')
.closest('.euiFlexGroup');
};
beforeEach(() => {
login(ROLE.system_indices_superuser);
loadPage('/app/management/security/roles/edit');
closeAllToasts();
cy.getByTestSubj('addSpacePrivilegeButton').click();
cy.getByTestSubj('featureCategoryButton_securitySolution').closest('button').click();
cy.get('.featurePrivilegeName:contains("Security")').closest('button').click();
});
it('should display RBAC entries with expected controls', () => {
getAllSubFeatureRows()
.then(($subFeatures) => {
const featureRows: string[] = [];
$subFeatures.each((_, $subFeature) => {
featureRows.push($subFeature.textContent ?? '');
});
return featureRows;
})
.should('deep.equal', [
'Endpoint List Displays all hosts running Elastic Defend and their relevant integration details.Endpoint List sub-feature privilegeAllReadNone',
'Trusted Applications Helps mitigate conflicts with other software, usually other antivirus or endpoint security applications.Trusted Applications sub-feature privilegeAllReadNone',
'Host Isolation Exceptions Add specific IP addresses that isolated hosts are still allowed to communicate with, even when isolated from the rest of the network.Host Isolation Exceptions sub-feature privilegeAllReadNone',
'Blocklist Extend Elastic Defends protection against malicious processes and protect against potentially harmful applications.Blocklist sub-feature privilegeAllReadNone',
'Event Filters Filter out endpoint events that you do not need or want stored in Elasticsearch.Event Filters sub-feature privilegeAllReadNone',
'Elastic Defend Policy Management Access the Elastic Defend integration policy to configure protections, event collection, and advanced policy features.Elastic Defend Policy Management sub-feature privilegeAllReadNone',
'Response Actions History Access the history of response actions performed on endpoints.Response Actions History sub-feature privilegeAllReadNone',
'Host Isolation Perform the "isolate" and "release" response actions.Host Isolation sub-feature privilegeAllNone',
'Process Operations Perform process-related response actions in the response console.Process Operations sub-feature privilegeAllNone',
'File Operations Perform file-related response actions in the response console.File Operations sub-feature privilegeAllNone',
'Execute Operations Perform script execution response actions in the response console.Execute Operations sub-feature privilegeAllNone',
'Scan Operations Perform folder scan response actions in the response console.Scan Operations sub-feature privilegeAllNone',
]);
});
}
);

View file

@ -20,8 +20,6 @@ import { MANAGEMENT_PATH } from '../../../../../common/constants';
import { getActionListMock } from '../../../components/endpoint_response_actions_list/mocks';
import { useGetEndpointsList } from '../../../hooks/endpoint/use_get_endpoints_list';
jest.mock('../../../../common/experimental_features_service');
let mockUseGetEndpointActionList: {
isFetched?: boolean;
isFetching?: boolean;
@ -463,7 +461,12 @@ describe('Response actions history page', () => {
expect(history.location.search).toEqual('?page=1&pageSize=20');
});
it('should set selected command filter options to URL params ', () => {
// TODO: remove this test when responseActionScanEnabled is removed
it('should set selected command filter options to URL params (without `responseActionScanEnabled`)', () => {
mockedContext.setExperimentalFlag({
responseActionScanEnabled: false,
});
const filterPrefix = 'actions-filter';
render();
const { getAllByTestId, getByTestId } = renderResult;
@ -480,6 +483,27 @@ describe('Response actions history page', () => {
);
});
it('should set selected command filter options to URL params (with `responseActionScanEnabled`)', () => {
mockedContext.setExperimentalFlag({
responseActionScanEnabled: true,
});
const filterPrefix = 'actions-filter';
render();
const { getAllByTestId, getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
allFilterOptions.forEach((option) => {
option.style.pointerEvents = 'all';
userEvent.click(option);
});
expect(history.location.search).toEqual(
'?commands=isolate%2Crelease%2Ckill-process%2Csuspend-process%2Cprocesses%2Cget-file%2Cexecute%2Cupload%2Cscan'
);
});
it('should set selected hosts filter options to URL params ', () => {
const filterPrefix = 'hosts-filter';
render();
@ -607,7 +631,12 @@ describe('Response actions history page', () => {
});
describe('Clear all selected options on a filter', () => {
it('should clear all selected options on `actions` filter', () => {
// TODO: remove this test when responseActionScanEnabled is removed
it('should clear all selected options on `actions` filter (without `responseActionScanEnabled`)', () => {
mockedContext.setExperimentalFlag({
responseActionScanEnabled: false,
});
const filterPrefix = 'actions-filter';
render();
const { getAllByTestId, getByTestId } = renderResult;
@ -629,6 +658,32 @@ describe('Response actions history page', () => {
expect(history.location.search).toEqual('');
});
it('should clear all selected options on `actions` filter (with `responseActionScanEnabled`)', () => {
mockedContext.setExperimentalFlag({
responseActionScanEnabled: true,
});
const filterPrefix = 'actions-filter';
render();
const { getAllByTestId, getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
allFilterOptions.forEach((option) => {
option.style.pointerEvents = 'all';
userEvent.click(option);
});
expect(history.location.search).toEqual(
'?commands=isolate%2Crelease%2Ckill-process%2Csuspend-process%2Cprocesses%2Cget-file%2Cexecute%2Cupload%2Cscan'
);
const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`);
clearAllButton.style.pointerEvents = 'all';
userEvent.click(clearAllButton);
expect(history.location.search).toEqual('');
});
it('should clear all selected options on `hosts` filter', () => {
const filterPrefix = 'hosts-filter';
render();

View file

@ -68,6 +68,7 @@ export default function ({ getService }: FtrProviderContext) {
'process_operations_all',
'file_operations_all',
'execute_operations_all',
'scan_operations_all',
],
uptime: ['all', 'read', 'minimal_all', 'minimal_read', 'elastic_managed_locations_enabled'],
securitySolutionAssistant: [

View file

@ -145,6 +145,7 @@ export default function ({ getService }: FtrProviderContext) {
'trusted_applications_read',
'file_operations_all',
'execute_operations_all',
'scan_operations_all',
],
uptime: [
'all',