[Defend Workflows] Add tags for mocked and real Endpoint Cypress tests for Serverless (#165094)

## Summary

- introduces tags for [Defend Workflows] cypress tests (similarly to
https://github.com/elastic/kibana/pull/162698)
- adds scripts to Security Solution:
  - `cypress:dw:serverless:open` and `:run`
  - `cypress:dw:endpoint:serverless:open` and `:run`
- adds CI jobs to run these scripts
- so far most of the expected tests got both `@serverless` and
`@brokenInServerless` tests, because of other issues to be solved,
- one test is able to run against serverless:
`x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/policy_details.cy.ts`
This commit is contained in:
Gergő Ábrahám 2023-09-20 10:36:11 +02:00 committed by GitHub
parent 4270c5e031
commit aa36fe67ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 698 additions and 424 deletions

View file

@ -28,6 +28,7 @@ disabled:
- x-pack/test/defend_workflows_cypress/cli_config.ts
- x-pack/test/defend_workflows_cypress/config.ts
- x-pack/test/defend_workflows_cypress/endpoint_config.ts
- x-pack/test/defend_workflows_cypress/endpoint_serverless_config.ts
- x-pack/plugins/observability_onboarding/e2e/ftr_config_open.ts
- x-pack/plugins/observability_onboarding/e2e/ftr_config_runner.ts
- x-pack/plugins/observability_onboarding/e2e/ftr_config.ts

View file

@ -22,3 +22,28 @@ steps:
automatic:
- exit_status: '*'
limit: 1
- command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh
label: 'Defend Workflows Cypress Tests on Serverless'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 120
parallelism: 2
retry:
automatic:
- exit_status: '*'
limit: 1
- command: .buildkite/scripts/steps/functional/defend_workflows_vagrant_serverless.sh
label: 'Defend Workflows Endpoint Cypress Tests on Serverless'
agents:
queue: n2-4-virt
depends_on: build
timeout_in_minutes: 120
parallelism: 5
retry:
automatic:
- exit_status: '*'
limit: 1

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
source .buildkite/scripts/steps/functional/common.sh
source .buildkite/scripts/steps/functional/common_cypress.sh
export JOB=kibana-defend-workflows-serverless-cypress
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Defend Workflows Cypress tests on Serverless"
cd x-pack/plugins/security_solution
yarn cypress:dw:serverless:run; status=$?; yarn junit:merge || :; exit $status

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
source .buildkite/scripts/steps/functional/common.sh
source .buildkite/scripts/steps/functional/common_cypress.sh
export JOB=kibana-defend-workflows-endpoint-serverless-cypress
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Defend Workflows Endpoint Cypress tests on Serverless"
cd x-pack/plugins/security_solution
yarn cypress:dw:endpoint:serverless:run; status=$?; yarn junit:merge || :; exit $status

View file

@ -10,12 +10,18 @@
"cypress": "NODE_OPTIONS=--openssl-legacy-provider ../../../node_modules/.bin/cypress",
"cypress:burn": "yarn cypress:dw run --env burn=2 --headed",
"cypress:changed-specs-only": "yarn cypress:dw run --changed-specs-only --env burn=2",
"cypress:dw": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress.config.ts ts --ftr-config-file ../../test/defend_workflows_cypress/cli_config",
"cypress:dw": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress/cypress.config.ts --ftr-config-file ../../test/defend_workflows_cypress/cli_config",
"cypress:dw:open": "yarn cypress:dw open",
"cypress:dw:run": "yarn cypress:dw run",
"cypress:dw:endpoint": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress_endpoint.config.ts --ftr-config-file ../../test/defend_workflows_cypress/endpoint_config",
"cypress:dw:endpoint": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress/cypress_endpoint.config.ts --ftr-config-file ../../test/defend_workflows_cypress/endpoint_config",
"cypress:dw:endpoint:run": "yarn cypress:dw:endpoint run",
"cypress:dw:endpoint:open": "yarn cypress:dw:endpoint open ",
"cypress:dw:endpoint:open": "yarn cypress:dw:endpoint open",
"cypress:dw:serverless": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress/cypress_serverless.config.ts --ftr-config-file ../../../x-pack/test_serverless/functional/test_suites/security/cypress/security_config",
"cypress:dw:serverless:open": "yarn cypress:dw:serverless open",
"cypress:dw:serverless:run": "yarn cypress:dw:serverless run",
"cypress:dw:endpoint:serverless": "NODE_OPTIONS=--openssl-legacy-provider node ./scripts/start_cypress_parallel --config-file ./public/management/cypress/cypress_endpoint_serverless.config.ts --ftr-config-file ../../test/defend_workflows_cypress/endpoint_serverless_config",
"cypress:dw:endpoint:serverless:open": "yarn cypress:dw:endpoint:serverless open",
"cypress:dw:endpoint:serverless:run": "yarn cypress:dw:endpoint:serverless run",
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results && yarn junit:transform && mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/",
"test:generate": "node scripts/endpoint/resolver_generator",
"mappings:generate": "node scripts/mappings/mappings_generator",
@ -24,4 +30,4 @@
"openapi:generate": "node scripts/openapi/generate",
"openapi:generate:debug": "node --inspect-brk scripts/openapi/generate"
}
}
}

View file

@ -0,0 +1,13 @@
{
"plugins": ["cypress"],
"extends": [
"plugin:cypress/recommended"
],
"env": {
"cypress/globals": true
},
"rules": {
"cypress/no-force": "warn",
"import/no-extraneous-dependencies": "off"
}
}

View file

@ -29,6 +29,16 @@ If you also want to run the tests against real endpoints as on the CI pipeline,
See [running interactive tests on real endpoint with vagrant](#cypress-interactive-with-real-endpoints-using-vagrant)
for more information.
## Adding new tests - tagging for ESS vs Serverless
Similarly to Security Solution cypress tests, we use tags in order to select which tests we want to execute on which environment:
- `@serverless` includes a test in the Serverless test suite. You need to explicitly add this tag to any test you want to run against a Serverless environment.
- `@ess` includes a test in the normal, non-Serverless test suite. You need to explicitly add this tag to any test you want to run against a non-Serverless environment.
- `@brokenInServerless` excludes a test from the Serverless test suite (even if it's tagged as `@serverless`). Indicates that a test should run in Serverless, but currently is broken.
Important: if you don't provide any tag, your test won't be executed.
## Running the tests
There are currently three ways to run the tests, comprised of two execution modes and two target environments, which
@ -102,14 +112,11 @@ failures locally, etc.
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
# launch the cypress test runner
cd x-pack/plugins/security_solution
yarn cypress:dw:run-as-ci
# launch the cypress test runner against ESS
yarn --cwd x-pack/plugins/security_solution cypress:dw:run
# or
# launch without changing directory from kibana/
yarn --cwd x-pack/plugins/security_solution cypress:dw:run-as-ci
# or against Serverless
yarn --cwd x-pack/plugins/security_solution cypress:dw:serverless:run
```
#### Cypress
@ -120,14 +127,11 @@ This is the preferred mode for developing new tests against mocked data
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
# launch the cypress test runner
cd x-pack/plugins/security_solution
yarn cypress:dw:open
# or
# launch without changing directory from kibana/
# launch the cypress test runner against ESS
yarn --cwd x-pack/plugins/security_solution cypress:dw:open
# or against Serverless
yarn --cwd x-pack/plugins/security_solution cypress:dw:serverless:open
```
For developing/debugging tests against real endpoint please use:
@ -138,14 +142,11 @@ Endpoint tests require [Multipass](https://multipass.run/) to be installed on yo
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
# launch the cypress test runner with real endpoint
cd x-pack/plugins/security_solution
yarn cypress:dw:endpoint:open
# or
# launch without changing directory from kibana/
# launch the cypress test runner against ESS
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:open
# or against Serverless
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:serverless:open
```
#### Cypress (interactive) with real Endpoints using Vagrant
@ -172,14 +173,11 @@ Endpoint tests require [Multipass](https://multipass.run/) to be installed on yo
# bootstrap kibana from the project root and build the plugins/assets that cypress will execute against
yarn kbn bootstrap && node scripts/build_kibana_platform_plugins
# launch the cypress test runner with real endpoint
cd x-pack/plugins/security_solution
yarn cypress:dw:endpoint:run
# or
# launch without changing directory from kibana/
# launch the cypress test runner with real endpoint against ESS
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:run
# or against Serverless
yarn --cwd x-pack/plugins/security_solution cypress:dw:endpoint:serverless:run
```
## Folder Structure

View file

@ -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 { defineCypressConfig } from '@kbn/cypress-config';
import { getCypressBaseConfig } from './cypress_base.config';
import { dataLoaders } from './support/data_loaders';
export default defineCypressConfig({
...getCypressBaseConfig(),
env: {
...getCypressBaseConfig().env,
grepTags: '@ess',
},
e2e: {
...getCypressBaseConfig().e2e,
specPattern: 'public/management/cypress/e2e/mocked_data/',
setupNodeEvents: (on, config) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return dataLoaders(on, config);
},
},
});

View file

@ -5,11 +5,7 @@
* 2.0.
*/
import { defineCypressConfig } from '@kbn/cypress-config';
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
import { dataLoaders } from './cypress/support/data_loaders';
export default defineCypressConfig({
export const getCypressBaseConfig = () => ({
reporter: '../../../../node_modules/cypress-multi-reporters',
reporterOptions: {
configFile: './public/management/reporter_config.json',
@ -36,21 +32,23 @@ export default defineCypressConfig({
KIBANA_URL: 'http://localhost:5601',
ELASTICSEARCH_URL: 'http://localhost:9200',
FLEET_SERVER_URL: 'https://localhost:8220',
// Username/password used for both elastic and kibana
KIBANA_USERNAME: 'system_indices_superuser',
KIBANA_PASSWORD: 'changeme',
ELASTICSEARCH_USERNAME: 'system_indices_superuser',
ELASTICSEARCH_PASSWORD: 'changeme',
// grep related configs
grepFilterSpecs: true,
grepOmitFiltered: true,
},
e2e: {
// baseUrl: To override, set Env. variable `CYPRESS_BASE_URL`
baseUrl: 'http://localhost:5601',
supportFile: 'public/management/cypress/support/e2e.ts',
specPattern: 'public/management/cypress/e2e/mocked_data/',
experimentalRunAllSpecs: true,
setupNodeEvents: (on, config) => {
return dataLoaders(on, config);
},
},
});

View file

@ -0,0 +1,47 @@
/*
* 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 { defineCypressConfig } from '@kbn/cypress-config';
import { getCypressBaseConfig } from './cypress_base.config';
import { dataLoaders, dataLoadersForRealEndpoints } from './support/data_loaders';
import { responseActionTasks } from './support/response_actions';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
...getCypressBaseConfig(),
env: {
...getCypressBaseConfig().env,
'cypress-react-selector': {
root: '#security-solution-app',
},
grepTags: '@ess',
},
e2e: {
...getCypressBaseConfig().e2e,
experimentalMemoryManagement: true,
experimentalInteractiveRunEvents: true,
specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}',
setupNodeEvents: (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {
dataLoaders(on, config);
// Data loaders specific to "real" Endpoint testing
dataLoadersForRealEndpoints(on, config);
responseActionTasks(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
},
},
});

View file

@ -0,0 +1,50 @@
/*
* 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 { defineCypressConfig } from '@kbn/cypress-config';
import { getCypressBaseConfig } from './cypress_base.config';
import { dataLoaders } from './support/data_loaders';
import { responseActionTasks } from './support/response_actions';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
...getCypressBaseConfig(),
env: {
...getCypressBaseConfig().env,
IS_SERVERLESS: true,
grepTags: '@serverless --@brokenInServerless',
'cypress-react-selector': {
root: '#security-solution-app',
},
},
e2e: {
...getCypressBaseConfig().e2e,
experimentalMemoryManagement: true,
experimentalInteractiveRunEvents: true,
specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}',
setupNodeEvents: (on, config) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
dataLoaders(on, config);
// skip dataLoadersForRealEndpoints()
// https://github.com/elastic/security-team/issues/7467
// Data loaders specific to "real" Endpoint testing
// dataLoadersForRealEndpoints(on, config);
responseActionTasks(on, config);
},
},
});

View file

@ -0,0 +1,36 @@
/*
* 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 { defineCypressConfig } from '@kbn/cypress-config';
import { getCypressBaseConfig } from './cypress_base.config';
import { dataLoaders } from './support/data_loaders';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
...getCypressBaseConfig(),
env: {
...getCypressBaseConfig().env,
IS_SERVERLESS: true,
grepTags: '@serverless --@brokenInServerless',
},
e2e: {
...getCypressBaseConfig().e2e,
specPattern: 'public/management/cypress/e2e/mocked_data/',
setupNodeEvents: (on, config) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return dataLoaders(on, config);
},
},
});

View file

@ -36,7 +36,7 @@ const yieldAppliedEndpointRevision = (): Cypress.Chainable<number> =>
const parseRevNumber = (revString: string) => Number(revString.match(/\d+/)?.[0]);
describe('Artifact pages', () => {
describe('Artifact pages', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
@ -61,6 +61,7 @@ describe('Artifact pages', () => {
// wait for ManifestManager to pick up artifact changes that happened either here
// or in a previous test suite `after`
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(6000); // packagerTaskInterval + 1s
yieldEndpointPolicyRevision().then((actualEndpointPolicyRevision) => {

View file

@ -20,86 +20,91 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
describe('Automated Response Actions', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
before(() => {
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version, 'automated_response_actions').then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_id).then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
});
});
})
);
});
after(() => {
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});
const hostname = new URL(Cypress.env('FLEET_SERVER_URL')).port;
const fleetHostname = `dev-fleet-server.${hostname}`;
beforeEach(() => {
login();
disableExpandableFlyoutAdvancedSettings();
});
describe('From alerts', () => {
let ruleId: string;
let ruleName: string;
describe(
'Automated Response Actions',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
before(() => {
loadRule().then((data) => {
ruleId = data.id;
ruleName = data.name;
});
getEndpointIntegrationVersion().then((version) =>
createAgentPolicyTask(version, 'automated_response_actions').then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_id).then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
});
});
})
);
});
after(() => {
if (ruleId) {
cleanupRule(ruleId);
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});
it.skip('should have generated endpoint and rule', () => {
loadPage(APP_ENDPOINTS_PATH);
cy.contains(createdHost.hostname).should('exist');
toggleRuleOffAndOn(ruleName);
const hostname = new URL(Cypress.env('FLEET_SERVER_URL')).port;
const fleetHostname = `dev-fleet-server.${hostname}`;
visitRuleAlerts(ruleName);
closeAllToasts();
changeAlertsFilter('event.category: "file"');
cy.getByTestSubj('expand-event').first().click();
cy.getByTestSubj('responseActionsViewTab').click();
cy.getByTestSubj('response-actions-notification').should('not.have.text', '0');
cy.getByTestSubj(`response-results-${createdHost.hostname}-details-tray`)
.should('contain', 'isolate completed successfully')
.and('contain', createdHost.hostname);
cy.getByTestSubj(`response-results-${fleetHostname}-details-tray`)
.should('contain', 'The host does not have Elastic Defend integration installed')
.and('contain', 'dev-fleet-server');
beforeEach(() => {
login();
disableExpandableFlyoutAdvancedSettings();
});
});
});
describe('From alerts', () => {
let ruleId: string;
let ruleName: string;
before(() => {
loadRule().then((data) => {
ruleId = data.id;
ruleName = data.name;
});
});
after(() => {
if (ruleId) {
cleanupRule(ruleId);
}
});
it.skip('should have generated endpoint and rule', () => {
loadPage(APP_ENDPOINTS_PATH);
cy.contains(createdHost.hostname).should('exist');
toggleRuleOffAndOn(ruleName);
visitRuleAlerts(ruleName);
closeAllToasts();
changeAlertsFilter('event.category: "file"');
cy.getByTestSubj('expand-event').first().click();
cy.getByTestSubj('responseActionsViewTab').click();
cy.getByTestSubj('response-actions-notification').should('not.have.text', '0');
cy.getByTestSubj(`response-results-${createdHost.hostname}-details-tray`)
.should('contain', 'isolate completed successfully')
.and('contain', createdHost.hostname);
cy.getByTestSubj(`response-results-${fleetHostname}-details-tray`)
.should('contain', 'The host does not have Elastic Defend integration installed')
.and('contain', 'dev-fleet-server');
});
});
}
);

View file

@ -19,79 +19,80 @@ import { login } from '../../tasks/login';
import { EXECUTE_ROUTE } from '../../../../../common/endpoint/constants';
import { waitForActionToComplete } from '../../tasks/response_actions';
describe('Endpoint generated alerts', () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
describe(
'Endpoint generated alerts',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] },
() => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version, 'alerts test').then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
before(() => {
getEndpointIntegrationVersion().then((version) => {
createAgentPolicyTask(version, 'alerts test').then((data) => {
indexedPolicy = data;
policy = indexedPolicy.integrationPolicies[0];
return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_id).then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
return enableAllPolicyProtections(policy.id).then(() => {
// Create and enroll a new Endpoint host
return createEndpointHost(policy.policy_id).then((host) => {
createdHost = host as CreateAndEnrollEndpointHostResponse;
});
});
});
});
});
});
after(() => {
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}
after(() => {
if (createdHost) {
cy.task('destroyEndpointHost', createdHost);
}
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
if (indexedPolicy) {
cy.task('deleteIndexedFleetEndpointPolicies', indexedPolicy);
}
if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});
if (createdHost) {
deleteAllLoadedEndpointData({ endpointAgentIds: [createdHost.agentId] });
}
});
beforeEach(() => {
login();
});
beforeEach(() => {
login();
});
it('should create a Detection Engine alert from an endpoint alert', () => {
// Triggers a Malicious Behaviour alert on Linux system (`grep *` was added only to identify this specific alert)
const executeMaliciousCommand = `bash -c cat /dev/tcp/foo | grep ${Math.random()
.toString(16)
.substring(2)}`;
it('should create a Detection Engine alert from an endpoint alert', () => {
// Triggers a Malicious Behaviour alert on Linux system (`grep *` was added only to identify this specific alert)
const executeMaliciousCommand = `bash -c cat /dev/tcp/foo | grep ${Math.random()
.toString(16)
.substring(2)}`;
// Send `execute` command that triggers malicious behaviour using the `execute` response action
request<ResponseActionApiResponse>({
method: 'POST',
url: EXECUTE_ROUTE,
body: {
endpoint_ids: [createdHost.agentId],
parameters: {
command: executeMaliciousCommand,
},
},
headers: {
'Elastic-Api-Version': '2023-10-31',
},
})
.then((response) => waitForActionToComplete(response.body.data.id))
.then(() => {
return waitForEndpointAlerts(createdHost.agentId, [
{
term: { 'process.group_leader.args': executeMaliciousCommand },
// Send `execute` command that triggers malicious behaviour using the `execute` response action
request<ResponseActionApiResponse>({
method: 'POST',
url: EXECUTE_ROUTE,
body: {
endpoint_ids: [createdHost.agentId],
parameters: {
command: executeMaliciousCommand,
},
]);
},
})
.then(() => {
return navigateToAlertsList(
`query=(language:kuery,query:'agent.id: "${createdHost.agentId}" ')`
);
});
.then((response) => waitForActionToComplete(response.body.data.id))
.then(() => {
return waitForEndpointAlerts(createdHost.agentId, [
{
term: { 'process.group_leader.args': executeMaliciousCommand },
},
]);
})
.then(() => {
return navigateToAlertsList(
`query=(language:kuery,query:'agent.id: "${createdHost.agentId}" ')`
);
});
getAlertsTableRows().should('have.length.greaterThan', 0);
});
});
getAlertsTableRows().should('have.length.greaterThan', 0);
});
}
);

View file

@ -32,7 +32,7 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
describe('Endpoints page', () => {
describe('Endpoints page', { tags: '@ess' }, () => {
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
let policy: PolicyData;
let createdHost: CreateAndEnrollEndpointHostResponse;
@ -109,24 +109,26 @@ describe('Endpoints page', () => {
it('User can reassign a single endpoint to a different Agent Configuration', () => {
loadPage(APP_ENDPOINTS_PATH);
const hostname = cy
.getByTestSubj(AGENT_HOSTNAME_CELL)
.filter(`:contains("${createdHost.hostname}")`);
const tableRow = hostname.parents('tr');
tableRow.findByTestSubj(TABLE_ROW_ACTIONS).click();
cy.getByTestSubj(TABLE_ROW_ACTIONS_MENU).contains('Reassign agent policy').click();
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL)
.find('select')
.select(response.agentPolicies[0].name);
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON).click();
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
.filter(`:contains("${createdHost.hostname}")`)
.should('exist');
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
.filter(`:contains("${createdHost.hostname}")`)
.parents('tr')
.findByTestSubj(AGENT_POLICY_CELL)
.should('have.text', response.agentPolicies[0].name);
.then((hostname) => {
const tableRow = hostname.parents('tr');
tableRow.find(`[data-test-subj=${TABLE_ROW_ACTIONS}`).trigger('click');
cy.getByTestSubj(TABLE_ROW_ACTIONS_MENU).contains('Reassign agent policy').click();
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL)
.find('select')
.select(response.agentPolicies[0].name);
cy.getByTestSubj(FLEET_REASSIGN_POLICY_MODAL_CONFIRM_BUTTON).click();
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
.filter(`:contains("${createdHost.hostname}")`)
.should('exist');
cy.getByTestSubj(AGENT_HOSTNAME_CELL)
.filter(`:contains("${createdHost.hostname}")`)
.parents('tr')
.findByTestSubj(AGENT_POLICY_CELL)
.should('have.text', response.agentPolicies[0].name);
});
});
});

View file

@ -31,7 +31,7 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
describe.skip('Isolate command', () => {
describe.skip('Isolate command', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
let isolateComment: string;
let releaseComment: string;
let indexedPolicy: IndexedFleetEndpointPolicyResponse;
@ -84,7 +84,7 @@ describe.skip('Isolate command', () => {
filterOutIsolatedHosts();
cy.contains('No items found');
cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}');
cy.getByTestSubj('adminSearchBar').type('{selectall}{backspace}');
cy.getByTestSubj('querySubmitButton').click();
cy.getByTestSubj('endpointTableRowActions').click();
cy.getByTestSubj('isolateLink').click();
@ -105,7 +105,7 @@ describe.skip('Isolate command', () => {
releaseHostWithComment(releaseComment, createdHost.hostname);
cy.contains('Confirm').click();
cy.getByTestSubj('euiFlyoutCloseButton').click();
cy.getByTestSubj('adminSearchBar').click().type('{selectall}{backspace}');
cy.getByTestSubj('adminSearchBar').type('{selectall}{backspace}');
cy.getByTestSubj('querySubmitButton').click();
checkEndpointListForOnlyUnIsolatedHosts();
});

View file

@ -19,6 +19,7 @@ import { disableExpandableFlyoutAdvancedSettings, loadPage } from '../../tasks/c
describe(
'Policy Details',
{
tags: '@ess',
env: { ftrConfig: { enableExperimental: ['protectionUpdatesEnabled'] } },
},
() => {
@ -175,7 +176,8 @@ describe(
it('should update note on save', () => {
loadProtectionUpdatesUrl(policy.id);
cy.getByTestSubj('protection-updates-manifest-note').contains(testNote);
cy.getByTestSubj('protection-updates-manifest-note').clear().type(updatedTestNote);
cy.getByTestSubj('protection-updates-manifest-note').clear();
cy.getByTestSubj('protection-updates-manifest-note').type(updatedTestNote);
cy.intercept('POST', `/api/endpoint/protection_updates_note/*`).as('note_updated');
cy.getByTestSubj('policyDetailsSaveButton').click();

View file

@ -27,7 +27,7 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy';
import { createEndpointHost } from '../../tasks/create_endpoint_host';
import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data';
describe('Response console', () => {
describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => {
beforeEach(() => {
login();
});

View file

@ -63,162 +63,166 @@ const visitArtifactTab = (tabId: string) => {
cy.get(`#${tabId}`).click();
};
describe('Artifact tabs in Policy Details page', () => {
before(() => {
login();
loadEndpointDataForEventFiltersIfNeeded();
});
describe(
'Artifact tabs in Policy Details page',
{ tags: ['@ess', '@serverless', '@brokenInServerless'] }, // broken due to disabled Native Role Management
() => {
before(() => {
login();
loadEndpointDataForEventFiltersIfNeeded();
});
after(() => {
login();
removeAllArtifacts();
});
after(() => {
login();
removeAllArtifacts();
});
for (const testData of getArtifactsListTestsData()) {
describe(`${testData.title} tab`, () => {
beforeEach(() => {
login();
removeExceptionsList(testData.createRequestBody.list_id);
});
it(`[NONE] User cannot see the tab for ${testData.title}`, () => {
loginWithPrivilegeNone(testData.privilegePrefix);
visitPolicyDetailsPage();
cy.get(`#${testData.tabId}`).should('not.exist');
});
context(`Given there are no ${testData.title} entries`, () => {
it(`[READ] User CANNOT add ${testData.title} artifact`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
cy.getByTestSubj('unexisting-manage-artifacts-button').should('not.exist');
});
it(`[ALL] User can add ${testData.title} artifact`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
cy.getByTestSubj('unexisting-manage-artifacts-button').should('exist').click();
const { formActions, checkResults } = testData.create;
performUserActions(formActions);
// Add a per policy artifact - but not assign it to any policy
cy.get('[data-test-subj$="-perPolicy"]').click(); // test-subjects are generated in different formats, but all ends with -perPolicy
cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click();
// Check new artifact is in the list
for (const checkResult of checkResults) {
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
}
cy.getByTestSubj('policyDetailsPage').should('not.exist');
cy.getByTestSubj('backToOrigin').contains(/^Back to .+ policy$/);
cy.getByTestSubj('backToOrigin').click();
cy.getByTestSubj('policyDetailsPage').should('exist');
});
});
context(`Given there are no assigned ${testData.title} entries`, () => {
for (const testData of getArtifactsListTestsData()) {
describe(`${testData.title} tab`, () => {
beforeEach(() => {
login();
createArtifactList(testData.createRequestBody.list_id);
createPerPolicyArtifact(testData.artifactName, testData.createRequestBody);
removeExceptionsList(testData.createRequestBody.list_id);
});
it(`[READ] User CANNOT Manage or Assign ${testData.title} artifacts`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
it(`[NONE] User cannot see the tab for ${testData.title}`, () => {
loginWithPrivilegeNone(testData.privilegePrefix);
visitPolicyDetailsPage();
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
cy.getByTestSubj('unassigned-manage-artifacts-button').should('not.exist');
cy.getByTestSubj('unassigned-assign-artifacts-button').should('not.exist');
cy.get(`#${testData.tabId}`).should('not.exist');
});
it(`[ALL] User can Manage and Assign ${testData.title} artifacts`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
context(`Given there are no ${testData.title} entries`, () => {
it(`[READ] User CANNOT add ${testData.title} artifact`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
// Manage artifacts
cy.getByTestSubj('unassigned-manage-artifacts-button').should('exist').click();
cy.location('pathname').should(
'equal',
`/app/security/administration/${testData.urlPath}`
);
cy.getByTestSubj('backToOrigin').click();
cy.getByTestSubj('unexisting-manage-artifacts-button').should('not.exist');
});
// Assign artifacts
cy.getByTestSubj('unassigned-assign-artifacts-button').should('exist').click();
it(`[ALL] User can add ${testData.title} artifact`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
cy.getByTestSubj('artifacts-assign-confirm-button').should('be.disabled');
cy.getByTestSubj('policy-artifacts-empty-unexisting').should('exist');
cy.getByTestSubj(`${testData.artifactName}_checkbox`).click();
cy.getByTestSubj('artifacts-assign-confirm-button').click();
});
});
cy.getByTestSubj('unexisting-manage-artifacts-button').should('exist').click();
context(`Given there are assigned ${testData.title} entries`, () => {
beforeEach(() => {
login();
createArtifactList(testData.createRequestBody.list_id);
yieldFirstPolicyID().then((policyID) => {
createPerPolicyArtifact(testData.artifactName, testData.createRequestBody, policyID);
const { formActions, checkResults } = testData.create;
performUserActions(formActions);
// Add a per policy artifact - but not assign it to any policy
cy.get('[data-test-subj$="-perPolicy"]').click(); // test-subjects are generated in different formats, but all ends with -perPolicy
cy.getByTestSubj(`${testData.pagePrefix}-flyout-submitButton`).click();
// Check new artifact is in the list
for (const checkResult of checkResults) {
cy.getByTestSubj(checkResult.selector).should('have.text', checkResult.value);
}
cy.getByTestSubj('policyDetailsPage').should('not.exist');
cy.getByTestSubj('backToOrigin').contains(/^Back to .+ policy$/);
cy.getByTestSubj('backToOrigin').click();
cy.getByTestSubj('policyDetailsPage').should('exist');
});
});
it(`[READ] User can see ${testData.title} artifacts but CANNOT assign or remove from policy`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
context(`Given there are no assigned ${testData.title} entries`, () => {
beforeEach(() => {
login();
createArtifactList(testData.createRequestBody.list_id);
createPerPolicyArtifact(testData.artifactName, testData.createRequestBody);
});
// List of artifacts
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
testData.artifactName
);
it(`[READ] User CANNOT Manage or Assign ${testData.title} artifacts`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
// Cannot assign artifacts
cy.getByTestSubj('artifacts-assign-button').should('not.exist');
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
// Cannot remove from policy
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
cy.getByTestSubj('remove-from-policy-action').should('not.exist');
cy.getByTestSubj('unassigned-manage-artifacts-button').should('not.exist');
cy.getByTestSubj('unassigned-assign-artifacts-button').should('not.exist');
});
it(`[ALL] User can Manage and Assign ${testData.title} artifacts`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
cy.getByTestSubj('policy-artifacts-empty-unassigned').should('exist');
// Manage artifacts
cy.getByTestSubj('unassigned-manage-artifacts-button').should('exist').click();
cy.location('pathname').should(
'equal',
`/app/security/administration/${testData.urlPath}`
);
cy.getByTestSubj('backToOrigin').click();
// Assign artifacts
cy.getByTestSubj('unassigned-assign-artifacts-button').should('exist').click();
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
cy.getByTestSubj('artifacts-assign-confirm-button').should('be.disabled');
cy.getByTestSubj(`${testData.artifactName}_checkbox`).click();
cy.getByTestSubj('artifacts-assign-confirm-button').click();
});
});
it(`[ALL] User can see ${testData.title} artifacts and can assign or remove artifacts from policy`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
context(`Given there are assigned ${testData.title} entries`, () => {
beforeEach(() => {
login();
createArtifactList(testData.createRequestBody.list_id);
yieldFirstPolicyID().then((policyID) => {
createPerPolicyArtifact(testData.artifactName, testData.createRequestBody, policyID);
});
});
// List of artifacts
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
testData.artifactName
);
it(`[READ] User can see ${testData.title} artifacts but CANNOT assign or remove from policy`, () => {
loginWithPrivilegeRead(testData.privilegePrefix);
visitArtifactTab(testData.tabId);
// Assign artifacts
cy.getByTestSubj('artifacts-assign-button').should('exist').click();
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
cy.getByTestSubj('artifacts-assign-cancel-button').click();
// List of artifacts
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
testData.artifactName
);
// Remove from policy
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
cy.getByTestSubj('remove-from-policy-action').click();
cy.getByTestSubj('confirmModalConfirmButton').click();
// Cannot assign artifacts
cy.getByTestSubj('artifacts-assign-button').should('not.exist');
cy.contains('Successfully removed');
// Cannot remove from policy
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
cy.getByTestSubj('remove-from-policy-action').should('not.exist');
});
it(`[ALL] User can see ${testData.title} artifacts and can assign or remove artifacts from policy`, () => {
loginWithPrivilegeAll();
visitArtifactTab(testData.tabId);
// List of artifacts
cy.getByTestSubj('artifacts-collapsed-list-card').should('have.length', 1);
cy.getByTestSubj('artifacts-collapsed-list-card-header-titleHolder').contains(
testData.artifactName
);
// Assign artifacts
cy.getByTestSubj('artifacts-assign-button').should('exist').click();
cy.getByTestSubj('artifacts-assign-flyout').should('exist');
cy.getByTestSubj('artifacts-assign-cancel-button').click();
// Remove from policy
cy.getByTestSubj('artifacts-collapsed-list-card-header-actions-button').click();
cy.getByTestSubj('remove-from-policy-action').click();
cy.getByTestSubj('confirmModalConfirmButton').click();
cy.contains('Successfully removed');
});
});
});
});
}
}
});
);

View file

@ -35,7 +35,7 @@ const loginWithoutAccess = (url: string) => {
loadPage(url);
};
describe('Artifacts pages', () => {
describe('Artifacts pages', { tags: '@ess' }, () => {
before(() => {
login();
loadEndpointDataForEventFiltersIfNeeded();

View file

@ -18,7 +18,7 @@ import { cleanupRule, generateRandomStringName, loadRule } from '../../../tasks/
import { RESPONSE_ACTION_TYPES } from '../../../../../../common/api/detection_engine';
import { loginWithRole, ROLE } from '../../../tasks/login';
describe('Form', () => {
describe('Form', { tags: '@ess' }, () => {
describe('User with no access can not create an endpoint response action', () => {
before(() => {
loginWithRole(ROLE.endpoint_response_actions_no_access);

View file

@ -12,7 +12,7 @@ import { indexEndpointRuleAlerts } from '../../../tasks/index_endpoint_rule_aler
import { login, ROLE } from '../../../tasks/login';
describe('Response actions history page', () => {
describe('Response actions history page', { tags: '@ess' }, () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts> | undefined;
let endpointDataWithAutomated: ReturnTypeFromChainable<typeof indexEndpointHosts> | undefined;
let alertData: ReturnTypeFromChainable<typeof indexEndpointRuleAlerts> | undefined;

View file

@ -15,7 +15,7 @@ import type { ReturnTypeFromChainable } from '../../../types';
import { indexEndpointHosts } from '../../../tasks/index_endpoint_hosts';
import { indexEndpointRuleAlerts } from '../../../tasks/index_endpoint_rule_alerts';
describe('No License', { env: { ftrConfig: { license: 'basic' } } }, () => {
describe('No License', { tags: '@ess', env: { ftrConfig: { license: 'basic' } } }, () => {
describe('User cannot use endpoint action in form', () => {
const [ruleName, ruleDescription] = generateRandomStringName(2);

View file

@ -15,7 +15,7 @@ import { indexEndpointRuleAlerts } from '../../../tasks/index_endpoint_rule_aler
import { login, ROLE } from '../../../tasks/login';
describe('Results', () => {
describe('Results', { tags: '@ess' }, () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts> | undefined;
let alertData: ReturnTypeFromChainable<typeof indexEndpointRuleAlerts> | undefined;
const [endpointAgentId, endpointHostname] = generateRandomStringName(2);

View file

@ -9,7 +9,7 @@ import { closeAllToasts } from '../../tasks/toasts';
import { login } from '../../tasks/login';
import { loadPage } from '../../tasks/common';
describe('When defining a kibana role for Endpoint security access', () => {
describe('When defining a kibana role for Endpoint security access', { tags: '@ess' }, () => {
const getAllSubFeatureRows = (): Cypress.Chainable<JQuery<HTMLElement>> => {
return cy
.get('#featurePrivilegeControls_siem')

View file

@ -13,7 +13,7 @@ import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { login } from '../../tasks/login';
import { loadPage } from '../../tasks/common';
describe('Endpoints page', () => {
describe('Endpoints page', { tags: '@ess' }, () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;
before(() => {

View file

@ -29,7 +29,7 @@ import { indexNewCase } from '../../tasks/index_new_case';
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts';
describe('Isolate command', () => {
describe('Isolate command', { tags: '@ess' }, () => {
describe('from Manage', () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts> | undefined;
let isolatedEndpointData: ReturnTypeFromChainable<typeof indexEndpointHosts> | undefined;
@ -165,6 +165,7 @@ describe('Isolate command', () => {
cy.contains(`Isolation on host ${hostname} successfully submitted`);
cy.getByTestSubj('euiFlyoutCloseButton').click();
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
openAlertDetails();

View file

@ -17,7 +17,7 @@ import type { CyIndexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { login } from '../../tasks/login';
describe('Policy Details', () => {
describe('Policy Details', { tags: ['@ess', '@serverless'] }, () => {
const packagePolicyBackupHelper = new PackagePolicyBackupHelper();
let indexedHostsData: CyIndexEndpointHosts;

View file

@ -15,7 +15,7 @@ import { navigateToFleetAgentDetails } from '../../screens/fleet';
import { EndpointPolicyResponseGenerator } from '../../../../../common/endpoint/data_generators/endpoint_policy_response_generator';
import { descriptions } from '../../../components/policy_response/policy_response_friendly_names';
describe.skip('Endpoint Policy Response', () => {
describe.skip('Endpoint Policy Response', { tags: '@ess' }, () => {
let loadedEndpoint: CyIndexEndpointHosts;
let endpointMetadata: HostMetadata;
let loadedPolicyResponse: IndexedEndpointPolicyResponse;

View file

@ -10,7 +10,7 @@ import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { login } from '../../tasks/login';
import { loadPage } from '../../tasks/common';
describe('Response actions history page', () => {
describe('Response actions history page', { tags: '@ess' }, () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;
// let actionData: ReturnTypeFromChainable<typeof indexActionResponses>;
@ -54,16 +54,16 @@ describe('Response actions history page', () => {
it('collapses expanded tray with a single click', () => {
loadPage(`/app/security/administration/response_actions_history`);
// 2nd row on 1st page
const row = cy.getByTestSubj('response-actions-list-expand-button').eq(1);
cy.getByTestSubj('response-actions-list-expand-button').eq(1).as('2nd-row');
// expand the row
row.click();
cy.get('@2nd-row').click();
cy.getByTestSubj('response-actions-list-details-tray').should('exist');
cy.url().should('include', 'withOutputs');
// collapse the row
cy.intercept('GET', '/api/endpoint/action*').as('getResponses');
row.click();
cy.get('@2nd-row').click();
// wait for the API response to come back
// and then see if the tray is actually closed
cy.wait('@getResponses', { timeout: 500 }).then(() => {

View file

@ -21,7 +21,7 @@ import { indexNewCase } from '../../tasks/index_new_case';
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts';
describe('When accessing Endpoint Response Console', () => {
describe('When accessing Endpoint Response Console', { tags: '@ess' }, () => {
const performResponderSanityChecks = () => {
openResponderActionLogFlyout();
// Ensure the popover in the action log date quick select picker is accessible

View file

@ -24,7 +24,7 @@ import {
} from '../../tasks/isolate';
import { login } from '../../tasks/login';
describe('Response console', () => {
describe('Response console', { tags: '@ess' }, () => {
beforeEach(() => {
login();
});

View file

@ -13,7 +13,8 @@ export const navigateToAlertsList = (urlQueryParams: string = '') => {
};
export const clickAlertListRefreshButton = (): Cypress.Chainable => {
return cy.getByTestSubj('querySubmitButton').click().should('be.enabled');
cy.getByTestSubj('querySubmitButton').click();
return cy.getByTestSubj('querySubmitButton').should('be.enabled');
};
/**

View file

@ -24,11 +24,13 @@
import { subj as testSubjSelector } from '@kbn/test-subj-selector';
// force ESM in this module
export {};
import 'cypress-react-selector';
// @ts-ignore
import registerCypressGrep from '@cypress/grep';
registerCypressGrep();
Cypress.Commands.addQuery<'getByTestSubj'>(
'getByTestSubj',
function getByTestSubj(selector, options) {

View file

@ -181,7 +181,7 @@ export const getEndpointDetectionAlertsQueryForAgentId = (endpointAgentId: strin
export const changeAlertsFilter = (text: string) => {
cy.getByTestSubj('filters-global-container').within(() => {
cy.getByTestSubj('queryInput').click().type(text);
cy.getByTestSubj('queryInput').type(text);
cy.getByTestSubj('querySubmitButton').click();
});
};

View file

@ -13,6 +13,7 @@ export const API_AUTH = Object.freeze({
export const COMMON_API_HEADERS = Object.freeze({
'kbn-xsrf': 'cypress',
'x-elastic-internal-origin': 'security-solution',
'Elastic-Api-Version': '2023-10-31',
});
export const waitForPageToBeLoaded = () => {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
/* eslint-disable cypress/no-unnecessary-waiting */
import type { ActionDetails } from '../../../../common/endpoint/types';
import { loadPage } from './common';
@ -114,7 +116,7 @@ export const filterOutEndpoints = (endpointHostname: string): void => {
};
export const filterOutIsolatedHosts = (): void => {
cy.getByTestSubj('adminSearchBar').click().type('united.endpoint.Endpoint.state.isolation: true');
cy.getByTestSubj('adminSearchBar').type('united.endpoint.Endpoint.state.isolation: true');
cy.getByTestSubj('querySubmitButton').click();
};

View file

@ -84,9 +84,6 @@ export const waitForActionToComplete = (
return request<ActionDetailsApiResponse>({
method: 'GET',
url: resolvePathVariables(ACTION_DETAILS_ROUTE, { action_id: actionId || 'undefined' }),
headers: {
'Elastic-Api-Version': '2023-10-31',
},
}).then((response) => {
if (response.body.data.isCompleted) {
action = response.body.data;

View file

@ -22,13 +22,11 @@ export const openResponseConsoleFromEndpointList = (): void => {
};
export const inputConsoleCommand = (command: string): void => {
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture').click().type(command);
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture').type(command);
};
export const clearConsoleCommandInput = (): void => {
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture')
.click()
.type(`{selectall}{backspace}`);
cy.getByTestSubj('endpointResponseActionsConsole-inputCapture').type(`{selectall}{backspace}`);
};
export const selectCommandFromHelpMenu = (command: string): void => {

View file

@ -6,8 +6,13 @@
*/
export const runEndpointLoaderScript = () => {
const { ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD, ELASTICSEARCH_URL, KIBANA_URL } =
Cypress.env();
const {
ELASTICSEARCH_USERNAME,
ELASTICSEARCH_PASSWORD,
ELASTICSEARCH_URL,
KIBANA_URL,
IS_SERVERLESS,
} = Cypress.env();
const ES_URL = new URL(ELASTICSEARCH_URL);
ES_URL.username = ELASTICSEARCH_USERNAME;
@ -16,8 +21,23 @@ export const runEndpointLoaderScript = () => {
const KBN_URL = new URL(KIBANA_URL);
KBN_URL.username = ELASTICSEARCH_USERNAME;
KBN_URL.password = ELASTICSEARCH_PASSWORD;
// FIXME: remove use of cli script and use instead data loaders
const script = `node scripts/endpoint/resolver_generator.js --node="${ES_URL.toString()}" --kibana="${KBN_URL.toString()}" --delete --numHosts=1 --numDocs=1 --fleet --withNewUser=santaEndpoint:changeme --anc=1 --gen=1 --ch=1 --related=1 --relAlerts=1`;
const script =
`node scripts/endpoint/resolver_generator.js ` +
`--node="${ES_URL.toString()}" ` +
`--kibana="${KBN_URL.toString()}" ` +
`--delete ` +
`--numHosts=1 ` +
`--numDocs=1 ` +
`--fleet ` +
`--withNewUser=santaEndpoint:changeme ` +
`--anc=1 ` +
`--gen=1 ` +
`--ch=1 ` +
`--related=1 ` +
`--relAlerts=1 ` +
`--ssl=${IS_SERVERLESS}`;
cy.exec(script, { env: { NODE_TLS_REJECT_UNAUTHORIZED: 1 }, timeout: 180000 });
};

View file

@ -2,9 +2,6 @@
"extends": "../../../../../../tsconfig.base.json",
"include": [
"**/*",
"../cypress_endpoint.config.ts",
"../cypress.config.ts",
"./cypress.d.ts"
],
"exclude": [
"target/**/*"

View file

@ -1,62 +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 { defineCypressConfig } from '@kbn/cypress-config';
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
import { dataLoaders, dataLoadersForRealEndpoints } from './cypress/support/data_loaders';
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
import { responseActionTasks } from './cypress/support/response_actions';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
reporter: '../../../../node_modules/cypress-multi-reporters',
reporterOptions: {
configFile: './public/management/reporter_config.json',
},
defaultCommandTimeout: 60000,
execTimeout: 120000,
pageLoadTimeout: 12000,
retries: {
runMode: 1,
openMode: 0,
},
screenshotsFolder:
'../../../target/kibana-security-solution/public/management/cypress/screenshots',
trashAssetsBeforeRuns: false,
video: false,
viewportHeight: 900,
viewportWidth: 1440,
experimentalStudio: true,
env: {
'cypress-react-selector': {
root: '#security-solution-app',
},
KIBANA_USERNAME: 'system_indices_superuser',
KIBANA_PASSWORD: 'changeme',
ELASTICSEARCH_USERNAME: 'system_indices_superuser',
ELASTICSEARCH_PASSWORD: 'changeme',
},
e2e: {
experimentalMemoryManagement: true,
experimentalInteractiveRunEvents: true,
baseUrl: 'http://localhost:5620',
supportFile: 'public/management/cypress/support/e2e.ts',
specPattern: 'public/management/cypress/e2e/endpoint/*.cy.{js,jsx,ts,tsx}',
experimentalRunAllSpecs: true,
setupNodeEvents: (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {
dataLoaders(on, config);
// Data loaders specific to "real" Endpoint testing
dataLoadersForRealEndpoints(on, config);
responseActionTasks(on, config);
},
},
});

View file

@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { getLocalhostRealIp } from '@kbn/security-solution-plugin/scripts/endpoint/common/localhost_services';
import { FtrConfigProviderContext } from '@kbn/test';
import { ExperimentalFeatures } from '@kbn/security-solution-plugin/common/experimental_features';
import { DefendWorkflowsCypressCliTestRunner } from './runner';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const defendWorkflowsCypressConfig = await readConfigFile(require.resolve('./config.ts'));
const svlSharedConfig = await readConfigFile(
require.resolve('../../test_serverless/shared/config.base.ts')
);
const hostIp = getLocalhostRealIp();
const enabledFeatureFlags: Array<keyof ExperimentalFeatures> = [];
return {
...svlSharedConfig.getAll(),
esTestCluster: {
...svlSharedConfig.get('esTestCluster'),
serverArgs: [
...svlSharedConfig.get('esTestCluster.serverArgs'),
// define custom es server here
// API Keys is enabled at the top level
],
},
servers: {
...svlSharedConfig.get('servers'),
fleetserver: {
protocol: 'https',
hostname: hostIp,
port: 8220,
},
},
kbnTestServer: {
...svlSharedConfig.get('kbnTestServer'),
serverArgs: [
...svlSharedConfig.get('kbnTestServer.serverArgs'),
'--csp.strict=false',
'--csp.warnLegacyBrowsers=false',
'--serverless=security',
'--xpack.encryptedSavedObjects.encryptionKey="abcdefghijklmnopqrstuvwxyz123456"',
'--xpack.security.enabled=true',
`--xpack.fleet.agents.fleet_server.hosts=["https://${hostIp}:8220"]`,
`--xpack.fleet.agents.elasticsearch.host=http://${hostIp}:${defendWorkflowsCypressConfig.get(
'servers.elasticsearch.port'
)}`,
// set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts
'--xpack.securitySolution.packagerTaskInterval=5s',
`--xpack.securitySolution.enableExperimental=${JSON.stringify(enabledFeatureFlags)}`,
],
},
testRunner: DefendWorkflowsCypressCliTestRunner,
};
}