[Security Solution][Serverless] Reusing Cypress tests for Serverless infrastructure (#162698)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Patryk Kopycinski <contact@patrykkopycinski.com>
Co-authored-by: Georgii Gorbachev <georgii.gorbachev@elastic.co>
This commit is contained in:
Gloria Hornero 2023-08-15 19:50:54 +02:00 committed by GitHub
parent e09de7dda4
commit f9c9722c6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
374 changed files with 4332 additions and 3679 deletions

View file

@ -46,6 +46,7 @@ disabled:
- x-pack/plugins/apm/ftr_e2e/ftr_config.ts
- x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts
- x-pack/test_serverless/functional/test_suites/observability/cypress/config_runner.ts
- x-pack/test/security_solution_cypress/serverless_config.ts
- x-pack/plugins/profiling/e2e/ftr_config_open.ts
- x-pack/plugins/profiling/e2e/ftr_config_runner.ts
- x-pack/plugins/profiling/e2e/ftr_config.ts

View file

@ -94,13 +94,14 @@ steps:
automatic:
- exit_status: '-1'
limit: 3
- command: .buildkite/scripts/steps/functional/security_serverless.sh
label: 'Serverless Security Cypress Tests'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 40
parallelism: 10
soft_fail:
- exit_status: 10
retry:
@ -110,8 +111,60 @@ steps:
- exit_status: '*'
limit: 1
artifact_paths:
- "target/kibana-security-serverless/**/*"
- "target/kibana-security-solution/**/*"
- command: .buildkite/scripts/steps/functional/security_serverless_defend_workflows.sh
label: 'Serverless Security Defend Workflows Cypress Tests'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 40
soft_fail:
- exit_status: 10
retry:
automatic:
- exit_status: '-1'
limit: 3
- exit_status: '*'
limit: 1
artifact_paths:
- "target/kibana-security-solution/**/*"
- command: .buildkite/scripts/steps/functional/security_serverless_investigations.sh
label: 'Serverless Security Investigations Cypress Tests'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 40
parallelism: 4
soft_fail:
- exit_status: 10
retry:
automatic:
- exit_status: '-1'
limit: 3
- exit_status: '*'
limit: 1
artifact_paths:
- "target/kibana-security-solution/**/*"
- command: .buildkite/scripts/steps/functional/security_serverless_explore.sh
label: 'Serverless Security Explore Cypress Tests'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 40
parallelism: 2
soft_fail:
- exit_status: 10
retry:
automatic:
- exit_status: '-1'
limit: 3
- exit_status: '*'
limit: 1
artifact_paths:
- "target/kibana-security-solution/**/*"
- command: .buildkite/scripts/steps/lint.sh
label: 'Linting'

View file

@ -11,15 +11,4 @@ steps:
- exit_status: '*'
limit: 1
artifact_paths:
- "target/kibana-security-solution/**/*"
- command: .buildkite/scripts/steps/functional/security_solution_burn.sh
label: 'Security Solution Cypress tests, burning changed specs'
agents:
queue: n2-4-spot
depends_on: build
timeout_in_minutes: 120
parallelism: 1
soft_fail: true
artifact_paths:
- "target/kibana-security-solution/**/*"
- "target/kibana-security-solution/**/*"

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Response Ops Cypress Tests on Security Solution"
yarn --cwd x-pack/plugins/security_solution cypress:run:respops
yarn --cwd x-pack/test/security_solution_cypress cypress:run:respops:ess

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Response Ops Cases Cypress Tests on Security Solution"
yarn --cwd x-pack/plugins/security_solution cypress:run:cases
yarn --cwd x-pack/test/security_solution_cypress cypress:run:cases:ess

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Security Serverless Cypress"
yarn --cwd x-pack/test_serverless/functional/test_suites/security/cypress cypress:run
yarn --cwd x-pack/test/security_solution_cypress cypress:run:serverless

View file

@ -0,0 +1,13 @@
#!/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-serverless-security-cypress
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Security Defend Workflows Serverless Cypress"
yarn --cwd x-pack/test_serverless/functional/test_suites/security/cypress cypress:run

View file

@ -0,0 +1,13 @@
#!/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-security-solution-chrome
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Explore Cypress Tests on Serverless"
yarn --cwd x-pack/test/security_solution_cypress cypress:explore:run:serverless

View file

@ -0,0 +1,13 @@
#!/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-security-solution-chrome
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Investigations Cypress Tests on Serverless"
yarn --cwd x-pack/test/security_solution_cypress cypress:investigations:run:serverless

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Security Solution Cypress tests (Chrome)"
yarn --cwd x-pack/plugins/security_solution cypress:run
yarn --cwd x-pack/test/security_solution_cypress cypress:run:ess

View file

@ -12,4 +12,4 @@ buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" 'fals
echo "--- Security Solution Cypress tests, burning changed specs (Chrome)"
yarn --cwd x-pack/plugins/security_solution cypress:changed-specs-only
yarn --cwd x-pack/test/security_solution_cypress cypress:changed-specs-only:ess

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Explore Cypress Tests on Security Solution"
yarn --cwd x-pack/plugins/security_solution cypress:explore:run
yarn --cwd x-pack/test/security_solution_cypress cypress:explore:run:ess

View file

@ -10,4 +10,4 @@ export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
echo "--- Investigations Cypress Tests on Security Solution"
yarn --cwd x-pack/plugins/security_solution cypress:investigations:run
yarn --cwd x-pack/test/security_solution_cypress cypress:investigations:run:ess

49
.github/CODEOWNERS vendored
View file

@ -1087,7 +1087,7 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/common/search_strategy/timeline @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/common/types/timeline @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/cypress/e2e/investigations @elastic/security-threat-hunting-investigations
/x-pack/test/security_solution_cypress/cypress/e2e/investigations @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/common/components/alerts_viewer @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_action @elastic/security-threat-hunting-investigations
@ -1111,12 +1111,11 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/common/search_strategy/security_solution/network @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/common/search_strategy/security_solution/user @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/e2e/explore @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/screens/hosts @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/screens/network @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/tasks/hosts @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/tasks/network @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/cypress/upgrade_e2e/threat_hunting/cases @elastic/security-threat-hunting-explore
/x-pack/test/security_solution_cypress/cypress/e2e/explore @elastic/security-threat-hunting-explore
/x-pack/test/security_solution_cypress/cypress/screens/hosts @elastic/security-threat-hunting-explore
/x-pack/test/security_solution_cypress/cypress/screens/network @elastic/security-threat-hunting-explore
/x-pack/test/security_solution_cypress/cypress/tasks/hosts @elastic/security-threat-hunting-explore
/x-pack/test/security_solution_cypress/cypress/tasks/network @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour @elastic/security-threat-hunting-explore
/x-pack/plugins/security_solution/public/common/components/charts @elastic/security-threat-hunting-explore
@ -1168,8 +1167,8 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/common/detection_engine/rule_management @elastic/security-detection-rule-management
/x-pack/plugins/security_solution/common/detection_engine/rule_monitoring @elastic/security-detection-rule-management
/x-pack/plugins/security_solution/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management
/x-pack/plugins/security_solution/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules @elastic/security-detection-rule-management
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management @elastic/security-detection-rule-management
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management
/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management
@ -1216,12 +1215,12 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index @elastic/security-detection-engine
/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/data_sources @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/detection_response/value_lists @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/entity_analytics @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/exceptions @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/e2e/overview @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/data_sources @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/value_lists @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/exceptions @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/e2e/overview @elastic/security-detection-engine
/x-pack/plugins/security_solution/common/detection_engine/rule_exceptions @elastic/security-detection-engine
@ -1240,14 +1239,14 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib
/x-pack/plugins/security_solution/public/common/components/threat_match @elastic/security-detection-engine
## Security Solution cross teams ownership
/x-pack/plugins/security_solution/cypress/fixtures @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/helpers @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/e2e/detection_rules @elastic/security-detection-rule-management @elastic/security-detection-engine
/x-pack/plugins/security_solution/cypress/objects @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/plugins @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/screens/common @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/support @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/plugins/security_solution/cypress/urls @elastic/security-threat-hunting-investigations @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/fixtures @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/helpers @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/e2e/detection_rules @elastic/security-detection-rule-management @elastic/security-detection-engine
/x-pack/test/security_solution_cypress/cypress/objects @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/plugins @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/screens/common @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/support @elastic/security-detections-response @elastic/security-threat-hunting
/x-pack/test/security_solution_cypress/cypress/urls @elastic/security-threat-hunting-investigations @elastic/security-detection-engine
/x-pack/plugins/security_solution/common/ecs @elastic/security-threat-hunting-investigations
/x-pack/plugins/security_solution/common/test @elastic/security-detection-rule-management @elastic/security-detection-engine
@ -1283,9 +1282,7 @@ x-pack/plugins/security_solution/server/usage/ @elastic/security-data-analytics
x-pack/plugins/security_solution/server/lib/telemetry/ @elastic/security-data-analytics
## Security Solution sub teams - security-engineering-productivity
x-pack/plugins/security_solution/cypress/ccs_e2e @elastic/security-engineering-productivity
x-pack/plugins/security_solution/cypress/upgrade_e2e @elastic/security-engineering-productivity
x-pack/plugins/security_solution/cypress/README.md @elastic/security-engineering-productivity
x-pack/test/security_solution_cypress/cypress/README.md @elastic/security-engineering-productivity
x-pack/test/security_solution_cypress @elastic/security-engineering-productivity
## Security Solution sub teams - adaptive-workload-protection

View file

@ -5,8 +5,8 @@
"private": true,
"license": "Elastic License 2.0",
"scripts": {
"cypress:open": "node ../security_solution/scripts/start_cypress_parallel open --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/osquery_cypress/cli_config",
"cypress:run": "node ../security_solution/scripts/start_cypress_parallel run --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/osquery_cypress/cli_config --concurrency 1",
"cypress:open": "node ../security_solution/scripts/start_cypress_parallel open --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../x-pack/test/osquery_cypress/cli_config",
"cypress:run": "node ../security_solution/scripts/start_cypress_parallel run --config-file ../osquery/cypress.config.ts --ftr-config-file ../../../x-pack/test/osquery_cypress/cli_config --concurrency 1",
"nyc": "../../../node_modules/.bin/nyc report --reporter=text-summary"
}
}

View file

@ -1,193 +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 { ROLES } from '../../../common/test';
import { DETECTIONS_RULE_MANAGEMENT_URL, ALERTS_URL } from '../../urls/navigation';
import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts';
const loadPageAsPlatformEngineerUser = (url: string) => {
login(ROLES.soc_manager);
waitForPageWithoutDateRange(url, ROLES.soc_manager);
waitForPageTitleToBeShown();
};
const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
describe('Detections > Need Admin Callouts indicating an admin is needed to migrate the alert data set', () => {
const NEED_ADMIN_FOR_UPDATE_CALLOUT = 'need-admin-for-update-rules';
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.
// Otherwise the app will be disabled and show a "welcome"-like page.
login();
visitWithoutDateRange(ALERTS_URL);
waitForPageTitleToBeShown();
});
context(
'The users index_mapping_outdated is "true" and their admin callouts should show up',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', (req) => {
req.reply((res) => {
res.send(200, {
index_mapping_outdated: true,
name: '.alerts-security.alerts-default',
});
});
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
}
);
context(
'The users index_mapping_outdated is "false" and their admin callouts should not show up ',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', {
index_mapping_outdated: false,
name: '.alerts-security.alerts-default',
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
}
);
context(
'The users index_mapping_outdated is "null" and their admin callouts should not show up ',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', {
index_mapping_outdated: null,
name: '.alerts-security.alerts-default',
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
}
);
});

View file

@ -1,123 +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 { APP_PATH, RULES_ADD_PATH, RULES_UPDATES } from '../../../../common/constants';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common';
import { login, waitForPageWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import { ROLES } from '../../../../common/test';
import {
ADD_ELASTIC_RULES_BTN,
getInstallSingleRuleButtonByRuleId,
getUpgradeSingleRuleButtonByRuleId,
INSTALL_ALL_RULES_BUTTON,
RULES_UPDATES_TAB,
RULE_CHECKBOX,
UPGRADE_ALL_RULES_BUTTON,
} from '../../../screens/alerts_detection_rules';
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
const loadPageAsReadOnlyUser = (url: string) => {
login(ROLES.reader);
waitForPageWithoutDateRange(url, ROLES.reader);
};
describe('Detection rules, Prebuilt Rules Installation and Update - Authorization/RBAC', () => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
waitForRulesTableToBeLoaded();
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
});
describe('User with read privileges on Security Solution', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
// Now login with read-only user in preparation for test
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('should not be able to install prebuilt rules', () => {
// Check that Add Elastic Rules button is disabled
cy.get(ADD_ELASTIC_RULES_BTN).should('be.disabled');
// Navigate to Add Elastic Rules page anyways via URL
// and assert that rules cannot be selected and all
// installation buttons are disabled
cy.visit(`${APP_PATH}${RULES_ADD_PATH}`);
cy.get(INSTALL_ALL_RULES_BUTTON).should('be.disabled');
cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).should(
'not.exist'
);
cy.get(RULE_CHECKBOX).should('not.exist');
});
});
describe('User with read privileges on Security Solution', () => {
beforeEach(() => {
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
// Now login with read-only user in preparation for test
loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('should not be able to upgrade prebuilt rules', () => {
// Check that Rule Update tab is not shown
cy.get(RULES_UPDATES_TAB).should('not.exist');
// Navigate to Rule Update tab anyways via URL
// and assert that rules cannot be selected and all
// upgrade buttons are disabled
cy.visit(`${APP_PATH}${RULES_UPDATES}`);
cy.get(UPGRADE_ALL_RULES_BUTTON).should('be.disabled');
cy.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id)).should(
'not.exist'
);
cy.get(RULE_CHECKBOX).should('not.exist');
});
});
});

View file

@ -1,142 +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 { createRuleAssetSavedObject } from '../../../helpers/rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import {
addElasticRulesButtonClick,
assertRuleAvailableForInstallAndInstallOne,
assertRuleAvailableForInstallAndInstallSelected,
assertRuleAvailableForInstallAndInstallAllInPage,
assertRuleAvailableForInstallAndInstallAll,
assertRuleUpgradeAvailableAndUpgradeOne,
assertRuleUpgradeAvailableAndUpgradeSelected,
assertRuleUpgradeAvailableAndUpgradeAllInPage,
assertRuleUpgradeAvailableAndUpgradeAll,
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
describe('Detection rules, Prebuilt Rules Installation and Update - Error handling', () => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
describe('Installation of prebuilt rules - Should fail gracefully with toast error message when', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
waitForRulesTableToBeLoaded();
});
it('installing prebuilt rules one by one', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1], didRequestFail: true });
});
it('installing multiple selected prebuilt rules by selecting them individually', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallSelected({
rules: [RULE_1, RULE_2],
didRequestFail: true,
});
});
it('installing multiple selected prebuilt rules by selecting all in page', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAllInPage({
rules: [RULE_1, RULE_2],
didRequestFail: true,
});
});
it('installing all available rules at once', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAll({ rules: [RULE_1, RULE_2], didRequestFail: true });
});
});
describe('Update of prebuilt rules - Should fail gracefully with toast error message when', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
beforeEach(() => {
/* Create a new rule and install it */
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
waitForRulesTableToBeLoaded();
reload();
});
it('upgrading prebuilt rules one by one', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1], didRequestFail: true });
});
it('upgrading multiple selected prebuilt rules by selecting them individually', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeSelected({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
it('upgrading multiple selected prebuilt rules by selecting all in page', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAllInPage({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
it('upgrading all rules with available upgrades at once', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
});
});

View file

@ -1,262 +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 { BulkInstallPackageInfo } from '@kbn/fleet-plugin/common';
import type { Rule } from '../../../../public/detection_engine/rule_management/logic/types';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import {
GO_BACK_TO_RULES_TABLE_BUTTON,
INSTALL_ALL_RULES_BUTTON,
INSTALL_SELECTED_RULES_BUTTON,
NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE,
NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE,
RULES_UPDATES_TAB,
RULE_CHECKBOX,
SELECT_ALL_RULES_ON_PAGE_CHECKBOX,
TOASTER,
} from '../../../screens/alerts_detection_rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import {
getRuleAssets,
createAndInstallMockedPrebuiltRules,
} from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import {
addElasticRulesButtonClick,
assertRuleAvailableForInstallAndInstallOne,
assertRuleAvailableForInstallAndInstallSelected,
assertRuleAvailableForInstallAndInstallAllInPage,
assertRuleAvailableForInstallAndInstallAll,
assertRuleUpgradeAvailableAndUpgradeOne,
assertRuleUpgradeAvailableAndUpgradeSelected,
assertRuleUpgradeAvailableAndUpgradeAllInPage,
assertRuleUpgradeAvailableAndUpgradeAll,
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
describe('Detection rules, Prebuilt Rules Installation and Update workflow', () => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
describe('Installation of prebuilt rules package via Fleet', () => {
beforeEach(() => {
cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk');
cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as(
'installPackage'
);
waitForRulesTableToBeLoaded();
});
it('should install package from Fleet in the background', () => {
/* Assert that the package in installed from Fleet */
cy.wait('@installPackageBulk', {
timeout: 60000,
}).then(({ response: bulkResponse }) => {
cy.wrap(bulkResponse?.statusCode).should('eql', 200);
const packages = bulkResponse?.body.items.map(
({ name, result }: BulkInstallPackageInfo) => ({
name,
})
);
const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name);
// Under normal flow the package is installed via the Fleet bulk install API.
// However, for testing purposes the package can be installed via the Fleet individual install API,
// so we need to intercept and wait for that request as well.
if (!packagesBulkInstalled.includes('security_detection_engine')) {
// Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set
cy.wait('@installPackage').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
cy.wrap(response?.body)
.should('have.property', 'items')
.should('have.length.greaterThan', 0);
});
} else {
// Normal flow, install via the Fleet bulk install API
expect(packages.length).to.have.greaterThan(0);
// At least one of the packages installed should be the security_detection_engine package
expect(packages).to.satisfy((pckgs: BulkInstallPackageInfo[]) =>
pckgs.some((pkg) => pkg.name === 'security_detection_engine')
);
}
});
});
it('should install rules from the Fleet package when user clicks on CTA', () => {
const getRulesAndAssertNumberInstalled = () => {
getRuleAssets().then((response) => {
const ruleIds = response.body.hits.hits.map(
(hit: { _source: { ['security-rule']: Rule } }) => hit._source['security-rule'].rule_id
);
const numberOfRulesToInstall = new Set(ruleIds).size;
addElasticRulesButtonClick();
cy.get(INSTALL_ALL_RULES_BUTTON).should('be.enabled').click();
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${numberOfRulesToInstall} rules installed successfully.`);
});
};
/* Retrieve how many rules were installed from the Fleet package */
/* See comments in test above for more details */
cy.wait('@installPackageBulk', {
timeout: 60000,
}).then(({ response: bulkResponse }) => {
cy.wrap(bulkResponse?.statusCode).should('eql', 200);
const packagesBulkInstalled = bulkResponse?.body.items.map(
({ name }: { name: string }) => name
);
if (!packagesBulkInstalled.includes('security_detection_engine')) {
cy.wait('@installPackage').then(() => {
getRulesAndAssertNumberInstalled();
});
} else {
getRulesAndAssertNumberInstalled();
}
});
});
});
describe('Installation of prebuilt rules', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
waitForRulesTableToBeLoaded();
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as(
'installPrebuiltRules'
);
});
it('should install prebuilt rules one by one', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1] });
});
it('should install multiple selected prebuilt rules by selecting them individually', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallSelected({ rules: [RULE_1, RULE_2] });
});
it('should install multiple selected prebuilt rules by selecting all in page', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAllInPage({ rules: [RULE_1, RULE_2] });
});
it('should install all available rules at once', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAll({ rules: [RULE_1, RULE_2] });
});
it('should display an empty screen when all available prebuilt rules have been installed', () => {
addElasticRulesButtonClick();
cy.get(INSTALL_ALL_RULES_BUTTON).click();
cy.get(TOASTER).should('be.visible').should('have.text', `2 rules installed successfully.`);
cy.get(RULE_CHECKBOX).should('not.exist');
cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE).should('exist');
cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).should('exist');
});
it('should fail gracefully with toast error message when request to install rules fails', () => {
/* Stub request to force rules installation to fail */
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', {
statusCode: 500,
}).as('installPrebuiltRules');
addElasticRulesButtonClick();
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
cy.wait('@installPrebuiltRules');
cy.get(TOASTER).should('be.visible').should('have.text', 'Rule installation failed');
});
});
describe('Upgrade of prebuilt rules', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
beforeEach(() => {
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as(
'updatePrebuiltRules'
);
/* Create a new rule and install it */
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
waitForRulesTableToBeLoaded();
reload();
});
it('should upgrade prebuilt rules one by one', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1] });
});
it('should upgrade multiple selected prebuilt rules by selecting them individually', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeSelected({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
});
it('should upgrade multiple selected prebuilt rules by selecting all in page', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAllInPage({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
});
it('should upgrade all rules with available upgrades at once', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should display an empty screen when all rules with available updates have been upgraded', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
cy.get(RULES_UPDATES_TAB).should('not.exist');
cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE).should('exist');
});
});
});

View file

@ -1,183 +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 { createRuleAssetSavedObject } from '../../../helpers/rules';
import { ADD_ELASTIC_RULES_BTN, RULES_UPDATES_TAB } from '../../../screens/alerts_detection_rules';
import {
deleteFirstRule,
waitForRulesTableToBeLoaded,
} from '../../../tasks/alerts_detection_rules';
import {
installAllPrebuiltRulesRequest,
createAndInstallMockedPrebuiltRules,
} from '../../../tasks/api_calls/prebuilt_rules';
import {
resetRulesTableState,
deleteAlertsAndRules,
reload,
deletePrebuiltRulesAssets,
} from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
describe('Detection rules, Prebuilt Rules Installation and Update Notifications', () => {
beforeEach(() => {
login();
/* Make sure persisted rules table state is cleared */
resetRulesTableState();
deleteAlertsAndRules();
deletePrebuiltRulesAssets();
});
describe('No notifications', () => {
it('should NOT display install or update notifications when no prebuilt assets and no rules are installed', () => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
// TODO: test plan asserts "should NOT see a CTA to install prebuilt rules"
// but current behavior is to always show the CTA, even with no prebuilt rule assets installed
// Update that behaviour and then update this test.
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should NOT display install or update notifications when latest rules are installed', () => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: true });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
/* Assert that there are no installation or update notifications */
/* Add Elastic Rules button should not contain a number badge */
/* and Rule Upgrade tab should not be displayed */
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', 'Add Elastic rules');
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
});
describe('Notifications', () => {
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: false });
});
describe('Rules installation notification when no rules have been installed', () => {
beforeEach(() => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
it('should notify user about prebuilt rules available for installation', () => {
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
});
describe('Rule installation notification when at least one rule already installed', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule assets with a different rule_id as the one that was */
/* installed before in order to trigger the installation notification */
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
const RULE_3 = createRuleAssetSavedObject({
name: 'Test rule 3',
rule_id: 'rule_3',
});
createAndInstallMockedPrebuiltRules({ rules: [RULE_2, RULE_3], installToKibana: false });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
});
it('should notify user about prebuilt rules available for installation', () => {
const numberOfAvailableRules = 2;
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should(
'have.text',
`Add Elastic rules${numberOfAvailableRules}`
);
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should notify user a rule is again available for installation if it is deleted', () => {
/* Install available rules, assert that the notification is gone */
/* then delete one rule and assert that the notification is back */
installAllPrebuiltRulesRequest().then(() => {
reload();
deleteFirstRule();
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
});
});
});
describe('Rule update notification', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule asset with the same rule_id as the one that was installed */
/* but with a higher version, in order to trigger the update notification */
const UPDATED_RULE = createRuleAssetSavedObject({
name: 'Test rule 1.1 (updated)',
rule_id: 'rule_1',
version: 2,
});
createAndInstallMockedPrebuiltRules({ rules: [UPDATED_RULE], installToKibana: false });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
reload();
});
});
it('should notify user about prebuilt rules package available for update', () => {
// No rules available for installation
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules`);
// But 1 rule available for update
cy.get(RULES_UPDATES_TAB).should('be.visible');
cy.get(RULES_UPDATES_TAB).should('have.text', `Rule Updates${1}`);
});
});
describe('Rule installation available and rule update available notifications', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule assets with a different rule_id as the one that was */
/* installed before in order to trigger the installation notification */
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
/* Create new rule asset with the same rule_id as the one that was installed */
/* but with a higher version, in order to trigger the update notification */
const UPDATED_RULE = createRuleAssetSavedObject({
name: 'Test rule 1.1 (updated)',
rule_id: 'rule_1',
version: 2,
});
createAndInstallMockedPrebuiltRules({
rules: [RULE_2, UPDATED_RULE],
installToKibana: false,
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
});
it('should notify user about prebuilt rules available for installation and for upgrade', () => {
// 1 rule available for installation
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
// 1 rule available for update
cy.get(RULES_UPDATES_TAB).should('be.visible');
cy.get(RULES_UPDATES_TAB).should('have.text', `Rule Updates${1}`);
});
});
});
});

View file

@ -1,72 +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 { getIndexConnector } from '../../../objects/connector';
import { getSimpleCustomQueryRule } from '../../../objects/rule';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import { deleteIndex, waitForNewDocumentToBeIndexed } from '../../../tasks/api_calls/elasticsearch';
import {
cleanKibana,
deleteAlertsAndRules,
deleteConnectors,
deleteDataView,
} from '../../../tasks/common';
import {
createAndEnableRule,
fillAboutRuleAndContinue,
fillDefineCustomRuleAndContinue,
fillRuleAction,
fillScheduleRuleAndContinue,
} from '../../../tasks/create_new_rule';
import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Rule actions during detection rule creation', () => {
const indexConnector = getIndexConnector();
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
deleteAlertsAndRules();
deleteConnectors();
deleteIndex(indexConnector.index);
deleteDataView(indexConnector.index);
});
const rule = getSimpleCustomQueryRule();
const actions = { connectors: [indexConnector] };
const index = actions.connectors[0].index;
const initialNumberOfDocuments = 0;
const expectedJson = JSON.parse(actions.connectors[0].document);
it('Indexes a new document after the index action is triggered ', function () {
visit(RULE_CREATION);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
fillRuleAction(actions);
createAndEnableRule();
goToRuleDetails();
/* When the rule is executed, the action is triggered. We wait for the new document to be indexed */
waitForNewDocumentToBeIndexed(index, initialNumberOfDocuments);
/* We assert that the new indexed document is the one set on the index action */
cy.request({
method: 'GET',
url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`,
headers: { 'kbn-xsrf': 'cypress-creds' },
}).then((response) => {
expect(response.body.hits.hits[0]._source).to.deep.equal(expectedJson);
});
});
});

View file

@ -1,221 +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 { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types';
import { ROLES } from '../../../../../../common/test';
import {
RULES_BULK_EDIT_ACTIONS_INFO,
RULES_BULK_EDIT_ACTIONS_WARNING,
ADD_RULE_ACTIONS_MENU_ITEM,
} from '../../../../../screens/rules_bulk_actions';
import { actionFormSelector } from '../../../../../screens/common/rule_actions';
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common';
import type { RuleActionCustomFrequency } from '../../../../../tasks/common/rule_actions';
import {
addSlackRuleAction,
assertSlackRuleAction,
addEmailConnectorAndRuleAction,
assertEmailRuleAction,
assertSelectedCustomFrequencyOption,
assertSelectedPerRuleRunFrequencyOption,
assertSelectedSummaryOfAlertsOption,
pickCustomFrequencyOption,
pickPerRuleRunFrequencyOption,
pickSummaryOfAlertsOption,
} from '../../../../../tasks/common/rule_actions';
import {
waitForRulesTableToBeLoaded,
selectNumberOfRules,
goToEditRuleActionsSettingsOf,
} from '../../../../../tasks/alerts_detection_rules';
import {
waitForBulkEditActionToFinish,
submitBulkEditForm,
checkOverwriteRuleActionsCheckbox,
openBulkEditRuleActionsForm,
openBulkActionsMenu,
} from '../../../../../tasks/rules_bulk_actions';
import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation';
import { createRule } from '../../../../../tasks/api_calls/rules';
import { createSlackConnector } from '../../../../../tasks/api_calls/connectors';
import {
getEqlRule,
getNewThreatIndicatorRule,
getNewRule,
getNewThresholdRule,
getMachineLearningRule,
getNewTermsRule,
} from '../../../../../objects/rule';
import { excessivelyInstallAllPrebuiltRules } from '../../../../../tasks/api_calls/prebuilt_rules';
const ruleNameToAssert = 'Custom rule name with actions';
const expectedNumberOfCustomRulesToBeEdited = 7;
// 7 custom rules of different types + 3 prebuilt.
// number of selected rules doesn't matter, we only want to make sure they will be edited an no modal window displayed as for other actions
const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 3;
const expectedExistingSlackMessage = 'Existing slack action';
const expectedSlackMessage = 'Slack action test message';
// TODO: Fix flakiness and unskip https://github.com/elastic/kibana/issues/154721
describe.skip('Detection rules, bulk edit of rule actions', () => {
before(() => {
cleanKibana();
login();
});
beforeEach(() => {
deleteAlertsAndRules();
deleteConnectors();
cy.task('esArchiverResetKibana');
createSlackConnector().then(({ body }) => {
const actions: RuleActionArray = [
{
id: body.id,
action_type_id: '.slack',
group: 'default',
params: {
message: expectedExistingSlackMessage,
},
frequency: {
summary: true,
throttle: null,
notifyWhen: 'onActiveAlert',
},
},
];
createRule(getNewRule({ name: ruleNameToAssert, rule_id: '1', max_signals: 500, actions }));
});
createRule(getEqlRule({ rule_id: '2' }));
createRule(getMachineLearningRule({ rule_id: '3' }));
createRule(getNewThreatIndicatorRule({ rule_id: '4' }));
createRule(getNewThresholdRule({ rule_id: '5' }));
createRule(getNewTermsRule({ rule_id: '6' }));
createRule(getNewRule({ saved_id: 'mocked', rule_id: '7' }));
createSlackConnector();
});
context('Restricted action privileges', () => {
it("User with no privileges can't add rule actions", () => {
login(ROLES.hunter_no_actions);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.hunter_no_actions);
waitForRulesTableToBeLoaded();
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkActionsMenu();
cy.get(ADD_RULE_ACTIONS_MENU_ITEM).should('be.disabled');
});
});
context('All actions privileges', () => {
beforeEach(() => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add a rule action to rules (existing connector)', () => {
const expectedActionFrequency: RuleActionCustomFrequency = {
throttle: 1,
throttleUnit: 'd',
};
excessivelyInstallAllPrebuiltRules();
// select both custom and prebuilt rules
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
openBulkEditRuleActionsForm();
// ensure rule actions info callout displayed on the form
cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible');
addSlackRuleAction(expectedSlackMessage);
pickSummaryOfAlertsOption();
pickCustomFrequencyOption(expectedActionFrequency);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedCustomFrequencyOption(expectedActionFrequency, 1);
assertSlackRuleAction(expectedExistingSlackMessage, 0);
assertSlackRuleAction(expectedSlackMessage, 1);
// ensure there is no third action
cy.get(actionFormSelector(2)).should('not.exist');
});
it('Overwrite rule actions in rules', () => {
excessivelyInstallAllPrebuiltRules();
// select both custom and prebuilt rules
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
openBulkEditRuleActionsForm();
addSlackRuleAction(expectedSlackMessage);
pickSummaryOfAlertsOption();
pickPerRuleRunFrequencyOption();
// check overwrite box, ensure warning is displayed
checkOverwriteRuleActionsCheckbox();
cy.get(RULES_BULK_EDIT_ACTIONS_WARNING).contains(
`You're about to overwrite rule actions for ${expectedNumberOfRulesToBeEdited} selected rules`
);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedPerRuleRunFrequencyOption();
assertSlackRuleAction(expectedSlackMessage);
// ensure existing action was overwritten
cy.get(actionFormSelector(1)).should('not.exist');
});
it('Add a rule action to rules (new connector)', () => {
const expectedActionFrequency: RuleActionCustomFrequency = {
throttle: 2,
throttleUnit: 'h',
};
const expectedEmail = 'test@example.com';
const expectedSubject = 'Subject';
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditRuleActionsForm();
addEmailConnectorAndRuleAction(expectedEmail, expectedSubject);
pickSummaryOfAlertsOption();
pickCustomFrequencyOption(expectedActionFrequency);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedCustomFrequencyOption(expectedActionFrequency, 1);
assertEmailRuleAction(expectedEmail, expectedSubject);
});
});
});

View file

@ -1,242 +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 {
RULES_BULK_EDIT_DATA_VIEWS_WARNING,
RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX,
} from '../../../../../screens/rules_bulk_actions';
import { DATA_VIEW_DETAILS, INDEX_PATTERNS_DETAILS } from '../../../../../screens/rule_details';
import {
waitForRulesTableToBeLoaded,
goToRuleDetails,
selectNumberOfRules,
goToTheRuleDetailsOf,
} from '../../../../../tasks/alerts_detection_rules';
import {
typeIndexPatterns,
waitForBulkEditActionToFinish,
submitBulkEditForm,
checkOverwriteDataViewCheckbox,
checkOverwriteIndexPatternsCheckbox,
openBulkEditAddIndexPatternsForm,
openBulkEditDeleteIndexPatternsForm,
} from '../../../../../tasks/rules_bulk_actions';
import {
hasIndexPatterns,
getDetails,
assertDetailsNotExist,
} from '../../../../../tasks/rule_details';
import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation';
import { createRule } from '../../../../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../../../../tasks/common';
import {
getEqlRule,
getNewThreatIndicatorRule,
getNewRule,
getNewThresholdRule,
getNewTermsRule,
} from '../../../../../objects/rule';
const DATA_VIEW_ID = 'auditbeat';
const expectedIndexPatterns = ['index-1-*', 'index-2-*'];
const expectedNumberOfCustomRulesToBeEdited = 6;
describe('Bulk editing index patterns of rules with a data view only', () => {
before(() => {
cleanKibana();
});
beforeEach(() => {
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
login();
postDataView(DATA_VIEW_ID);
createRule(getNewRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' }));
createRule(getEqlRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '2' }));
createRule(
getNewThreatIndicatorRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '3' })
);
createRule(getNewThresholdRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '4' }));
createRule(getNewTermsRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '5' }));
createRule(
getNewRule({ index: undefined, data_view_id: DATA_VIEW_ID, saved_id: 'mocked', rule_id: '6' })
);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add index patterns to custom rules with configured data view: all rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Add index patterns to custom rules with configured data view when data view checkbox is checked: rules are updated', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
// click on data view overwrite checkbox, ensure warning is displayed
cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('not.exist');
checkOverwriteDataViewCheckbox();
cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('be.visible');
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been updated with index patterns and data view does not exist
goToRuleDetails();
hasIndexPatterns(expectedIndexPatterns.join(''));
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is NOT checked:: rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is checked: rules are updated', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been overwritten with index patterns and data view does not exist
goToRuleDetails();
hasIndexPatterns(expectedIndexPatterns.join(''));
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
it('Delete index patterns in custom rules with configured data view: rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditDeleteIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
// in delete form data view checkbox is absent
cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX).should('not.exist');
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
});
});
describe('Bulk editing index patterns of rules with index patterns and rules with a data view', () => {
const customRulesNumber = 2;
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
postDataView(DATA_VIEW_ID);
createRule(
getNewRule({ name: 'with dataview', index: [], data_view_id: DATA_VIEW_ID, rule_id: '1' })
);
createRule(getNewRule({ name: 'no data view', index: ['test-index-1-*'], rule_id: '2' }));
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => {
selectNumberOfRules(customRulesNumber);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 1,
skippedCount: 1,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToTheRuleDetailsOf('with dataview');
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => {
selectNumberOfRules(customRulesNumber);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 2,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
});

View file

@ -1,121 +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 { deleteAlertsAndRules } from '../../../tasks/common';
import {
expandFirstAlert,
goToClosedAlertsOnRuleDetailsPage,
openAddEndpointExceptionFromAlertActionButton,
openAddEndpointExceptionFromFirstAlert,
waitForAlerts,
} from '../../../tasks/alerts';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { getEndpointRule } from '../../../objects/rule';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import { createRule } from '../../../tasks/api_calls/rules';
import {
waitForAlertsToPopulate,
waitForTheRuleToBeExecuted,
} from '../../../tasks/create_new_rule';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
import {
addExceptionEntryFieldValueAndSelectSuggestion,
addExceptionEntryFieldValueValue,
addExceptionFlyoutItemName,
editExceptionFlyoutItemName,
selectCloseSingleAlerts,
submitNewExceptionItem,
validateExceptionConditionField,
} from '../../../tasks/exceptions';
import { ALERTS_COUNT } from '../../../screens/alerts';
import {
ADD_AND_BTN,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_ITEM_VIEWER_CONTAINER,
} from '../../../screens/exceptions';
import { goToEndpointExceptionsTab } from '../../../tasks/rule_details';
describe('Endpoint Exceptions workflows from Alert', () => {
const ITEM_NAME = 'Sample Exception List Item';
const ITEM_NAME_EDIT = 'Sample Exception List Item';
const ADDITIONAL_ENTRY = 'host.hostname';
beforeEach(() => {
cy.task('esArchiverUnload', 'endpoint');
cy.task('esArchiverResetKibana');
login();
deleteAlertsAndRules();
cy.task('esArchiverLoad', 'endpoint');
createRule(getEndpointRule());
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
});
after(() => {
cy.task('esArchiverUnload', 'endpoint');
});
it('Should be able to create and close single Endpoint exception from overflow menu', () => {
// The Endpoint will populated with predefined fields
openAddEndpointExceptionFromFirstAlert();
// As the endpoint.alerts-* is used to trigger the alert the
// file.Ext.code_signature will be auto-populated
validateExceptionConditionField('file.Ext.code_signature');
selectCloseSingleAlerts();
addExceptionFlyoutItemName(ITEM_NAME);
submitNewExceptionItem();
// Instead of immediately checking if the Opened Alert has moved to the closed tab,
// use the waitForAlerts method to create a buffer, allowing the alerts some time to
// be moved to the Closed Alert tab.
waitForAlerts();
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
});
it('Should be able to create Endpoint exception from Alerts take action button, and change multiple exception items without resetting to initial auto-prefilled entries', () => {
// Open first Alert Summary
expandFirstAlert();
// The Endpoint should populated with predefined fields
openAddEndpointExceptionFromAlertActionButton();
// As the endpoint.alerts-* is used to trigger the alert the
// file.Ext.code_signature will be auto-populated
validateExceptionConditionField('file.Ext.code_signature');
addExceptionFlyoutItemName(ITEM_NAME);
cy.get(ADD_AND_BTN).click();
// edit conditions
addExceptionEntryFieldValueAndSelectSuggestion(ADDITIONAL_ENTRY, 6);
addExceptionEntryFieldValueValue('foo', 4);
// Change the name again
editExceptionFlyoutItemName(ITEM_NAME_EDIT);
// validate the condition is still "agent.name" or got rest after the name is changed
validateExceptionConditionField(ADDITIONAL_ENTRY);
selectCloseSingleAlerts();
submitNewExceptionItem();
// Endpoint Exception will move to Endpoint List under Exception tab of rule
goToEndpointExceptionsTab();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME_EDIT);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).contains('span', ADDITIONAL_ENTRY);
});
});

View file

@ -1,194 +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 { LOADING_INDICATOR } from '../../../../screens/security_header';
import { getEndpointRule } from '../../../../objects/rule';
import { createRule } from '../../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../../tasks/alerts_detection_rules';
import {
addExceptionFromFirstAlert,
expandFirstAlert,
openAddRuleExceptionFromAlertActionButton,
} from '../../../../tasks/alerts';
import {
addExceptionEntryFieldValue,
addExceptionEntryFieldValueValue,
addExceptionFlyoutItemName,
submitNewExceptionItem,
validateExceptionConditionField,
validateExceptionCommentCountAndText,
editExceptionFlyoutItemName,
validateHighlightedFieldsPopulatedAsExceptionConditions,
validateEmptyExceptionConditionField,
} from '../../../../tasks/exceptions';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { goToExceptionsTab } from '../../../../tasks/rule_details';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
import { deleteAlertsAndRules } from '../../../../tasks/common';
import {
ADD_AND_BTN,
ENTRY_DELETE_BTN,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_ITEM_VIEWER_CONTAINER,
} from '../../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
describe('Auto populate exception with Alert data', () => {
const ITEM_NAME = 'Sample Exception Item';
const ITEM_NAME_EDIT = 'Sample Exception Item Edit';
const ADDITIONAL_ENTRY = 'host.hostname';
beforeEach(() => {
cy.task('esArchiverUnload', 'endpoint');
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', 'endpoint');
login();
createRule(getEndpointRule());
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();
});
after(() => {
cy.task('esArchiverUnload', 'endpoint');
deleteAlertsAndRules();
});
afterEach(() => {
cy.task('esArchiverUnload', 'endpoint');
});
it('Should create a Rule exception item from alert actions overflow menu and auto populate the conditions using alert Highlighted fields', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
addExceptionFromFirstAlert();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Validate that the comments are opened by default with one comment added
* showing a text contains information about the pre-filled conditions
*/
validateExceptionCommentCountAndText(
1,
'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):'
);
addExceptionFlyoutItemName(ITEM_NAME);
submitNewExceptionItem();
});
it('Should create a Rule exception from Alerts take action button and change multiple exception items without resetting to initial auto-prefilled entries', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
// Open first Alert Summary
expandFirstAlert();
// The Rule exception should populated with highlighted fields
openAddRuleExceptionFromAlertActionButton();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Validate that the comments are opened by default with one comment added
* showing a text contains information about the pre-filled conditions
*/
validateExceptionCommentCountAndText(
1,
'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):'
);
addExceptionFlyoutItemName(ITEM_NAME);
cy.get(ADD_AND_BTN).click();
// edit conditions
addExceptionEntryFieldValue(ADDITIONAL_ENTRY, 5);
addExceptionEntryFieldValueValue('foo', 5);
// Change the name again
editExceptionFlyoutItemName(ITEM_NAME_EDIT);
// validate the condition is still 'host.hostname' or got rest after the name is changed
validateExceptionConditionField(ADDITIONAL_ENTRY);
submitNewExceptionItem();
goToExceptionsTab();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME_EDIT);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).contains('span', 'host.hostname');
});
it('Should delete all prefilled exception entries when creating a Rule exception from Alerts take action button without resetting to initial auto-prefilled entries', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
// Open first Alert Summary
expandFirstAlert();
// The Rule exception should populated with highlighted fields
openAddRuleExceptionFromAlertActionButton();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Delete all the highlighted fields to see if any condition
* will prefuilled again.
*/
const highlightedFieldsCount = highlightedFieldsBasedOnAlertDoc.length - 1;
highlightedFieldsBasedOnAlertDoc.forEach((_, index) =>
cy
.get(ENTRY_DELETE_BTN)
.eq(highlightedFieldsCount - index)
.click()
);
/**
* Validate that there are no highlighted fields are auto populated
* after the deletion
*/
validateEmptyExceptionConditionField();
});
});

View file

@ -1,178 +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 { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
goToClosedAlertsOnRuleDetailsPage,
goToOpenedAlertsOnRuleDetailsPage,
} from '../../../tasks/alerts';
import {
editException,
editExceptionFlyoutItemName,
submitEditedExceptionItem,
} from '../../../tasks/exceptions';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import {
addFirstExceptionFromRuleDetails,
goToAlertsTab,
goToExceptionsTab,
openEditException,
removeException,
waitForTheRuleToBeExecuted,
} from '../../../tasks/rule_details';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
import { postDataView, deleteAlertsAndRules } from '../../../tasks/common';
import {
NO_EXCEPTIONS_EXIST_PROMPT,
EXCEPTION_ITEM_VIEWER_CONTAINER,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_ITEM_CONTAINER,
VALUES_INPUT,
FIELD_INPUT_PARENT,
} from '../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
describe('Add exception using data views from rule details', () => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const ITEM_NAME = 'Sample Exception List Item';
before(() => {
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', 'exceptions');
login();
postDataView('exceptions-*');
});
after(() => {
cy.task('esArchiverUnload', 'exceptions');
});
beforeEach(() => {
deleteAlertsAndRules();
createRule(
getNewRule({
query: 'agent.name:*',
data_view_id: 'exceptions-*',
interval: '10s',
rule_id: 'rule_testing',
})
);
login();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();
});
afterEach(() => {
cy.task('esArchiverUnload', 'exceptions_2');
});
it('Creates an exception item and close all matching alerts', () => {
goToExceptionsTab();
// when no exceptions exist, empty component shows with action to add exception
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// clicks prompt button to add first exception that will also select to close
// all matching alerts
addFirstExceptionFromRuleDetails(
{
field: 'agent.name',
operator: 'is',
values: ['foo'],
},
ITEM_NAME
);
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// Alerts table should now be empty from having added exception and closed
// matching alert
goToAlertsTab();
cy.get(EMPTY_ALERT_TABLE).should('exist');
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`);
// Remove the exception and load an event that would have matched that exception
// to show that said exception now starts to show up again
goToExceptionsTab();
// when removing exception and again, no more exist, empty screen shows again
removeException();
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// load more docs
cy.task('esArchiverLoad', 'exceptions_2');
// now that there are no more exceptions, the docs should match and populate alerts
goToAlertsTab();
goToOpenedAlertsOnRuleDetailsPage();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
});
it('Edits an exception item', () => {
const NEW_ITEM_NAME = 'Exception item-EDITED';
const ITEM_FIELD = 'unique_value.test';
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
goToExceptionsTab();
// add item to edit
addFirstExceptionFromRuleDetails(
{
field: ITEM_FIELD,
operator: 'is',
values: ['foo'],
},
ITEM_NAME
);
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' unique_value.testIS foo');
// open edit exception modal
openEditException();
// edit exception item name
editExceptionFlyoutItemName(NEW_ITEM_NAME);
// check that the existing item's field is being populated
cy.get(EXCEPTION_ITEM_CONTAINER)
.eq(0)
.find(FIELD_INPUT_PARENT)
.eq(0)
.should('have.text', ITEM_FIELD);
cy.get(VALUES_INPUT).should('have.text', 'foo');
// edit conditions
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
// submit
submitEditedExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// check that updates stuck
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', NEW_ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.nameIS foo');
});
});

View file

@ -1,140 +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 { getExceptionList, expectedExportedExceptionList } from '../../../../objects/exception';
import { getNewRule } from '../../../../objects/rule';
import { createRule } from '../../../../tasks/api_calls/rules';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
import {
deleteExceptionListWithoutRuleReferenceByListId,
deleteExceptionListWithRuleReferenceByListId,
exportExceptionList,
waitForExceptionsTableToBeLoaded,
createSharedExceptionList,
linkRulesToExceptionList,
assertNumberLinkedRules,
} from '../../../../tasks/exceptions_table';
import {
EXCEPTIONS_LIST_MANAGEMENT_NAME,
EXCEPTIONS_TABLE_SHOWING_LISTS,
} from '../../../../screens/exceptions';
import { createExceptionList } from '../../../../tasks/api_calls/exceptions';
import { TOASTER } from '../../../../screens/alerts_detection_rules';
const EXCEPTION_LIST_NAME = 'My test list';
const EXCEPTION_LIST_TO_DUPLICATE_NAME = 'A test list 2';
const getExceptionList1 = () => ({
...getExceptionList(),
name: EXCEPTION_LIST_NAME,
list_id: 'exception_list_1',
});
const getExceptionList2 = () => ({
...getExceptionList(),
name: EXCEPTION_LIST_TO_DUPLICATE_NAME,
list_id: 'exception_list_2',
});
describe('Manage lists from "Shared Exception Lists" page', () => {
describe('Create/Export/Delete List', () => {
before(() => {
createRule(getNewRule({ name: 'Another rule' }));
// Create exception list associated with a rule
createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) =>
createRule(
getNewRule({
exceptions_list: [
{
id: response.body.id,
list_id: getExceptionList2().list_id,
type: getExceptionList2().type,
namespace_type: getExceptionList2().namespace_type,
},
],
})
)
);
// Create exception list not used by any rules
createExceptionList(getExceptionList1(), getExceptionList1().list_id).as(
'exceptionListResponse'
);
});
beforeEach(() => {
login();
visitWithoutDateRange(EXCEPTIONS_URL);
waitForExceptionsTableToBeLoaded();
});
it('Export exception list', function () {
cy.intercept(/(\/api\/exception_lists\/_export)/).as('export');
exportExceptionList(getExceptionList1().list_id);
cy.wait('@export').then(({ response }) => {
cy.wrap(response?.body).should(
'eql',
expectedExportedExceptionList(this.exceptionListResponse)
);
cy.get(TOASTER).should(
'have.text',
`Exception list "${EXCEPTION_LIST_NAME}" exported successfully`
);
});
});
it('Link rules to shared exception list', function () {
assertNumberLinkedRules(getExceptionList2().list_id, '1');
linkRulesToExceptionList(getExceptionList2().list_id, 1);
assertNumberLinkedRules(getExceptionList2().list_id, '2');
});
it('Create exception list', function () {
createSharedExceptionList(
{ name: 'Newly created list', description: 'This is my list.' },
true
);
// After creation - directed to list detail page
cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', 'Newly created list');
});
it('Delete exception list without rule reference', () => {
// Using cy.contains because we do not care about the exact text,
// just checking number of lists shown
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '4');
deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id);
// Using cy.contains because we do not care about the exact text,
// just checking number of lists shown
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
});
it('Deletes exception list with rule reference', () => {
waitForPageWithoutDateRange(EXCEPTIONS_URL);
waitForExceptionsTableToBeLoaded();
// Using cy.contains because we do not care about the exact text,
// just checking number of lists shown
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id);
// Using cy.contains because we do not care about the exact text,
// just checking number of lists shown
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
});
});
});

View file

@ -1,87 +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 { createRule } from '../../../../tasks/api_calls/rules';
import { getNewRule } from '../../../../objects/rule';
import {
CORRELATIONS_ANCESTRY_SECTION,
CORRELATIONS_ANCESTRY_TABLE,
CORRELATIONS_CASES_SECTION,
CORRELATIONS_SESSION_SECTION,
CORRELATIONS_SOURCE_SECTION,
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON,
} from '../../../../screens/expandable_flyout/alert_details_left_panel_correlations_tab';
import {
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB,
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_BUTTON_GROUP,
} from '../../../../screens/expandable_flyout/alert_details_left_panel';
import {
expandCorrelationsSection,
openCorrelationsTab,
} from '../../../../tasks/expandable_flyout/alert_details_left_panel_correlations_tab';
import { openInsightsTab } from '../../../../tasks/expandable_flyout/alert_details_left_panel';
import { expandDocumentDetailsExpandableFlyoutLeftSection } from '../../../../tasks/expandable_flyout/alert_details_right_panel';
import {
createNewCaseFromExpandableFlyout,
expandFirstAlertExpandableFlyout,
} from '../../../../tasks/expandable_flyout/common';
import { cleanKibana } from '../../../../tasks/common';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
import { login, visit } from '../../../../tasks/login';
import { ALERTS_URL } from '../../../../urls/navigation';
describe('Expandable flyout left panel correlations', () => {
beforeEach(() => {
cleanKibana();
login();
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
expandFirstAlertExpandableFlyout();
expandDocumentDetailsExpandableFlyoutLeftSection();
createNewCaseFromExpandableFlyout();
openInsightsTab();
openCorrelationsTab();
});
it('should render correlations details correctly', () => {
cy.log('link the alert to a new case');
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).scrollIntoView();
cy.log('should render the Insights header');
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).should('be.visible').and('have.text', 'Insights');
cy.log('should render the inner tab switch');
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_BUTTON_GROUP).should('be.visible');
cy.log('should render correlations tab activator / button');
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON)
.should('be.visible')
.and('have.text', 'Correlations');
cy.log('should render all the correlations sections');
cy.get(CORRELATIONS_ANCESTRY_SECTION)
.should('be.visible')
.and('have.text', '1 alert related by ancestry');
cy.get(CORRELATIONS_SOURCE_SECTION)
.should('be.visible')
.and('have.text', '0 alerts related by source event');
cy.get(CORRELATIONS_SESSION_SECTION)
.should('be.visible')
.and('have.text', '1 alert related by session');
cy.get(CORRELATIONS_CASES_SECTION).should('be.visible').and('have.text', '1 related case');
expandCorrelationsSection(CORRELATIONS_ANCESTRY_SECTION);
cy.get(CORRELATIONS_ANCESTRY_TABLE).should('be.visible');
});
});

View file

@ -1,231 +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 { upperFirst } from 'lodash';
import {
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_CREATE_BUTTON,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_DESCRIPTION_INPUT,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_NAME_INPUT,
EXISTING_CASE_SELECT_BUTTON,
VIEW_CASE_TOASTER_LINK,
} from '../../../../screens/expandable_flyout/common';
import {
createNewCaseFromCases,
expandFirstAlertExpandableFlyout,
navigateToAlertsPage,
navigateToCasesPage,
} from '../../../../tasks/expandable_flyout/common';
import { ALERT_CHECKBOX } from '../../../../screens/alerts';
import { CASE_DETAILS_PAGE_TITLE } from '../../../../screens/case_details';
import {
DOCUMENT_DETAILS_FLYOUT_COLLAPSE_DETAILS_BUTTON,
DOCUMENT_DETAILS_FLYOUT_EXPAND_DETAILS_BUTTON,
DOCUMENT_DETAILS_FLYOUT_FOOTER,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_ENDPOINT_EXCEPTION,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_MARK_AS_ACKNOWLEDGED,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION_FLYOUT_CANCEL_BUTTON,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION_FLYOUT_HEADER,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_EXISTING_CASE,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE,
DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE,
DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE_ENTRY,
DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE_SECTION,
DOCUMENT_DETAILS_FLYOUT_FOOTER_MARK_AS_CLOSED,
DOCUMENT_DETAILS_FLYOUT_FOOTER_RESPOND,
DOCUMENT_DETAILS_FLYOUT_FOOTER_TAKE_ACTION_BUTTON,
DOCUMENT_DETAILS_FLYOUT_HEADER_CHAT_BUTTON,
DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE,
DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE,
DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY,
DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE,
DOCUMENT_DETAILS_FLYOUT_HEADER_STATUS,
DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE,
DOCUMENT_DETAILS_FLYOUT_JSON_TAB,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB,
DOCUMENT_DETAILS_FLYOUT_TABLE_TAB,
} from '../../../../screens/expandable_flyout/alert_details_right_panel';
import {
collapseDocumentDetailsExpandableFlyoutLeftSection,
expandDocumentDetailsExpandableFlyoutLeftSection,
openJsonTab,
openTableTab,
openTakeActionButton,
openTakeActionButtonAndSelectItem,
selectTakeActionItem,
} from '../../../../tasks/expandable_flyout/alert_details_right_panel';
import { cleanKibana } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import { createRule } from '../../../../tasks/api_calls/rules';
import { getNewRule } from '../../../../objects/rule';
import { ALERTS_URL } from '../../../../urls/navigation';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
describe('Alert details expandable flyout right panel', () => {
const rule = getNewRule();
beforeEach(() => {
cleanKibana();
login();
createRule(rule);
visit(ALERTS_URL);
waitForAlertsToPopulate();
});
it('should display header and footer basics', () => {
expandFirstAlertExpandableFlyout();
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name);
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_CHAT_BUTTON).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_STATUS).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE)
.should('be.visible')
.and('have.text', rule.risk_score);
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE)
.should('be.visible')
.and('have.text', upperFirst(rule.severity));
cy.log('Verify all 3 tabs are visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB).should('be.visible').and('have.text', 'Overview');
cy.get(DOCUMENT_DETAILS_FLYOUT_TABLE_TAB).should('be.visible').and('have.text', 'Table');
cy.get(DOCUMENT_DETAILS_FLYOUT_JSON_TAB).should('be.visible').and('have.text', 'JSON');
cy.log('Verify the expand/collapse button is visible and functionality works');
expandDocumentDetailsExpandableFlyoutLeftSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_COLLAPSE_DETAILS_BUTTON)
.should('be.visible')
.and('have.text', 'Collapse details');
collapseDocumentDetailsExpandableFlyoutLeftSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_EXPAND_DETAILS_BUTTON)
.should('be.visible')
.and('have.text', 'Expand details');
cy.log('Verify the take action button is visible on all tabs');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_TAKE_ACTION_BUTTON).should('be.visible');
openTableTab();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_TAKE_ACTION_BUTTON).should('be.visible');
openJsonTab();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_TAKE_ACTION_BUTTON).should('be.visible');
});
// TODO this will change when add to existing case is improved
// https://github.com/elastic/security-team/issues/6298
it('should add to existing case', () => {
navigateToCasesPage();
createNewCaseFromCases();
cy.get(CASE_DETAILS_PAGE_TITLE).should('be.visible').and('have.text', 'case');
navigateToAlertsPage();
expandFirstAlertExpandableFlyout();
openTakeActionButtonAndSelectItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_EXISTING_CASE);
cy.get(EXISTING_CASE_SELECT_BUTTON).should('be.visible').contains('Select').click();
cy.get(VIEW_CASE_TOASTER_LINK).should('be.visible').and('contain.text', 'View case');
});
// TODO this will change when add to new case is improved
// https://github.com/elastic/security-team/issues/6298
it('should add to new case', () => {
expandFirstAlertExpandableFlyout();
openTakeActionButtonAndSelectItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE);
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_NAME_INPUT).type('case');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_DESCRIPTION_INPUT).type(
'case description'
);
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_TO_NEW_CASE_CREATE_BUTTON).click();
cy.get(VIEW_CASE_TOASTER_LINK).should('be.visible').and('contain.text', 'View case');
});
it('should mark as acknowledged', () => {
cy.get(ALERT_CHECKBOX).should('have.length', 2);
expandFirstAlertExpandableFlyout();
openTakeActionButtonAndSelectItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_MARK_AS_ACKNOWLEDGED);
// TODO figure out how to verify the toasts pops up
// cy.get(KIBANA_TOAST)
// .should('be.visible')
// .and('have.text', 'Successfully marked 1 alert as acknowledged.');
cy.get(ALERT_CHECKBOX).should('have.length', 1);
});
it('should mark as closed', () => {
cy.get(ALERT_CHECKBOX).should('have.length', 2);
expandFirstAlertExpandableFlyout();
openTakeActionButtonAndSelectItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_MARK_AS_CLOSED);
// TODO figure out how to verify the toasts pops up
// cy.get(KIBANA_TOAST).should('be.visible').and('have.text', 'Successfully closed 1 alert.');
cy.get(ALERT_CHECKBOX).should('have.length', 1);
});
// these actions are now grouped together as we're not really testing their functionality but just the existence of the option in the dropdown
it('should test other action within take action dropdown', () => {
expandFirstAlertExpandableFlyout();
cy.log('should add endpoint exception');
// TODO figure out why this option is disabled in Cypress but not running the app locally
// https://github.com/elastic/security-team/issues/6300
openTakeActionButton();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_ENDPOINT_EXCEPTION).should('be.disabled');
cy.log('should add rule exception');
// TODO this isn't fully testing the add rule exception yet
// https://github.com/elastic/security-team/issues/6301
selectTakeActionItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION);
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION_FLYOUT_HEADER).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ADD_RULE_EXCEPTION_FLYOUT_CANCEL_BUTTON)
.should('be.visible')
.click();
// cy.log('should isolate host');
// TODO figure out why isolate host isn't showing up in the dropdown
// https://github.com/elastic/security-team/issues/6302
// openTakeActionButton();
// cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_ISOLATE_HOST).should('be.visible');
cy.log('should respond');
// TODO this will change when respond is improved
// https://github.com/elastic/security-team/issues/6303
openTakeActionButton();
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_RESPOND).should('be.disabled');
cy.log('should investigate in timeline');
selectTakeActionItem(DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE);
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE_SECTION)
.first()
.within(() =>
cy.get(DOCUMENT_DETAILS_FLYOUT_FOOTER_INVESTIGATE_IN_TIMELINE_ENTRY).should('be.visible')
);
});
});

View file

@ -1,351 +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 { collapseDocumentDetailsExpandableFlyoutLeftSection } from '../../../../tasks/expandable_flyout/alert_details_right_panel';
import { DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT } from '../../../../screens/expandable_flyout/alert_details_left_panel_investigation_tab';
import {
createNewCaseFromExpandableFlyout,
expandFirstAlertExpandableFlyout,
} from '../../../../tasks/expandable_flyout/common';
import {
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ABOUT_SECTION_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ABOUT_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_PREVIEW_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_HEADER_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_VALUES,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_VALUES,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_GUIDE_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_SECTION_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_MITRE_ATTACK_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_MITRE_ATTACK_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_SESSION_PREVIEW_CONTENT,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_VALUE_CELL,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_RESPONSE_SECTION_EMPTY_RESPONSE,
} from '../../../../screens/expandable_flyout/alert_details_right_panel_overview_tab';
import {
navigateToCorrelationsDetails,
clickInvestigationGuideButton,
navigateToPrevalenceDetails,
toggleOverviewTabAboutSection,
toggleOverviewTabInsightsSection,
toggleOverviewTabInvestigationSection,
toggleOverviewTabResponseSection,
toggleOverviewTabVisualizationsSection,
} from '../../../../tasks/expandable_flyout/alert_details_right_panel_overview_tab';
import { cleanKibana } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import { createRule } from '../../../../tasks/api_calls/rules';
import { getNewRule } from '../../../../objects/rule';
import { ALERTS_URL } from '../../../../urls/navigation';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
import {
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT,
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_HOST_DETAILS,
DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_USER_DETAILS,
} from '../../../../screens/expandable_flyout/alert_details_left_panel_entities_tab';
describe('Alert details expandable flyout right panel overview tab', () => {
const rule = getNewRule();
beforeEach(() => {
cleanKibana();
login();
createRule(rule);
visit(ALERTS_URL);
waitForAlertsToPopulate();
expandFirstAlertExpandableFlyout();
});
describe('about section', () => {
it('should display about section', () => {
cy.log('header and content');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ABOUT_SECTION_HEADER)
.should('be.visible')
.and('have.text', 'About');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ABOUT_SECTION_CONTENT).should('be.visible');
cy.log('description');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE)
.should('be.visible')
.and('contain.text', 'Rule description');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE)
.should('be.visible')
.within(() => {
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON)
.should('be.visible')
.and('have.text', 'Rule summary');
});
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS)
.should('be.visible')
.and('have.text', rule.description);
cy.log('reason');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE)
.should('be.visible')
.and('have.text', 'Alert reason');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_DETAILS)
.should('be.visible')
.and('contain.text', rule.name);
cy.log('mitre attack');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_MITRE_ATTACK_TITLE)
.should('be.visible')
// @ts-ignore
.and('contain.text', rule.threat[0].framework);
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_MITRE_ATTACK_DETAILS)
.should('be.visible')
// @ts-ignore
.and('contain.text', rule.threat[0].technique[0].name)
// @ts-ignore
.and('contain.text', rule.threat[0].tactic.name);
});
});
describe('visualizations section', () => {
it('should display analyzer and session previews', () => {
toggleOverviewTabAboutSection();
toggleOverviewTabVisualizationsSection();
cy.log('analyzer graph preview');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_PREVIEW_CONTENT).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_PREVIEW_CONTENT).should('be.visible');
cy.log('session view preview');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_SESSION_PREVIEW_CONTENT).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_SESSION_PREVIEW_CONTENT).should('be.visible');
});
});
describe('investigation section', () => {
it('should display investigation section', () => {
toggleOverviewTabAboutSection();
cy.log('header and content');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_SECTION_HEADER)
.should('be.visible')
.and('have.text', 'Investigation');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_SECTION_CONTENT).should(
'be.visible'
);
cy.log('investigation guide button');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_GUIDE_BUTTON)
.should('be.visible')
.and('have.text', 'Investigation guide');
cy.log('should navigate to left Investigation tab');
clickInvestigationGuideButton();
cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATION_TAB_CONTENT).should('be.visible');
cy.log('highlighted fields');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_HEADER_TITLE)
.should('be.visible')
.and('have.text', 'Highlighted fields');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_DETAILS).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL)
.should('be.visible')
.and('contain.text', 'host.name');
const hostNameCell =
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_VALUE_CELL('siem-kibana');
cy.get(hostNameCell).should('be.visible').and('have.text', 'siem-kibana');
cy.get(hostNameCell).click();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_HOST_DETAILS).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_HOST_DETAILS).should('be.visible');
collapseDocumentDetailsExpandableFlyoutLeftSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_FIELD_CELL)
.should('be.visible')
.and('contain.text', 'user.name');
const userNameCell =
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_TABLE_VALUE_CELL('test');
cy.get(userNameCell).should('be.visible').and('have.text', 'test');
cy.get(userNameCell).click();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_USER_DETAILS).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_USER_DETAILS).should('be.visible');
});
});
describe('insights section', () => {
it('should display entities section', () => {
toggleOverviewTabAboutSection();
toggleOverviewTabInvestigationSection();
toggleOverviewTabInsightsSection();
cy.log('header and content');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER)
.should('be.visible')
.and('have.text', 'Entities');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_CONTENT).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER).should('be.visible');
cy.log('should navigate to left panel Entities tab');
// TODO: skipping this section as Cypress can't seem to find the element (though it's in the DOM)
// navigateToEntitiesDetails();
// cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible');
});
it('should display threat intelligence section', () => {
toggleOverviewTabAboutSection();
toggleOverviewTabInvestigationSection();
toggleOverviewTabInsightsSection();
cy.log('header and content');
cy.get(
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER
).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER)
.should('be.visible')
.and('have.text', 'Threat Intelligence');
cy.get(
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT
).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT)
.should('be.visible')
.within(() => {
// threat match detected
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES)
.eq(0)
.should('be.visible')
.and('have.text', '0 threat match detected'); // TODO work on getting proper IoC data to get proper data here
// field with threat enrichement
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES)
.eq(1)
.should('be.visible')
.and('have.text', '0 field enriched with threat intelligence'); // TODO work on getting proper IoC data to get proper data here
});
cy.log('should navigate to left panel Threat Intelligence tab');
// TODO: skipping this section as Cypress can't seem to find the element (though it's in the DOM)
// navigateToThreatIntelligenceDetails();
// cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); // TODO update when we can navigate to Threat Intelligence sub tab directly
});
// TODO: skipping this due to flakiness
it.skip('should display correlations section', () => {
cy.log('link the alert to a new case');
createNewCaseFromExpandableFlyout();
toggleOverviewTabAboutSection();
toggleOverviewTabInvestigationSection();
toggleOverviewTabInsightsSection();
cy.log('header and content');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_HEADER)
.should('be.visible')
.and('have.text', 'Correlations');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_CONTENT).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_CONTENT)
.should('be.visible')
.within(() => {
// TODO the order in which these appear is not deterministic currently, hence this can cause flakiness
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_VALUES)
.eq(0)
.should('be.visible')
.and('have.text', '1 alert related by ancestry');
// cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_VALUES)
// .eq(2)
// .should('be.visible')
// .and('have.text', '1 alert related by the same source event'); // TODO work on getting proper data to display some same source data here
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_VALUES)
.eq(2)
.should('be.visible')
.and('have.text', '1 alert related by session');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_CORRELATIONS_VALUES)
.eq(1)
.should('be.visible')
.and('have.text', '1 related case');
});
cy.log('should navigate to left panel Correlations tab');
navigateToCorrelationsDetails();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); // TODO update when we can navigate to Correlations sub tab directly
});
// TODO work on getting proper data to make the prevalence section work here
// we need to generate enough data to have at least one field with prevalence
it.skip('should display prevalence section', () => {
toggleOverviewTabAboutSection();
toggleOverviewTabInvestigationSection();
toggleOverviewTabInsightsSection();
cy.log('header and content');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_HEADER)
.should('be.visible')
.and('have.text', 'Prevalence');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_CONTENT).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_CONTENT)
.should('be.visible')
.within(() => {
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_PREVALENCE_VALUES)
.should('be.visible')
.and('have.text', 'is uncommon');
});
cy.log('should navigate to left panel Prevalence tab');
navigateToPrevalenceDetails();
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); // TODO update when we can navigate to Prevalence sub tab directly
});
});
describe('response section', () => {
it('should display empty message', () => {
toggleOverviewTabAboutSection();
toggleOverviewTabInvestigationSection();
toggleOverviewTabResponseSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_RESPONSE_SECTION_EMPTY_RESPONSE).should(
'be.visible'
);
});
});
});

View file

@ -1,167 +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 { Timeline } from '../../../objects/timeline';
import {
MODAL_CONFIRMATION_BTN,
MODAL_CONFIRMATION_CANCEL_BTN,
} from '../../../screens/alerts_detection_rules';
import {
ALERTS_PAGE,
APP_LEAVE_CONFIRM_MODAL,
CASES_PAGE,
MANAGE_PAGE,
OBSERVABILITY_ALERTS_PAGE,
} from '../../../screens/kibana_navigation';
import { TIMELINE_SAVE_MODAL } from '../../../screens/timeline';
import { cleanKibana } from '../../../tasks/common';
import {
navigateFromKibanaCollapsibleTo,
openKibanaNavigation,
} from '../../../tasks/kibana_navigation';
import { login, visit } from '../../../tasks/login';
import { closeTimelineUsingToggle } from '../../../tasks/security_main';
import {
addNameAndDescriptionToTimeline,
createNewTimeline,
populateTimeline,
waitForTimelineChanges,
} from '../../../tasks/timeline';
import { HOSTS_URL, MANAGE_URL } from '../../../urls/navigation';
describe('Save Timeline Prompts', () => {
before(() => {
cleanKibana();
login();
/*
* When timeline changes are pending, chrome would popup with
* a confirm dialog stating that `you can lose unsaved changed.
* Below changes will disable that.
*
* */
cy.window().then((win) => {
win.onbeforeunload = null;
});
});
beforeEach(() => {
login();
visit(HOSTS_URL);
createNewTimeline();
});
it('unchanged & unsaved timeline should NOT prompt when user navigates away', () => {
openKibanaNavigation();
navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE);
cy.url().should('not.contain', HOSTS_URL);
});
it('Changed & unsaved timeline should prompt when user navigates away from security solution', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
openKibanaNavigation();
navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE);
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_BTN).click();
});
it('Changed & unsaved timeline should NOT prompt when user navigates away within security solution where timelines are enabled', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
// navigate to any other page in security solution
openKibanaNavigation();
cy.get(CASES_PAGE).click();
cy.get(APP_LEAVE_CONFIRM_MODAL).should('not.exist');
});
it('Changed & unsaved timeline should prompt when user navigates away within security solution where timelines are disbaled eg. admin screen', () => {
populateTimeline();
waitForTimelineChanges();
openKibanaNavigation();
cy.get(MANAGE_PAGE).click();
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_BTN).click();
});
it('Changed & saved timeline should NOT prompt when user navigates away out of security solution', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
openKibanaNavigation();
navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE);
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_CANCEL_BTN).click();
addNameAndDescriptionToTimeline(
{
title: 'Some Timeline',
description: 'Some Timeline',
} as Timeline,
true
);
openKibanaNavigation();
navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE);
cy.url().should('not.contain', HOSTS_URL);
});
it('Changed & saved timeline should NOT prompt when user navigates within security solution where timelines are disabled', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
openKibanaNavigation();
cy.get(MANAGE_PAGE).click();
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_CANCEL_BTN).click();
addNameAndDescriptionToTimeline(
{
title: 'Some Timeline',
description: 'Some Timeline',
} as Timeline,
true
);
openKibanaNavigation();
cy.get(MANAGE_PAGE).click();
cy.url().should('not.contain', HOSTS_URL);
});
it('When user navigates to the page where timeline is present, Time save modal should not exists.', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
openKibanaNavigation();
cy.get(MANAGE_PAGE).click();
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_BTN).click();
// Navigate back to HOSTS_URL and ensure that
// timeline save modal is NOT present
openKibanaNavigation();
cy.get(ALERTS_PAGE).click();
cy.get(TIMELINE_SAVE_MODAL).should('not.exist');
});
it('Changed and unsaved timeline should NOT prompt when user navigates from the page where timeline is disabled', () => {
populateTimeline();
waitForTimelineChanges();
closeTimelineUsingToggle();
openKibanaNavigation();
cy.get(MANAGE_PAGE).click();
cy.get(APP_LEAVE_CONFIRM_MODAL).should('be.visible');
cy.get(MODAL_CONFIRMATION_BTN).click();
// now we have come from MANAGE_PAGE where timeline is disabled
// to outside app where timeline is not present.
// There should be NO confirmation model in that case.
openKibanaNavigation();
navigateFromKibanaCollapsibleTo(OBSERVABILITY_ALERTS_PAGE);
// should not be manage page i.e. successfull navigation
cy.get(TIMELINE_SAVE_MODAL).should('not.exist');
cy.url().should('not.contain', MANAGE_URL);
});
});

View file

@ -10,17 +10,11 @@
"cypress": "../../../node_modules/.bin/cypress",
"cypress:burn": "yarn cypress:run:reporter --env burn=2 --concurrency=1 --headed",
"cypress:changed-specs-only": "yarn cypress:run:reporter --changed-specs-only --env burn=2",
"cypress:open": "TZ=UTC node ./scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config",
"cypress:run": "yarn cypress:run:reporter --spec './cypress/e2e/{,!(investigations,explore)/**/}*.cy.ts'; status=$?; yarn junit:merge && exit $status",
"cypress:run:cases": "yarn cypress:run:reporter --spec './cypress/e2e/explore/cases/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "TZ=UTC node ./scripts/start_cypress_parallel run --browser chrome --config-file ./cypress/cypress_ci.config.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json",
"cypress:run:respops": "yarn cypress:run:reporter --spec './cypress/e2e/(detection_alerts|detection_rules|exceptions)/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:dw:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config",
"cypress:dw:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; yarn junit:merge && exit $status",
"cypress:dw:endpoint:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress_endpoint.config.ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json --concurrency 1; status=$?; yarn junit:merge && exit $status",
"cypress:dw:endpoint:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress_endpoint.config.ts ts --ftr-config-file ../../../../../../x-pack/test/defend_workflows_cypress/endpoint_config",
"cypress:investigations:run": "yarn cypress:run:reporter --spec './cypress/e2e/investigations/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:explore:run": "yarn cypress:run:reporter --spec './cypress/e2e/explore/**/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "TZ=UTC node ./scripts/start_cypress_parallel run --ftr-config-file ../../test/defend_workflows_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=../../test/security_solution_cypress/cypress/reporter_config.json",
"cypress:dw:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress.config.ts ts --ftr-config-file ../../test/defend_workflows_cypress/cli_config",
"cypress:dw:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress.config.ts --ftr-config-file ../../test/defend_workflows_cypress/cli_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=../../test/security_solution_cypress/cypress/reporter_config.json; status=$?; yarn junit:merge && exit $status",
"cypress:dw:endpoint:run": "node ./scripts/start_cypress_parallel run --config-file ./public/management/cypress_endpoint.config.ts --ftr-config-file ../../test/defend_workflows_cypress/endpoint_config --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=../../test/security_solution_cypress/cypress/reporter_config.json --concurrency 1; status=$?; yarn junit:merge && exit $status",
"cypress:dw:endpoint:open": "node ./scripts/start_cypress_parallel open --config-file ./public/management/cypress_endpoint.config.ts ts --ftr-config-file ../../test/defend_workflows_cypress/endpoint_config",
"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",

View file

@ -84,8 +84,10 @@ export const cli = () => {
);
const isOpen = argv._[0] === 'open';
const cypressConfigFilePath = require.resolve(`../../${argv.configFile}`) as string;
const cypressConfigFile = await import(require.resolve(`../../${argv.configFile}`));
const cypressConfigFilePath = require.resolve(
`../../${_.isArray(argv.configFile) ? _.last(argv.configFile) : argv.configFile}`
) as string;
const cypressConfigFile = await import(cypressConfigFilePath);
const spec: string | undefined = argv?.spec as string;
let files = retrieveIntegrations(spec ? [spec] : cypressConfigFile?.e2e?.specPattern);
@ -192,7 +194,9 @@ export const cli = () => {
const config = await readConfigFile(
log,
EsVersion.getDefault(),
_.isArray(argv.ftrConfigFile) ? _.last(argv.ftrConfigFile) : argv.ftrConfigFile,
path.resolve(
_.isArray(argv.ftrConfigFile) ? _.last(argv.ftrConfigFile) : argv.ftrConfigFile
),
{
servers: {
elasticsearch: {

View file

@ -10,12 +10,12 @@ import Url from 'url';
import * as yaml from 'js-yaml';
import { encode } from '@kbn/rison';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants';
import {
LOADING_INDICATOR,
LOADING_INDICATOR_HIDDEN,
} from '@kbn/security-solution-plugin/cypress/screens/security_header';
import { encode } from '@kbn/rison';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants';
} from '../../../../test/security_solution_cypress/cypress/screens/security_header';
import type { ROLES } from './privileges';
const LOGIN_API_ENDPOINT = '/internal/security/login';

View file

@ -5,12 +5,12 @@
"license": "Elastic License 2.0",
"scripts": {
"cypress": "../../../node_modules/.bin/cypress",
"cypress:open": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/threat_intelligence_cypress/cli_config_parallel",
"cypress:open": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel open --spec './cypress/e2e/**/*.cy.ts' --config-file ../../plugins/threat_intelligence/cypress/cypress.config.ts --ftr-config-file ../../test/threat_intelligence_cypress/cli_config_parallel",
"cypress:run": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/**/*.cy.ts'; status=$?; yarn junit:merge && exit $status",
"cypress:run:spec": "yarn cypress:run:reporter --browser chrome --spec ${SPEC_LIST:-'./cypress/e2e/**/*.cy.ts'}; status=$?; yarn junit:merge && exit $status",
"cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/cases/*.cy.ts' --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel run --config-file ./cypress/cypress.config.ts --ftr-config-file ../../../../../../x-pack/test/threat_intelligence_cypress/cli_config_parallel --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json",
"cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec ./cypress/e2e/detection_alerts/*.cy.ts,./cypress/e2e/detection_rules/*.cy.ts,./cypress/e2e/exceptions/*.cy.ts --ftr-config-file ../../../../../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:cases": "yarn cypress:run:reporter --browser chrome --spec './cypress/e2e/cases/*.cy.ts' --ftr-config-file ../../test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"cypress:run:reporter": "TZ=UTC node ../security_solution/scripts/start_cypress_parallel run --config-file ../../plugins/threat_intelligence/cypress/cypress.config.ts --ftr-config-file ../../test/threat_intelligence_cypress/cli_config_parallel --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json",
"cypress:run:respops": "yarn cypress:run:reporter --browser chrome --spec ./cypress/e2e/detection_alerts/*.cy.ts,./cypress/e2e/detection_rules/*.cy.ts,./cypress/e2e/exceptions/*.cy.ts --ftr-config-file ../../x-pack/test/security_solution_cypress/cli_config; status=$?; yarn junit:merge && exit $status",
"junit:merge": "../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-threat-intelligence/cypress/results/mochawesome*.json > ../../../target/kibana-threat-intelligence/cypress/results/output.json && ../../../node_modules/.bin/marge ../../../target/kibana-threat-intelligence/cypress/results/output.json --reportDir ../../../target/kibana-threat-intelligence/cypress/results && mkdir -p ../../../target/junit && cp ../../../target/kibana-threat-intelligence/cypress/results/*.xml ../../../target/junit/"
}
}

View file

@ -38,21 +38,39 @@ of data for your test, [**Running the tests**](#running-the-tests) to know how t
Please, before opening a PR with the new test, please make sure that the test fails. If you never see your test fail you dont know if your test is actually testing the right thing, or testing anything at all.
Note that we use tags in order to select which tests we want to execute:
```typescript
export const tag = {
SERVERLESS: '@serverless',
ESS: '@ess',
BROKEN_IN_SERVERLESS: '@brokenInServerless',
};
```
Please, before opening a PR with the new test, make sure that the test fails. If you never see your test fail you dont know if your test is actually testing the right thing, or testing anything at all.
## Running the tests
### Run them locally
Run the tests with the following yarn scripts:
When running the tests, FTR is used to spawn both a Kibana instance (http://localhost:5620) and an Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section).
Run the tests with the following yarn scripts from `x-pack/test/security_solution_cypress`:
| Script Name | Description |
| ----------- | ----------- |
| cypress | Runs the default Cypress command |
| cypress:open | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a local kibana and ES instance. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. |C
| cypress:run | Runs all tests in the `e2e` directory excluding `investigations` and `explore` directories in headless mode |
| cypress:run:cases | Runs all tests under `explore/cases` in the `e2e` directory related to the Cases area team in headless mode |
| cypress:run:reporter | Runs all tests with the specified configuration in headless mode and produces a report using `cypress-multi-reporters` |
| cypress:run:respops | Runs all tests related to the Response Ops area team, specifically tests in `detection_alerts`, `detection_rules`, and `exceptions` directories in headless mode |
| cypress:investigations:run | Runs all tests in the `e2e/investigations` directory in headless mode |
| cypress:explore:run | Runs all tests in the `e2e/explore` directory in headless mode |
| cypress:open:ess | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a local kibana and ES instance. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. |
| cypress:open:serverless | Opens the Cypress UI with all tests in the `e2e` directory. This also runs a mocked serverless environment. The kibana instance will reload when you make code changes. This is the recommended way to debug and develop tests. |
| cypress:run:ess | Runs all tests tagged as ESS placed in the `e2e` directory excluding `investigations` and `explore` directories in headless mode |
| cypress:run:cases:ess | Runs all tests under `explore/cases` in the `e2e` directory related to the Cases area team in headless mode |
| cypress:ess | Runs all ESS tests with the specified configuration in headless mode and produces a report using `cypress-multi-reporters` |
| cypress:run:respops:ess | Runs all tests related to the Response Ops area team, specifically tests in `detection_alerts`, `detection_rules`, and `exceptions` directories in headless mode |
| cypress:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e` directory excluding `investigations` and `explore` directories in headless mode |
| cypress:investigations:run:ess | Runs all tests tagged as ESS in the `e2e/investigations` directory in headless mode |
| cypress:explore:run:ess | Runs all tests tagged as ESS in the `e2e/explore` directory in headless mode |
| cypress:investigations:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/investigations` directory in headless mode |
| cypress:explore:run:serverless | Runs all tests tagged as SERVERLESS in the `e2e/explore` directory in headless mode |
| junit:merge | Merges individual test reports into a single report and moves the report to the `junit` directory |
Please note that all the headless mode commands do not open the Cypress UI and are typically used in CI/CD environments. The scripts that open the Cypress UI are useful for development and debugging.
@ -148,7 +166,7 @@ We use es_archiver to manage the data that our Cypress tests need.
1. Set up a clean instance of kibana and elasticsearch (if this is not possible, try to clean/minimize the data that you are going to archive).
2. With the kibana and elasticsearch instance up and running, create the data that you need for your test.
3. When you are sure that you have all the data you need run the following command from: `x-pack/plugins/security_solution`
3. When you are sure that you have all the data you need run the following command from: `x-pack/test/security_solution_cypress`
```sh
node ../../../scripts/es_archiver save <nameOfTheFolderWhereDataIsSaved> <indexPatternsToBeSaved> --dir ../../test/security_solution_cypress/es_archives --config ../../../test/functional/config.base.js --es-url http://<elasticsearchUsername>:<elasticsearchPassword>@<elasticsearchHost>:<elasticsearchPort>
@ -164,7 +182,7 @@ Note that the command will create the folder if it does not exist.
### Using an archive from within the Cypress tests
Task [cypress/support/es_archiver.ts](https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/cypress/support/es_archiver.ts) provides helpers such as `esArchiverLoad` and `esArchiverUnload` by means of `es_archiver`'s CLI.
Task [cypress/support/es_archiver.ts](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts) provides helpers such as `esArchiverLoad` and `esArchiverUnload` by means of `es_archiver`'s CLI.
## Development Best Practices

View file

@ -10,6 +10,10 @@ import { esArchiver } from './support/es_archiver';
export default defineCypressConfig({
defaultCommandTimeout: 60000,
env: {
grepFilterSpecs: true,
grepTags: '@ess',
},
execTimeout: 60000,
pageLoadTimeout: 60000,
responseTimeout: 60000,
@ -25,6 +29,9 @@ export default defineCypressConfig({
experimentalMemoryManagement: true,
setupNodeEvents(on, config) {
esArchiver(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
},
},
});

View file

@ -11,6 +11,10 @@ import { esArchiver } from './support/es_archiver';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
defaultCommandTimeout: 150000,
env: {
grepFilterSpecs: true,
grepTags: '@ess',
},
execTimeout: 150000,
pageLoadTimeout: 150000,
numTestsKeptInMemory: 0,
@ -29,6 +33,9 @@ export default defineCypressConfig({
specPattern: './cypress/e2e/**/*.cy.ts',
setupNodeEvents(on, config) {
esArchiver(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
},
},
});

View file

@ -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 { defineCypressConfig } from '@kbn/cypress-config';
import { esArchiver } from './support/es_archiver';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
defaultCommandTimeout: 150000,
env: {
grepFilterSpecs: true,
grepTags: '@serverless --@brokenInServerless',
},
execTimeout: 150000,
pageLoadTimeout: 150000,
numTestsKeptInMemory: 0,
retries: {
runMode: 1,
},
screenshotsFolder: '../../../target/kibana-security-solution/cypress/screenshots',
trashAssetsBeforeRuns: false,
video: false,
videosFolder: '../../../../target/kibana-security-solution/cypress/videos',
viewportHeight: 946,
viewportWidth: 1680,
e2e: {
baseUrl: 'http://localhost:5601',
experimentalMemoryManagement: true,
specPattern: './cypress/e2e/**/*.cy.ts',
setupNodeEvents(on, config) {
esArchiver(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
},
},
});

View file

@ -0,0 +1,38 @@
/*
* 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 { esArchiver } from './support/es_archiver';
// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
defaultCommandTimeout: 60000,
execTimeout: 60000,
pageLoadTimeout: 60000,
responseTimeout: 60000,
screenshotsFolder: '../../../target/kibana-security-solution/cypress/screenshots',
trashAssetsBeforeRuns: false,
video: false,
videosFolder: '../../../target/kibana-security-solution/cypress/videos',
viewportHeight: 946,
viewportWidth: 1680,
numTestsKeptInMemory: 10,
env: {
grepFilterSpecs: true,
grepTags: '@serverless --@brokenInServerless',
},
e2e: {
experimentalRunAllSpecs: true,
experimentalMemoryManagement: true,
setupNodeEvents(on, config) {
esArchiver(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
},
},
});

View file

@ -24,7 +24,7 @@ import type {
RuleName,
RuleReferenceArray,
RuleTagArray,
} from '../../common/api/detection_engine';
} from '@kbn/security-solution-plugin/common/api/detection_engine';
interface RuleFields {
defaultIndexPatterns: IndexPatternArray;

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { login, visit } from '../../tasks/login';
import { openTimelineUsingToggle } from '../../tasks/security_main';
import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timeline';
@ -25,7 +27,7 @@ import { GET_TIMELINE_HEADER } from '../../screens/timeline';
const alertRunTimeField = 'field.name.alert.page';
const timelineRuntimeField = 'field.name.timeline';
describe('Create DataView runtime field', () => {
describe('Create DataView runtime field', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
deleteRuntimeField('security-solution-default', alertRunTimeField);
deleteRuntimeField('security-solution-default', timelineRuntimeField);

View file

@ -5,6 +5,12 @@
* 2.0.
*/
import {
DEFAULT_ALERTS_INDEX,
DEFAULT_INDEX_PATTERN,
} from '@kbn/security-solution-plugin/common/constants';
import { tag } from '../../tags';
import { login, loginWithUser, visit, visitWithUser } from '../../tasks/login';
import { HOSTS_URL, TIMELINES_URL } from '../../urls/navigation';
@ -30,7 +36,6 @@ import { postDataView } from '../../tasks/common';
import { openTimelineUsingToggle } from '../../tasks/security_main';
import { createUsersAndRoles, secReadCasesAll, secReadCasesAllUser } from '../../tasks/privileges';
import { TOASTER } from '../../screens/configure_cases';
import { DEFAULT_ALERTS_INDEX, DEFAULT_INDEX_PATTERN } from '../../../common/constants';
import { SOURCERER } from '../../screens/sourcerer';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { getTimeline, getTimelineModifiedSourcerer } from '../../objects/timeline';
@ -46,7 +51,7 @@ describe('Sourcerer', () => {
cy.task('esArchiverResetKibana');
dataViews.forEach((dataView: string) => postDataView(dataView));
});
describe('permissions', () => {
describe('permissions', { tags: tag.ESS }, () => {
before(() => {
createUsersAndRoles(usersToCreate, rolesToCreate);
});
@ -57,7 +62,7 @@ describe('Sourcerer', () => {
});
});
describe('Default scope', () => {
describe('Default scope', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();
@ -117,20 +122,24 @@ describe('Sourcerer', () => {
cy.get(SOURCERER.saveButton).should('be.disabled');
});
it('adds a pattern to the default index and correctly filters out auditbeat-*', () => {
openSourcerer();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection('*beat*');
addIndexToDefault('*beat*');
isHostsStatValue('1');
openSourcerer();
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererSelection('*beat*');
});
it(
'adds a pattern to the default index and correctly filters out auditbeat-*',
{ tags: tag.BROKEN_IN_SERVERLESS },
() => {
openSourcerer();
isSourcererSelection(`auditbeat-*`);
isNotSourcererSelection('*beat*');
addIndexToDefault('*beat*');
isHostsStatValue('1');
openSourcerer();
openAdvancedSettings();
isSourcererSelection(`auditbeat-*`);
isSourcererSelection('*beat*');
}
);
});
});
describe('Timeline scope', () => {
describe('Timeline scope', { tags: tag.BROKEN_IN_SERVERLESS }, () => {
beforeEach(() => {
cy.clearLocalStorage();
login();

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { getNewRule } from '../../objects/rule';
import {
clickAlertTag,
@ -24,7 +26,7 @@ import {
UNSELECTED_ALERT_TAG,
} from '../../screens/alerts';
describe('Alert tagging', () => {
describe('Alert tagging', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverResetKibana');

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { getNewRule } from '../../objects/rule';
import { ALERTS_COUNT } from '../../screens/alerts';
import {
@ -24,7 +26,7 @@ import {
} from '../../screens/search_bar';
import { TOASTER } from '../../screens/alerts_detection_rules';
describe('Histogram legend hover actions', () => {
describe('Histogram legend hover actions', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
const ruleConfigs = getNewRule();
before(() => {

View file

@ -0,0 +1,199 @@
/*
* 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 { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../tags';
import { DETECTIONS_RULE_MANAGEMENT_URL, ALERTS_URL } from '../../urls/navigation';
import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts';
const loadPageAsPlatformEngineerUser = (url: string) => {
login(ROLES.soc_manager);
waitForPageWithoutDateRange(url, ROLES.soc_manager);
waitForPageTitleToBeShown();
};
const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
describe(
'Detections > Need Admin Callouts indicating an admin is needed to migrate the alert data set',
{ tags: tag.ESS },
() => {
const NEED_ADMIN_FOR_UPDATE_CALLOUT = 'need-admin-for-update-rules';
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.
// Otherwise the app will be disabled and show a "welcome"-like page.
login();
visitWithoutDateRange(ALERTS_URL);
waitForPageTitleToBeShown();
});
context(
'The users index_mapping_outdated is "true" and their admin callouts should show up',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', (req) => {
req.reply((res) => {
res.send(200, {
index_mapping_outdated: true,
name: '.alerts-security.alerts-default',
});
});
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
waitForCallOutToBeShown(NEED_ADMIN_FOR_UPDATE_CALLOUT, 'primary');
});
});
}
);
context(
'The users index_mapping_outdated is "false" and their admin callouts should not show up ',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', {
index_mapping_outdated: false,
name: '.alerts-security.alerts-default',
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
}
);
context(
'The users index_mapping_outdated is "null" and their admin callouts should not show up ',
() => {
beforeEach(() => {
// Index mapping outdated is forced to return true as being outdated so that we get the
// need admin callouts being shown.
cy.intercept('GET', '/api/detection_engine/index', {
index_mapping_outdated: null,
name: '.alerts-security.alerts-default',
});
});
context('On Detections home page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(ALERTS_URL);
});
it('We show the need admin primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rules Management page', () => {
beforeEach(() => {
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
});
it('We show 1 primary callout of need admin', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
context('On Rule Details page', () => {
beforeEach(() => {
createRule(getNewRule({ rule_id: 'rule_testing' }));
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
});
afterEach(() => {
deleteCustomRule();
});
it('We show 1 primary callout', () => {
getCallOut(NEED_ADMIN_FOR_UPDATE_CALLOUT).should('not.exist');
});
});
}
);
}
);

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { disableExpandableFlyout } from '../../tasks/api_calls/kibana_advanced_settings';
import { getNewThreatIndicatorRule, indicatorRuleMatchingDoc } from '../../objects/rule';
import { cleanKibana } from '../../tasks/common';
@ -28,7 +30,7 @@ import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_det
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
import { addsFieldsToTimeline } from '../../tasks/rule_details';
describe('CTI Enrichment', () => {
describe('CTI Enrichment', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverLoad', 'threat_indicator');

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { getNewRule } from '../../objects/rule';
import {
HOST_RISK_HEADER_COLIMN,
@ -30,7 +32,7 @@ import { login, visit } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
describe('Enrichment', () => {
describe('Enrichment', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverLoad', 'risk_users');

View file

@ -5,7 +5,9 @@
* 2.0.
*/
import { ROLES } from '../../../common/test';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../tags';
import { DETECTIONS_RULE_MANAGEMENT_URL, ALERTS_URL } from '../../urls/navigation';
import { getNewRule } from '../../objects/rule';
import { PAGE_TITLE } from '../../screens/common/page';
@ -36,7 +38,7 @@ const waitForPageTitleToBeShown = () => {
cy.get(PAGE_TITLE).should('be.visible');
};
describe('Detections > Callouts', () => {
describe('Detections > Callouts', { tags: tag.ESS }, () => {
const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
before(() => {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visit } from '../../tasks/login';
@ -14,7 +16,7 @@ import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timelin
import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
describe('Ransomware Detection Alerts', () => {
describe('Ransomware Detection Alerts', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cy.task('esArchiverLoad', 'ransomware_detection');
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visit } from '../../tasks/login';
@ -14,7 +16,7 @@ import { TIMELINE_QUERY, TIMELINE_VIEW_IN_ANALYZER } from '../../screens/timelin
import { selectAlertsHistogram } from '../../tasks/alerts';
import { createTimeline } from '../../tasks/timelines';
describe('Ransomware Prevention Alerts', () => {
describe('Ransomware Prevention Alerts', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cy.task('esArchiverLoad', 'ransomware_prevention');
});

View file

@ -0,0 +1,133 @@
/*
* 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_PATH,
RULES_ADD_PATH,
RULES_UPDATES,
} from '@kbn/security-solution-plugin/common/constants';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../../tags';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common';
import { login, waitForPageWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import {
ADD_ELASTIC_RULES_BTN,
getInstallSingleRuleButtonByRuleId,
getUpgradeSingleRuleButtonByRuleId,
INSTALL_ALL_RULES_BUTTON,
RULES_UPDATES_TAB,
RULE_CHECKBOX,
UPGRADE_ALL_RULES_BUTTON,
} from '../../../screens/alerts_detection_rules';
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
const loadPageAsReadOnlyUser = (url: string) => {
login(ROLES.reader);
waitForPageWithoutDateRange(url, ROLES.reader);
};
describe(
'Detection rules, Prebuilt Rules Installation and Update - Authorization/RBAC',
{ tags: tag.ESS },
() => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
waitForRulesTableToBeLoaded();
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
});
describe('User with read privileges on Security Solution', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
// Now login with read-only user in preparation for test
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('should not be able to install prebuilt rules', () => {
// Check that Add Elastic Rules button is disabled
cy.get(ADD_ELASTIC_RULES_BTN).should('be.disabled');
// Navigate to Add Elastic Rules page anyways via URL
// and assert that rules cannot be selected and all
// installation buttons are disabled
cy.visit(`${APP_PATH}${RULES_ADD_PATH}`);
cy.get(INSTALL_ALL_RULES_BUTTON).should('be.disabled');
cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).should(
'not.exist'
);
cy.get(RULE_CHECKBOX).should('not.exist');
});
});
describe('User with read privileges on Security Solution', () => {
beforeEach(() => {
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
// Now login with read-only user in preparation for test
loadPageAsReadOnlyUser(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('should not be able to upgrade prebuilt rules', () => {
// Check that Rule Update tab is not shown
cy.get(RULES_UPDATES_TAB).should('not.exist');
// Navigate to Rule Update tab anyways via URL
// and assert that rules cannot be selected and all
// upgrade buttons are disabled
cy.visit(`${APP_PATH}${RULES_UPDATES}`);
cy.get(UPGRADE_ALL_RULES_BUTTON).should('be.disabled');
cy.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id)).should(
'not.exist'
);
cy.get(RULE_CHECKBOX).should('not.exist');
});
});
}
);

View file

@ -0,0 +1,151 @@
/*
* 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 { tag } from '../../../tags';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import {
addElasticRulesButtonClick,
assertRuleAvailableForInstallAndInstallOne,
assertRuleAvailableForInstallAndInstallSelected,
assertRuleAvailableForInstallAndInstallAllInPage,
assertRuleAvailableForInstallAndInstallAll,
assertRuleUpgradeAvailableAndUpgradeOne,
assertRuleUpgradeAvailableAndUpgradeSelected,
assertRuleUpgradeAvailableAndUpgradeAllInPage,
assertRuleUpgradeAvailableAndUpgradeAll,
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
describe(
'Detection rules, Prebuilt Rules Installation and Update - Error handling',
{ tags: tag.ESS },
() => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
describe('Installation of prebuilt rules - Should fail gracefully with toast error message when', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
waitForRulesTableToBeLoaded();
});
it('installing prebuilt rules one by one', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1], didRequestFail: true });
});
it('installing multiple selected prebuilt rules by selecting them individually', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallSelected({
rules: [RULE_1, RULE_2],
didRequestFail: true,
});
});
it('installing multiple selected prebuilt rules by selecting all in page', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAllInPage({
rules: [RULE_1, RULE_2],
didRequestFail: true,
});
});
it('installing all available rules at once', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAll({
rules: [RULE_1, RULE_2],
didRequestFail: true,
});
});
});
describe('Update of prebuilt rules - Should fail gracefully with toast error message when', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
beforeEach(() => {
/* Create a new rule and install it */
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
waitForRulesTableToBeLoaded();
reload();
});
it('upgrading prebuilt rules one by one', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1], didRequestFail: true });
});
it('upgrading multiple selected prebuilt rules by selecting them individually', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeSelected({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
it('upgrading multiple selected prebuilt rules by selecting all in page', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAllInPage({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
it('upgrading all rules with available upgrades at once', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
didRequestFail: true,
});
});
});
}
);

View file

@ -0,0 +1,270 @@
/*
* 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 { BulkInstallPackageInfo } from '@kbn/fleet-plugin/common';
import type { Rule } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types';
import { tag } from '../../../tags';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import {
GO_BACK_TO_RULES_TABLE_BUTTON,
INSTALL_ALL_RULES_BUTTON,
INSTALL_SELECTED_RULES_BUTTON,
NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE,
NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE,
RULES_UPDATES_TAB,
RULE_CHECKBOX,
SELECT_ALL_RULES_ON_PAGE_CHECKBOX,
TOASTER,
} from '../../../screens/alerts_detection_rules';
import { waitForRulesTableToBeLoaded } from '../../../tasks/alerts_detection_rules';
import {
getRuleAssets,
createAndInstallMockedPrebuiltRules,
} from '../../../tasks/api_calls/prebuilt_rules';
import { resetRulesTableState, deleteAlertsAndRules, reload } from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
import {
addElasticRulesButtonClick,
assertRuleAvailableForInstallAndInstallOne,
assertRuleAvailableForInstallAndInstallSelected,
assertRuleAvailableForInstallAndInstallAllInPage,
assertRuleAvailableForInstallAndInstallAll,
assertRuleUpgradeAvailableAndUpgradeOne,
assertRuleUpgradeAvailableAndUpgradeSelected,
assertRuleUpgradeAvailableAndUpgradeAllInPage,
assertRuleUpgradeAvailableAndUpgradeAll,
ruleUpdatesTabClick,
} from '../../../tasks/prebuilt_rules';
describe(
'Detection rules, Prebuilt Rules Installation and Update workflow',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
beforeEach(() => {
login();
resetRulesTableState();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
describe('Installation of prebuilt rules package via Fleet', () => {
beforeEach(() => {
cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk');
cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as(
'installPackage'
);
waitForRulesTableToBeLoaded();
});
it('should install package from Fleet in the background', () => {
/* Assert that the package in installed from Fleet */
cy.wait('@installPackageBulk', {
timeout: 60000,
}).then(({ response: bulkResponse }) => {
cy.wrap(bulkResponse?.statusCode).should('eql', 200);
const packages = bulkResponse?.body.items.map(
({ name, result }: BulkInstallPackageInfo) => ({
name,
})
);
const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name);
// Under normal flow the package is installed via the Fleet bulk install API.
// However, for testing purposes the package can be installed via the Fleet individual install API,
// so we need to intercept and wait for that request as well.
if (!packagesBulkInstalled.includes('security_detection_engine')) {
// Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set
cy.wait('@installPackage').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
cy.wrap(response?.body)
.should('have.property', 'items')
.should('have.length.greaterThan', 0);
});
} else {
// Normal flow, install via the Fleet bulk install API
expect(packages.length).to.have.greaterThan(0);
// At least one of the packages installed should be the security_detection_engine package
expect(packages).to.satisfy((pckgs: BulkInstallPackageInfo[]) =>
pckgs.some((pkg) => pkg.name === 'security_detection_engine')
);
}
});
});
it('should install rules from the Fleet package when user clicks on CTA', () => {
const getRulesAndAssertNumberInstalled = () => {
getRuleAssets().then((response) => {
const ruleIds = response.body.hits.hits.map(
(hit: { _source: { ['security-rule']: Rule } }) =>
hit._source['security-rule'].rule_id
);
const numberOfRulesToInstall = new Set(ruleIds).size;
addElasticRulesButtonClick();
cy.get(INSTALL_ALL_RULES_BUTTON).should('be.enabled').click();
cy.get(TOASTER)
.should('be.visible')
.should('have.text', `${numberOfRulesToInstall} rules installed successfully.`);
});
};
/* Retrieve how many rules were installed from the Fleet package */
/* See comments in test above for more details */
cy.wait('@installPackageBulk', {
timeout: 60000,
}).then(({ response: bulkResponse }) => {
cy.wrap(bulkResponse?.statusCode).should('eql', 200);
const packagesBulkInstalled = bulkResponse?.body.items.map(
({ name }: { name: string }) => name
);
if (!packagesBulkInstalled.includes('security_detection_engine')) {
cy.wait('@installPackage').then(() => {
getRulesAndAssertNumberInstalled();
});
} else {
getRulesAndAssertNumberInstalled();
}
});
});
});
describe('Installation of prebuilt rules', () => {
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false });
waitForRulesTableToBeLoaded();
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as(
'installPrebuiltRules'
);
});
it('should install prebuilt rules one by one', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1] });
});
it('should install multiple selected prebuilt rules by selecting them individually', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallSelected({ rules: [RULE_1, RULE_2] });
});
it('should install multiple selected prebuilt rules by selecting all in page', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAllInPage({ rules: [RULE_1, RULE_2] });
});
it('should install all available rules at once', () => {
addElasticRulesButtonClick();
assertRuleAvailableForInstallAndInstallAll({ rules: [RULE_1, RULE_2] });
});
it('should display an empty screen when all available prebuilt rules have been installed', () => {
addElasticRulesButtonClick();
cy.get(INSTALL_ALL_RULES_BUTTON).click();
cy.get(TOASTER).should('be.visible').should('have.text', `2 rules installed successfully.`);
cy.get(RULE_CHECKBOX).should('not.exist');
cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE).should('exist');
cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).should('exist');
});
it('should fail gracefully with toast error message when request to install rules fails', () => {
/* Stub request to force rules installation to fail */
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', {
statusCode: 500,
}).as('installPrebuiltRules');
addElasticRulesButtonClick();
cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click();
cy.get(INSTALL_SELECTED_RULES_BUTTON).click();
cy.wait('@installPrebuiltRules');
cy.get(TOASTER).should('be.visible').should('have.text', 'Rule installation failed');
});
});
describe('Upgrade of prebuilt rules', () => {
const RULE_1_ID = 'rule_1';
const RULE_2_ID = 'rule_2';
const OUTDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Outdated rule 1',
rule_id: RULE_1_ID,
version: 1,
});
const UPDATED_RULE_1 = createRuleAssetSavedObject({
name: 'Updated rule 1',
rule_id: RULE_1_ID,
version: 2,
});
const OUTDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Outdated rule 2',
rule_id: RULE_2_ID,
version: 1,
});
const UPDATED_RULE_2 = createRuleAssetSavedObject({
name: 'Updated rule 2',
rule_id: RULE_2_ID,
version: 2,
});
beforeEach(() => {
cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as(
'updatePrebuiltRules'
);
/* Create a new rule and install it */
createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
/* Create a second version of the rule, making it available for update */
createAndInstallMockedPrebuiltRules({
rules: [UPDATED_RULE_1, UPDATED_RULE_2],
installToKibana: false,
});
waitForRulesTableToBeLoaded();
reload();
});
it('should upgrade prebuilt rules one by one', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1] });
});
it('should upgrade multiple selected prebuilt rules by selecting them individually', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeSelected({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
});
it('should upgrade multiple selected prebuilt rules by selecting all in page', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAllInPage({
rules: [OUTDATED_RULE_1, OUTDATED_RULE_2],
});
});
it('should upgrade all rules with available upgrades at once', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should display an empty screen when all rules with available updates have been upgraded', () => {
ruleUpdatesTabClick();
assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] });
cy.get(RULES_UPDATES_TAB).should('not.exist');
cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE).should('exist');
});
});
}
);

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import {
COLLAPSED_ACTION_BTN,
@ -49,7 +51,7 @@ const rules = Array.from(Array(5)).map((_, i) => {
});
});
describe('Prebuilt rules', () => {
describe('Prebuilt rules', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -0,0 +1,192 @@
/*
* 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 { tag } from '../../../tags';
import { createRuleAssetSavedObject } from '../../../helpers/rules';
import { ADD_ELASTIC_RULES_BTN, RULES_UPDATES_TAB } from '../../../screens/alerts_detection_rules';
import {
deleteFirstRule,
waitForRulesTableToBeLoaded,
} from '../../../tasks/alerts_detection_rules';
import {
installAllPrebuiltRulesRequest,
createAndInstallMockedPrebuiltRules,
} from '../../../tasks/api_calls/prebuilt_rules';
import {
resetRulesTableState,
deleteAlertsAndRules,
reload,
deletePrebuiltRulesAssets,
} from '../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../urls/navigation';
const RULE_1 = createRuleAssetSavedObject({
name: 'Test rule 1',
rule_id: 'rule_1',
});
describe(
'Detection rules, Prebuilt Rules Installation and Update Notifications',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
beforeEach(() => {
login();
/* Make sure persisted rules table state is cleared */
resetRulesTableState();
deleteAlertsAndRules();
deletePrebuiltRulesAssets();
});
describe('No notifications', () => {
it('should NOT display install or update notifications when no prebuilt assets and no rules are installed', () => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
// TODO: test plan asserts "should NOT see a CTA to install prebuilt rules"
// but current behavior is to always show the CTA, even with no prebuilt rule assets installed
// Update that behaviour and then update this test.
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should NOT display install or update notifications when latest rules are installed', () => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: true });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
/* Assert that there are no installation or update notifications */
/* Add Elastic Rules button should not contain a number badge */
/* and Rule Upgrade tab should not be displayed */
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', 'Add Elastic rules');
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
});
describe('Notifications', () => {
beforeEach(() => {
createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: false });
});
describe('Rules installation notification when no rules have been installed', () => {
beforeEach(() => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
});
it('should notify user about prebuilt rules available for installation', () => {
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
});
describe('Rule installation notification when at least one rule already installed', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule assets with a different rule_id as the one that was */
/* installed before in order to trigger the installation notification */
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
const RULE_3 = createRuleAssetSavedObject({
name: 'Test rule 3',
rule_id: 'rule_3',
});
createAndInstallMockedPrebuiltRules({
rules: [RULE_2, RULE_3],
installToKibana: false,
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
});
it('should notify user about prebuilt rules available for installation', () => {
const numberOfAvailableRules = 2;
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should(
'have.text',
`Add Elastic rules${numberOfAvailableRules}`
);
cy.get(RULES_UPDATES_TAB).should('not.exist');
});
it('should notify user a rule is again available for installation if it is deleted', () => {
/* Install available rules, assert that the notification is gone */
/* then delete one rule and assert that the notification is back */
installAllPrebuiltRulesRequest().then(() => {
reload();
deleteFirstRule();
cy.get(ADD_ELASTIC_RULES_BTN).should('be.visible');
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
});
});
});
describe('Rule update notification', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule asset with the same rule_id as the one that was installed */
/* but with a higher version, in order to trigger the update notification */
const UPDATED_RULE = createRuleAssetSavedObject({
name: 'Test rule 1.1 (updated)',
rule_id: 'rule_1',
version: 2,
});
createAndInstallMockedPrebuiltRules({ rules: [UPDATED_RULE], installToKibana: false });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
reload();
});
});
it('should notify user about prebuilt rules package available for update', () => {
// No rules available for installation
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules`);
// But 1 rule available for update
cy.get(RULES_UPDATES_TAB).should('be.visible');
cy.get(RULES_UPDATES_TAB).should('have.text', `Rule Updates${1}`);
});
});
describe('Rule installation available and rule update available notifications', () => {
beforeEach(() => {
installAllPrebuiltRulesRequest().then(() => {
/* Create new rule assets with a different rule_id as the one that was */
/* installed before in order to trigger the installation notification */
const RULE_2 = createRuleAssetSavedObject({
name: 'Test rule 2',
rule_id: 'rule_2',
});
/* Create new rule asset with the same rule_id as the one that was installed */
/* but with a higher version, in order to trigger the update notification */
const UPDATED_RULE = createRuleAssetSavedObject({
name: 'Test rule 1.1 (updated)',
rule_id: 'rule_1',
version: 2,
});
createAndInstallMockedPrebuiltRules({
rules: [RULE_2, UPDATED_RULE],
installToKibana: false,
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
});
it('should notify user about prebuilt rules available for installation and for upgrade', () => {
// 1 rule available for installation
cy.get(ADD_ELASTIC_RULES_BTN).should('have.text', `Add Elastic rules${1}`);
// 1 rule available for update
cy.get(RULES_UPDATES_TAB).should('be.visible');
cy.get(RULES_UPDATES_TAB).should('have.text', `Rule Updates${1}`);
});
});
});
}
);

View file

@ -0,0 +1,78 @@
/*
* 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 { tag } from '../../../tags';
import { getIndexConnector } from '../../../objects/connector';
import { getSimpleCustomQueryRule } from '../../../objects/rule';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import { deleteIndex, waitForNewDocumentToBeIndexed } from '../../../tasks/api_calls/elasticsearch';
import {
cleanKibana,
deleteAlertsAndRules,
deleteConnectors,
deleteDataView,
} from '../../../tasks/common';
import {
createAndEnableRule,
fillAboutRuleAndContinue,
fillDefineCustomRuleAndContinue,
fillRuleAction,
fillScheduleRuleAndContinue,
} from '../../../tasks/create_new_rule';
import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe(
'Rule actions during detection rule creation',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
const indexConnector = getIndexConnector();
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
deleteAlertsAndRules();
deleteConnectors();
deleteIndex(indexConnector.index);
deleteDataView(indexConnector.index);
});
const rule = getSimpleCustomQueryRule();
const actions = { connectors: [indexConnector] };
const index = actions.connectors[0].index;
const initialNumberOfDocuments = 0;
const expectedJson = JSON.parse(actions.connectors[0].document);
it('Indexes a new document after the index action is triggered ', function () {
visit(RULE_CREATION);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
fillRuleAction(actions);
createAndEnableRule();
goToRuleDetails();
/* When the rule is executed, the action is triggered. We wait for the new document to be indexed */
waitForNewDocumentToBeIndexed(index, initialNumberOfDocuments);
/* We assert that the new indexed document is the one set on the index action */
cy.request({
method: 'GET',
url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search`,
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
}).then((response) => {
expect(response.body.hits.hits[0]._source).to.deep.equal(expectedJson);
});
});
}
);

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { ruleFields } from '../../../data/detection_engine';
import {
getNewRule,
@ -112,7 +114,7 @@ import { enablesRule, getDetails } from '../../../tasks/rule_details';
import { RULE_CREATION, DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
describe('Custom query rules', () => {
describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getDataViewRule } from '../../../objects/rule';
import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../screens/alerts';
@ -67,7 +69,7 @@ import { getDetails } from '../../../tasks/rule_details';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Custom query rules', () => {
describe('Custom query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
describe('Custom detection rules creation with data views', () => {
const rule = getDataViewRule();
const expectedUrls = rule.references?.join('');

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { getNewRule, getSavedQueryRule } from '../../../objects/rule';
import {
@ -45,7 +47,7 @@ const savedQueryName = 'custom saved query';
const savedQueryQuery = 'process.name: test';
const savedQueryFilterKey = 'testAgent.value';
describe('Custom saved_query rules', () => {
describe('Custom saved_query rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../../objects/rule';
@ -61,7 +63,7 @@ import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('EQL rules', () => {
describe('EQL rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
});
@ -150,10 +152,10 @@ describe('EQL rules', () => {
const rule = getEqlSequenceRule();
before(() => {
beforeEach(() => {
cy.task('esArchiverLoad', 'auditbeat_big');
});
after(() => {
afterEach(() => {
cy.task('esArchiverUnload', 'auditbeat_big');
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import {
getIndexPatterns,
@ -108,7 +110,7 @@ import { DETECTIONS_RULE_MANAGEMENT_URL, RULE_CREATION } from '../../../urls/nav
const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"';
describe('indicator match', () => {
describe('indicator match', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
describe('Detection rules, Indicator Match', () => {
const expectedUrls = getNewThreatIndicatorRule().references?.join('');
const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join('');

View file

@ -4,8 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { isArray } from 'lodash';
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getMachineLearningRule } from '../../../objects/rule';
@ -54,7 +54,7 @@ import { login, visitWithoutDateRange } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, machine learning', () => {
describe('Detection rules, machine learning', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
const expectedUrls = (getMachineLearningRule().references ?? []).join('');
const expectedFalsePositives = (getMachineLearningRule().false_positives ?? []).join('');
const expectedTags = (getMachineLearningRule().tags ?? []).join('');

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getIndexPatterns, getNewTermsRule } from '../../../objects/rule';
@ -59,7 +61,7 @@ import { login, visit } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('New Terms rules', () => {
describe('New Terms rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getIndexPatterns, getNewOverrideRule, getSeveritiesOverride } from '../../../objects/rule';
@ -61,7 +63,7 @@ import { getDetails } from '../../../tasks/rule_details';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, override', () => {
describe('Detection rules, override', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
const rule = getNewOverrideRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../tags';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../../helpers/rules';
import { getIndexPatterns, getNewThresholdRule } from '../../../objects/rule';
@ -59,7 +61,7 @@ import { login, visitWithoutDateRange } from '../../../tasks/login';
import { RULE_CREATION } from '../../../urls/navigation';
describe('Detection rules, threshold', () => {
describe('Detection rules, threshold', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
const rule = getNewThresholdRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');

View file

@ -5,7 +5,9 @@
* 2.0.
*/
import { ROLES } from '../../../../../common/test';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../../../tags';
import { getNewRule } from '../../../../objects/rule';
import {
COLLAPSED_ACTION_BTN,
@ -26,7 +28,7 @@ import { SECURITY_DETECTIONS_RULES_URL } from '../../../../urls/navigation';
const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
describe('All rules - read only', () => {
describe('All rules - read only', { tags: tag.ESS }, () => {
before(() => {
cleanKibana();
createRule(getNewRule({ rule_id: '1' }));

View file

@ -8,11 +8,12 @@
import { INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH } from '@kbn/alerting-plugin/common';
import type { MaintenanceWindowCreateBody } from '@kbn/alerting-plugin/common';
import type { AsApiContract } from '@kbn/alerting-plugin/server/routes/lib';
import { tag } from '../../../../tags';
import { cleanKibana } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
describe('Maintenance window callout on Rule Management page', () => {
describe('Maintenance window callout on Rule Management page', { tags: [tag.ESS] }, () => {
let maintenanceWindowId = '';
before(() => {
@ -34,7 +35,7 @@ describe('Maintenance window callout on Rule Management page', () => {
cy.request({
method: 'POST',
url: INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH,
headers: { 'kbn-xsrf': 'cypress-creds' },
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
body,
}).then((response) => {
maintenanceWindowId = response.body.id;
@ -46,7 +47,7 @@ describe('Maintenance window callout on Rule Management page', () => {
cy.request({
method: 'DELETE',
url: `${INTERNAL_ALERTING_API_MAINTENANCE_WINDOW_PATH}/${maintenanceWindowId}`,
headers: { 'kbn-xsrf': 'cypress-creds' },
headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' },
});
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../tags';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
import { FIELD } from '../../../../screens/alerts_details';
@ -52,7 +54,7 @@ Note that the rule we are using for testing purposes has the following character
- Integration: unknown
*/
describe('Related integrations', () => {
describe('Related integrations', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../../tags';
import {
waitForRulesTableToBeLoaded,
goToTheRuleDetailsOf,
@ -53,7 +55,7 @@ const EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item';
const NON_EXPIRED_EXCEPTION_ITEM_NAME = 'Sample exception item with future expiration';
describe('Detection rules, bulk duplicate', () => {
describe('Detection rules, bulk duplicate', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../../tags';
import {
MODAL_CONFIRMATION_BTN,
MODAL_CONFIRMATION_BODY,
@ -121,7 +123,7 @@ const defaultRuleData = {
timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e',
};
describe('Detection rules, bulk edit', () => {
describe('Detection rules, bulk edit', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -0,0 +1,226 @@
/*
* 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 { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../../../../tags';
import {
RULES_BULK_EDIT_ACTIONS_INFO,
RULES_BULK_EDIT_ACTIONS_WARNING,
ADD_RULE_ACTIONS_MENU_ITEM,
} from '../../../../../screens/rules_bulk_actions';
import { actionFormSelector } from '../../../../../screens/common/rule_actions';
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common';
import type { RuleActionCustomFrequency } from '../../../../../tasks/common/rule_actions';
import {
addSlackRuleAction,
assertSlackRuleAction,
addEmailConnectorAndRuleAction,
assertEmailRuleAction,
assertSelectedCustomFrequencyOption,
assertSelectedPerRuleRunFrequencyOption,
assertSelectedSummaryOfAlertsOption,
pickCustomFrequencyOption,
pickPerRuleRunFrequencyOption,
pickSummaryOfAlertsOption,
} from '../../../../../tasks/common/rule_actions';
import {
waitForRulesTableToBeLoaded,
selectNumberOfRules,
goToEditRuleActionsSettingsOf,
} from '../../../../../tasks/alerts_detection_rules';
import {
waitForBulkEditActionToFinish,
submitBulkEditForm,
checkOverwriteRuleActionsCheckbox,
openBulkEditRuleActionsForm,
openBulkActionsMenu,
} from '../../../../../tasks/rules_bulk_actions';
import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation';
import { createRule } from '../../../../../tasks/api_calls/rules';
import { createSlackConnector } from '../../../../../tasks/api_calls/connectors';
import {
getEqlRule,
getNewThreatIndicatorRule,
getNewRule,
getNewThresholdRule,
getMachineLearningRule,
getNewTermsRule,
} from '../../../../../objects/rule';
import { excessivelyInstallAllPrebuiltRules } from '../../../../../tasks/api_calls/prebuilt_rules';
const ruleNameToAssert = 'Custom rule name with actions';
const expectedNumberOfCustomRulesToBeEdited = 7;
// 7 custom rules of different types + 3 prebuilt.
// number of selected rules doesn't matter, we only want to make sure they will be edited an no modal window displayed as for other actions
const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 3;
const expectedExistingSlackMessage = 'Existing slack action';
const expectedSlackMessage = 'Slack action test message';
// TODO: Fix flakiness and unskip https://github.com/elastic/kibana/issues/154721
describe.skip(
'Detection rules, bulk edit of rule actions',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
before(() => {
cleanKibana();
login();
});
beforeEach(() => {
deleteAlertsAndRules();
deleteConnectors();
cy.task('esArchiverResetKibana');
createSlackConnector().then(({ body }) => {
const actions: RuleActionArray = [
{
id: body.id,
action_type_id: '.slack',
group: 'default',
params: {
message: expectedExistingSlackMessage,
},
frequency: {
summary: true,
throttle: null,
notifyWhen: 'onActiveAlert',
},
},
];
createRule(getNewRule({ name: ruleNameToAssert, rule_id: '1', max_signals: 500, actions }));
});
createRule(getEqlRule({ rule_id: '2' }));
createRule(getMachineLearningRule({ rule_id: '3' }));
createRule(getNewThreatIndicatorRule({ rule_id: '4' }));
createRule(getNewThresholdRule({ rule_id: '5' }));
createRule(getNewTermsRule({ rule_id: '6' }));
createRule(getNewRule({ saved_id: 'mocked', rule_id: '7' }));
createSlackConnector();
});
context('Restricted action privileges', () => {
it("User with no privileges can't add rule actions", () => {
login(ROLES.hunter_no_actions);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.hunter_no_actions);
waitForRulesTableToBeLoaded();
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkActionsMenu();
cy.get(ADD_RULE_ACTIONS_MENU_ITEM).should('be.disabled');
});
});
context('All actions privileges', () => {
beforeEach(() => {
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add a rule action to rules (existing connector)', () => {
const expectedActionFrequency: RuleActionCustomFrequency = {
throttle: 1,
throttleUnit: 'd',
};
excessivelyInstallAllPrebuiltRules();
// select both custom and prebuilt rules
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
openBulkEditRuleActionsForm();
// ensure rule actions info callout displayed on the form
cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible');
addSlackRuleAction(expectedSlackMessage);
pickSummaryOfAlertsOption();
pickCustomFrequencyOption(expectedActionFrequency);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedCustomFrequencyOption(expectedActionFrequency, 1);
assertSlackRuleAction(expectedExistingSlackMessage, 0);
assertSlackRuleAction(expectedSlackMessage, 1);
// ensure there is no third action
cy.get(actionFormSelector(2)).should('not.exist');
});
it('Overwrite rule actions in rules', () => {
excessivelyInstallAllPrebuiltRules();
// select both custom and prebuilt rules
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
openBulkEditRuleActionsForm();
addSlackRuleAction(expectedSlackMessage);
pickSummaryOfAlertsOption();
pickPerRuleRunFrequencyOption();
// check overwrite box, ensure warning is displayed
checkOverwriteRuleActionsCheckbox();
cy.get(RULES_BULK_EDIT_ACTIONS_WARNING).contains(
`You're about to overwrite rule actions for ${expectedNumberOfRulesToBeEdited} selected rules`
);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedPerRuleRunFrequencyOption();
assertSlackRuleAction(expectedSlackMessage);
// ensure existing action was overwritten
cy.get(actionFormSelector(1)).should('not.exist');
});
it('Add a rule action to rules (new connector)', () => {
const expectedActionFrequency: RuleActionCustomFrequency = {
throttle: 2,
throttleUnit: 'h',
};
const expectedEmail = 'test@example.com';
const expectedSubject = 'Subject';
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditRuleActionsForm();
addEmailConnectorAndRuleAction(expectedEmail, expectedSubject);
pickSummaryOfAlertsOption();
pickCustomFrequencyOption(expectedActionFrequency);
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been updated
goToEditRuleActionsSettingsOf(ruleNameToAssert);
assertSelectedSummaryOfAlertsOption();
assertSelectedCustomFrequencyOption(expectedActionFrequency, 1);
assertEmailRuleAction(expectedEmail, expectedSubject);
});
});
}
);

View file

@ -0,0 +1,255 @@
/*
* 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 { tag } from '../../../../../tags';
import {
RULES_BULK_EDIT_DATA_VIEWS_WARNING,
RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX,
} from '../../../../../screens/rules_bulk_actions';
import { DATA_VIEW_DETAILS, INDEX_PATTERNS_DETAILS } from '../../../../../screens/rule_details';
import {
waitForRulesTableToBeLoaded,
goToRuleDetails,
selectNumberOfRules,
goToTheRuleDetailsOf,
} from '../../../../../tasks/alerts_detection_rules';
import {
typeIndexPatterns,
waitForBulkEditActionToFinish,
submitBulkEditForm,
checkOverwriteDataViewCheckbox,
checkOverwriteIndexPatternsCheckbox,
openBulkEditAddIndexPatternsForm,
openBulkEditDeleteIndexPatternsForm,
} from '../../../../../tasks/rules_bulk_actions';
import {
hasIndexPatterns,
getDetails,
assertDetailsNotExist,
} from '../../../../../tasks/rule_details';
import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../../../urls/navigation';
import { createRule } from '../../../../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../../../../tasks/common';
import {
getEqlRule,
getNewThreatIndicatorRule,
getNewRule,
getNewThresholdRule,
getNewTermsRule,
} from '../../../../../objects/rule';
const DATA_VIEW_ID = 'auditbeat';
const expectedIndexPatterns = ['index-1-*', 'index-2-*'];
const expectedNumberOfCustomRulesToBeEdited = 6;
describe(
'Bulk editing index patterns of rules with a data view only',
{ tags: [tag.ESS, tag.SERVERLESS] },
() => {
before(() => {
cleanKibana();
});
beforeEach(() => {
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
login();
postDataView(DATA_VIEW_ID);
createRule(getNewRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' }));
createRule(getEqlRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '2' }));
createRule(
getNewThreatIndicatorRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '3' })
);
createRule(
getNewThresholdRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '4' })
);
createRule(getNewTermsRule({ index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '5' }));
createRule(
getNewRule({
index: undefined,
data_view_id: DATA_VIEW_ID,
saved_id: 'mocked',
rule_id: '6',
})
);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add index patterns to custom rules with configured data view: all rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Add index patterns to custom rules with configured data view when data view checkbox is checked: rules are updated', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
// click on data view overwrite checkbox, ensure warning is displayed
cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('not.exist');
checkOverwriteDataViewCheckbox();
cy.get(RULES_BULK_EDIT_DATA_VIEWS_WARNING).should('be.visible');
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been updated with index patterns and data view does not exist
goToRuleDetails();
hasIndexPatterns(expectedIndexPatterns.join(''));
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is NOT checked:: rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Overwrite index patterns in custom rules with configured data view when overwrite data view checkbox is checked: rules are updated', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteIndexPatternsCheckbox();
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited });
// check if rule has been overwritten with index patterns and data view does not exist
goToRuleDetails();
hasIndexPatterns(expectedIndexPatterns.join(''));
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
it('Delete index patterns in custom rules with configured data view: rules are skipped', () => {
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
openBulkEditDeleteIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
// in delete form data view checkbox is absent
cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX).should('not.exist');
submitBulkEditForm();
waitForBulkEditActionToFinish({
skippedCount: expectedNumberOfCustomRulesToBeEdited,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
});
}
);
describe('Bulk editing index patterns of rules with index patterns and rules with a data view', () => {
const customRulesNumber = 2;
before(() => {
cleanKibana();
});
beforeEach(() => {
login();
deleteAlertsAndRules();
cy.task('esArchiverResetKibana');
postDataView(DATA_VIEW_ID);
createRule(
getNewRule({ name: 'with dataview', index: [], data_view_id: DATA_VIEW_ID, rule_id: '1' })
);
createRule(getNewRule({ name: 'no data view', index: ['test-index-1-*'], rule_id: '2' }));
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
});
it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => {
selectNumberOfRules(customRulesNumber);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 1,
skippedCount: 1,
showDataViewsWarning: true,
});
// check if rule still has data view and index patterns field does not exist
goToTheRuleDetailsOf('with dataview');
getDetails(DATA_VIEW_DETAILS).contains(DATA_VIEW_ID);
assertDetailsNotExist(INDEX_PATTERNS_DETAILS);
});
it('Add index patterns to custom rules when overwrite data view checkbox is checked: all rules are updated', () => {
selectNumberOfRules(customRulesNumber);
openBulkEditAddIndexPatternsForm();
typeIndexPatterns(expectedIndexPatterns);
checkOverwriteDataViewCheckbox();
submitBulkEditForm();
waitForBulkEditActionToFinish({
updatedCount: 2,
});
// check if rule still has data view and index patterns field does not exist
goToRuleDetails();
assertDetailsNotExist(DATA_VIEW_DETAILS);
});
});

View file

@ -6,6 +6,7 @@
*/
import path from 'path';
import { tag } from '../../../../../tags';
import { expectedExportedRule, getNewRule } from '../../../../../objects/rule';
import {
@ -56,7 +57,7 @@ const prebuiltRules = Array.from(Array(7)).map((_, i) => {
});
});
describe('Export rules', () => {
describe('Export rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
const downloadsFolder = Cypress.config('downloadsFolder');
before(() => {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../../tags';
import { TOASTER } from '../../../../../screens/alerts_detection_rules';
import {
expectManagementTableRules,
@ -17,7 +19,7 @@ import { login, visitWithoutDateRange } from '../../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../../urls/navigation';
const RULES_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_rules.ndjson';
describe('Import rules', () => {
describe('Import rules', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -6,7 +6,9 @@
*/
import { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/common';
import type { RuleResponse } from '../../../../../../common/api/detection_engine';
import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine';
import { tag } from '../../../../../tags';
import { createRule, snoozeRule as snoozeRuleViaAPI } from '../../../../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../../../tasks/login';
@ -42,7 +44,7 @@ import { TOOLTIP } from '../../../../../screens/common';
const RULES_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_rules.ndjson';
describe('rule snoozing', () => {
describe('rule snoozing', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../tags';
import {
RULE_CHECKBOX,
REFRESH_RULES_STATUS,
@ -32,7 +34,7 @@ import { getNewRule } from '../../../../objects/rule';
const DEFAULT_RULE_REFRESH_INTERVAL_VALUE = 60000;
const NUM_OF_TEST_RULES = 6;
describe('Rules table: auto-refresh', () => {
describe('Rules table: auto-refresh', { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../tags';
import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import {
@ -26,7 +28,7 @@ import {
import { getNewRule } from '../../../../objects/rule';
describe('Rules table: filtering', () => {
describe('Rules table: filtering', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../tags';
import { getNewRule } from '../../../../objects/rule';
import { RULES_MONITORING_TAB, RULE_NAME } from '../../../../screens/alerts_detection_rules';
import { createRule } from '../../../../tasks/api_calls/rules';
@ -12,7 +14,7 @@ import { cleanKibana, deleteAlertsAndRules } from '../../../../tasks/common';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
describe('Rules table: links', () => {
describe('Rules table: links', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -6,6 +6,8 @@
*/
import { encode } from '@kbn/rison';
import { tag } from '../../../../tags';
import { cleanKibana, resetRulesTableState } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import {
@ -98,7 +100,7 @@ function expectDefaultRulesTableState(): void {
expectTablePage(1);
}
describe('Rules table: persistent state', () => {
describe('Rules table: persistent state', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
createTestRules();

View file

@ -4,6 +4,9 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../../tags';
import { createRuleAssetSavedObject } from '../../../../helpers/rules';
import {
SELECTED_RULES_NUMBER_LABEL,
@ -32,7 +35,7 @@ const RULE_2 = createRuleAssetSavedObject({
rule_id: 'rule_2',
});
describe('Rules table: selection', () => {
describe('Rules table: selection', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../../../tags';
import {
FIRST_RULE,
RULE_NAME,
@ -37,7 +39,7 @@ import {
} from '../../../../tasks/table_pagination';
import { TABLE_FIRST_PAGE, TABLE_SECOND_PAGE } from '../../../../screens/table_pagination';
describe('Rules table: sorting', () => {
describe('Rules table: sorting', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -5,8 +5,10 @@
* 2.0.
*/
import { ROLES } from '../../../../common/test';
import { deleteRoleAndUser, login, visitWithoutDateRange } from '../../../tasks/login';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../../tags';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
import {
createListsIndex,
@ -29,7 +31,7 @@ import {
} from '../../../screens/lists';
describe('value lists', () => {
describe('management modal', () => {
describe('management modal', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
beforeEach(() => {
login();
createListsIndex();
@ -221,17 +223,10 @@ describe('value lists', () => {
});
});
describe('user with restricted access role', () => {
before(() => {
describe('user with restricted access role', { tags: tag.ESS }, () => {
it('Does not allow a t1 analyst user to upload a value list', () => {
login(ROLES.t1_analyst);
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL, ROLES.t1_analyst);
});
after(() => {
deleteRoleAndUser(ROLES.t1_analyst);
});
it('Does not allow a t1 analyst user to upload a value list', () => {
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
});

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { tag } from '../../tags';
import {
PAGE_TITLE,
HOST_RISK_PREVIEW_TABLE,
@ -41,7 +43,10 @@ import {
describe(
'Entity analytics management page',
{ env: { ftrConfig: { enableExperimental: ['riskScoringRoutesEnabled'] } } },
{
env: { ftrConfig: { enableExperimental: ['riskScoringRoutesEnabled'] } },
tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS],
},
() => {
before(() => {
cleanKibana();

View file

@ -0,0 +1,127 @@
/*
* 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 { tag } from '../../../tags';
import { deleteAlertsAndRules } from '../../../tasks/common';
import {
expandFirstAlert,
goToClosedAlertsOnRuleDetailsPage,
openAddEndpointExceptionFromAlertActionButton,
openAddEndpointExceptionFromFirstAlert,
waitForAlerts,
} from '../../../tasks/alerts';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { getEndpointRule } from '../../../objects/rule';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import { createRule } from '../../../tasks/api_calls/rules';
import {
waitForAlertsToPopulate,
waitForTheRuleToBeExecuted,
} from '../../../tasks/create_new_rule';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
import {
addExceptionEntryFieldValueAndSelectSuggestion,
addExceptionEntryFieldValueValue,
addExceptionFlyoutItemName,
editExceptionFlyoutItemName,
selectCloseSingleAlerts,
submitNewExceptionItem,
validateExceptionConditionField,
} from '../../../tasks/exceptions';
import { ALERTS_COUNT } from '../../../screens/alerts';
import {
ADD_AND_BTN,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_ITEM_VIEWER_CONTAINER,
} from '../../../screens/exceptions';
import { goToEndpointExceptionsTab } from '../../../tasks/rule_details';
describe(
'Endpoint Exceptions workflows from Alert',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
const ITEM_NAME = 'Sample Exception List Item';
const ITEM_NAME_EDIT = 'Sample Exception List Item';
const ADDITIONAL_ENTRY = 'host.hostname';
beforeEach(() => {
cy.task('esArchiverUnload', 'endpoint');
cy.task('esArchiverResetKibana');
login();
deleteAlertsAndRules();
cy.task('esArchiverLoad', 'endpoint');
createRule(getEndpointRule());
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
});
after(() => {
cy.task('esArchiverUnload', 'endpoint');
});
it('Should be able to create and close single Endpoint exception from overflow menu', () => {
// The Endpoint will populated with predefined fields
openAddEndpointExceptionFromFirstAlert();
// As the endpoint.alerts-* is used to trigger the alert the
// file.Ext.code_signature will be auto-populated
validateExceptionConditionField('file.Ext.code_signature');
selectCloseSingleAlerts();
addExceptionFlyoutItemName(ITEM_NAME);
submitNewExceptionItem();
// Instead of immediately checking if the Opened Alert has moved to the closed tab,
// use the waitForAlerts method to create a buffer, allowing the alerts some time to
// be moved to the Closed Alert tab.
waitForAlerts();
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
});
it('Should be able to create Endpoint exception from Alerts take action button, and change multiple exception items without resetting to initial auto-prefilled entries', () => {
// Open first Alert Summary
expandFirstAlert();
// The Endpoint should populated with predefined fields
openAddEndpointExceptionFromAlertActionButton();
// As the endpoint.alerts-* is used to trigger the alert the
// file.Ext.code_signature will be auto-populated
validateExceptionConditionField('file.Ext.code_signature');
addExceptionFlyoutItemName(ITEM_NAME);
cy.get(ADD_AND_BTN).click();
// edit conditions
addExceptionEntryFieldValueAndSelectSuggestion(ADDITIONAL_ENTRY, 6);
addExceptionEntryFieldValueValue('foo', 4);
// Change the name again
editExceptionFlyoutItemName(ITEM_NAME_EDIT);
// validate the condition is still "agent.name" or got rest after the name is changed
validateExceptionConditionField(ADDITIONAL_ENTRY);
selectCloseSingleAlerts();
submitNewExceptionItem();
// Endpoint Exception will move to Endpoint List under Exception tab of rule
goToEndpointExceptionsTab();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME_EDIT);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).contains('span', ADDITIONAL_ENTRY);
});
}
);

View file

@ -0,0 +1,198 @@
/*
* 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 { tag } from '../../../../tags';
import { LOADING_INDICATOR } from '../../../../screens/security_header';
import { getEndpointRule } from '../../../../objects/rule';
import { createRule } from '../../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../../tasks/alerts_detection_rules';
import {
addExceptionFromFirstAlert,
expandFirstAlert,
openAddRuleExceptionFromAlertActionButton,
} from '../../../../tasks/alerts';
import {
addExceptionEntryFieldValue,
addExceptionEntryFieldValueValue,
addExceptionFlyoutItemName,
submitNewExceptionItem,
validateExceptionConditionField,
validateExceptionCommentCountAndText,
editExceptionFlyoutItemName,
validateHighlightedFieldsPopulatedAsExceptionConditions,
validateEmptyExceptionConditionField,
} from '../../../../tasks/exceptions';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { goToExceptionsTab } from '../../../../tasks/rule_details';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../../urls/navigation';
import { deleteAlertsAndRules } from '../../../../tasks/common';
import {
ADD_AND_BTN,
ENTRY_DELETE_BTN,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_ITEM_VIEWER_CONTAINER,
} from '../../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
describe(
'Auto populate exception with Alert data',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
const ITEM_NAME = 'Sample Exception Item';
const ITEM_NAME_EDIT = 'Sample Exception Item Edit';
const ADDITIONAL_ENTRY = 'host.hostname';
beforeEach(() => {
cy.task('esArchiverUnload', 'endpoint');
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', 'endpoint');
login();
createRule(getEndpointRule());
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();
});
after(() => {
cy.task('esArchiverUnload', 'endpoint');
deleteAlertsAndRules();
});
afterEach(() => {
cy.task('esArchiverUnload', 'endpoint');
});
it('Should create a Rule exception item from alert actions overflow menu and auto populate the conditions using alert Highlighted fields', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
addExceptionFromFirstAlert();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Validate that the comments are opened by default with one comment added
* showing a text contains information about the pre-filled conditions
*/
validateExceptionCommentCountAndText(
1,
'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):'
);
addExceptionFlyoutItemName(ITEM_NAME);
submitNewExceptionItem();
});
it('Should create a Rule exception from Alerts take action button and change multiple exception items without resetting to initial auto-prefilled entries', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
// Open first Alert Summary
expandFirstAlert();
// The Rule exception should populated with highlighted fields
openAddRuleExceptionFromAlertActionButton();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Validate that the comments are opened by default with one comment added
* showing a text contains information about the pre-filled conditions
*/
validateExceptionCommentCountAndText(
1,
'Exception conditions are pre-filled with relevant data from an alert with the alert id (_id):'
);
addExceptionFlyoutItemName(ITEM_NAME);
cy.get(ADD_AND_BTN).click();
// edit conditions
addExceptionEntryFieldValue(ADDITIONAL_ENTRY, 5);
addExceptionEntryFieldValueValue('foo', 5);
// Change the name again
editExceptionFlyoutItemName(ITEM_NAME_EDIT);
// validate the condition is still 'host.hostname' or got rest after the name is changed
validateExceptionConditionField(ADDITIONAL_ENTRY);
submitNewExceptionItem();
goToExceptionsTab();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME_EDIT);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).contains('span', 'host.hostname');
});
it('Should delete all prefilled exception entries when creating a Rule exception from Alerts take action button without resetting to initial auto-prefilled entries', () => {
cy.get(LOADING_INDICATOR).should('not.exist');
// Open first Alert Summary
expandFirstAlert();
// The Rule exception should populated with highlighted fields
openAddRuleExceptionFromAlertActionButton();
const highlightedFieldsBasedOnAlertDoc = [
'host.name',
'agent.id',
'user.name',
'process.executable',
'file.path',
];
/**
* Validate the highlighted fields are auto populated, these
* fields are based on the alert document that should be generated
* when the endpoint rule runs
*/
validateHighlightedFieldsPopulatedAsExceptionConditions(highlightedFieldsBasedOnAlertDoc);
/**
* Delete all the highlighted fields to see if any condition
* will prefuilled again.
*/
const highlightedFieldsCount = highlightedFieldsBasedOnAlertDoc.length - 1;
highlightedFieldsBasedOnAlertDoc.forEach((_, index) =>
cy
.get(ENTRY_DELETE_BTN)
.eq(highlightedFieldsCount - index)
.click()
);
/**
* Validate that there are no highlighted fields are auto populated
* after the deletion
*/
validateEmptyExceptionConditionField();
});
}
);

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import { getNewRule } from '../../../objects/rule';
@ -65,7 +66,7 @@ import { getExceptionList } from '../../../objects/exception';
// to test in enzyme and very small changes can inadvertently add
// bugs. As the complexity within the builder grows, these should
// ensure the most basic logic holds.
describe.skip('Exceptions flyout', { testIsolation: false }, () => {
describe.skip('Exceptions flyout', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
// this is a made-up index that has just the necessary

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import { getNewRule } from '../../../objects/rule';
@ -30,7 +31,7 @@ import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
describe(
'Add multiple conditions and validate the generated exceptions',
{ testIsolation: false },
{ tags: [tag.ESS, tag.SERVERLESS] },
() => {
beforeEach(() => {
cy.task('esArchiverResetKibana');

View file

@ -4,6 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import {
addExceptionEntryFieldMatchIncludedValue,
addExceptionEntryFieldValue,
@ -49,7 +51,7 @@ const goToRulesAndOpenValueListModal = () => {
openValueListsModal();
};
describe('Use Value list in exception entry', () => {
describe('Use Value list in exception entry', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cleanKibana();
login();

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import { getNewRule } from '../../../objects/rule';
@ -44,7 +45,7 @@ import {
} from '../../../screens/exceptions';
import { createEndpointExceptionList } from '../../../tasks/api_calls/exceptions';
describe('Add endpoint exception from rule details', () => {
describe('Add endpoint exception from rule details', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
const ITEM_NAME = 'Sample Exception List Item';
before(() => {

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import { getException, getExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
@ -59,7 +60,7 @@ import {
} from '../../../tasks/api_calls/exceptions';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
describe('Add/edit exception from rule details', () => {
describe('Add/edit exception from rule details', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
const ITEM_FIELD = 'unique_value.test';

View file

@ -0,0 +1,183 @@
/*
* 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 { tag } from '../../../tags';
import { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
goToClosedAlertsOnRuleDetailsPage,
goToOpenedAlertsOnRuleDetailsPage,
} from '../../../tasks/alerts';
import {
editException,
editExceptionFlyoutItemName,
submitEditedExceptionItem,
} from '../../../tasks/exceptions';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import {
addFirstExceptionFromRuleDetails,
goToAlertsTab,
goToExceptionsTab,
openEditException,
removeException,
waitForTheRuleToBeExecuted,
} from '../../../tasks/rule_details';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
import { postDataView, deleteAlertsAndRules } from '../../../tasks/common';
import {
NO_EXCEPTIONS_EXIST_PROMPT,
EXCEPTION_ITEM_VIEWER_CONTAINER,
EXCEPTION_CARD_ITEM_NAME,
EXCEPTION_CARD_ITEM_CONDITIONS,
EXCEPTION_ITEM_CONTAINER,
VALUES_INPUT,
FIELD_INPUT_PARENT,
} from '../../../screens/exceptions';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
describe(
'Add exception using data views from rule details',
{ tags: [tag.ESS, tag.SERVERLESS] },
() => {
const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert';
const ITEM_NAME = 'Sample Exception List Item';
before(() => {
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', 'exceptions');
login();
postDataView('exceptions-*');
});
after(() => {
cy.task('esArchiverUnload', 'exceptions');
});
beforeEach(() => {
deleteAlertsAndRules();
createRule(
getNewRule({
query: 'agent.name:*',
data_view_id: 'exceptions-*',
interval: '10s',
rule_id: 'rule_testing',
})
);
login();
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();
});
afterEach(() => {
cy.task('esArchiverUnload', 'exceptions_2');
});
it('Creates an exception item and close all matching alerts', () => {
goToExceptionsTab();
// when no exceptions exist, empty component shows with action to add exception
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// clicks prompt button to add first exception that will also select to close
// all matching alerts
addFirstExceptionFromRuleDetails(
{
field: 'agent.name',
operator: 'is',
values: ['foo'],
},
ITEM_NAME
);
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// Alerts table should now be empty from having added exception and closed
// matching alert
goToAlertsTab();
cy.get(EMPTY_ALERT_TABLE).should('exist');
// Closed alert should appear in table
goToClosedAlertsOnRuleDetailsPage();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`);
// Remove the exception and load an event that would have matched that exception
// to show that said exception now starts to show up again
goToExceptionsTab();
// when removing exception and again, no more exist, empty screen shows again
removeException();
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
// load more docs
cy.task('esArchiverLoad', 'exceptions_2');
// now that there are no more exceptions, the docs should match and populate alerts
goToAlertsTab();
goToOpenedAlertsOnRuleDetailsPage();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
cy.get(ALERTS_COUNT).should('exist');
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
});
it('Edits an exception item', () => {
const NEW_ITEM_NAME = 'Exception item-EDITED';
const ITEM_FIELD = 'unique_value.test';
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
goToExceptionsTab();
// add item to edit
addFirstExceptionFromRuleDetails(
{
field: ITEM_FIELD,
operator: 'is',
values: ['foo'],
},
ITEM_NAME
);
// displays existing exception items
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' unique_value.testIS foo');
// open edit exception modal
openEditException();
// edit exception item name
editExceptionFlyoutItemName(NEW_ITEM_NAME);
// check that the existing item's field is being populated
cy.get(EXCEPTION_ITEM_CONTAINER)
.eq(0)
.find(FIELD_INPUT_PARENT)
.eq(0)
.should('have.text', ITEM_FIELD);
cy.get(VALUES_INPUT).should('have.text', 'foo');
// edit conditions
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
// submit
submitEditedExceptionItem();
// new exception item displays
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
// check that updates stuck
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', NEW_ITEM_NAME);
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should('have.text', ' agent.nameIS foo');
});
}
);

View file

@ -4,10 +4,11 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ROLES } from '@kbn/security-solution-plugin/common/test';
import { tag } from '../../../tags';
import { getExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
import { ROLES } from '../../../../common/test';
import { createRule } from '../../../tasks/api_calls/rules';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details';
@ -27,7 +28,7 @@ import {
deleteExceptionList,
} from '../../../tasks/api_calls/exceptions';
describe('Exceptions viewer read only', () => {
describe('Exceptions viewer read only', { tags: tag.ESS }, () => {
const exceptionList = getExceptionList();
before(() => {

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../../tags';
import { getExceptionList } from '../../../../objects/exception';
import { getNewRule } from '../../../../objects/rule';
@ -40,7 +41,7 @@ const getExceptionList1 = () => ({
const EXCEPTION_LIST_NAME = 'Newly created list';
describe('Exception list detail page', () => {
describe('Exception list detail page', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
login();

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../tags';
import { getNewRule } from '../../../objects/rule';
import { login, visitWithoutDateRange } from '../../../tasks/login';
@ -38,7 +39,7 @@ import {
waitForExceptionsTableToBeLoaded,
} from '../../../tasks/exceptions_table';
describe('Add, edit and delete exception', () => {
describe('Add, edit and delete exception', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
before(() => {
cy.task('esArchiverResetKibana');
cy.task('esArchiverLoad', 'exceptions');

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../../tags';
import { createRule } from '../../../../tasks/api_calls/rules';
import { getExceptionList } from '../../../../objects/exception';
@ -40,7 +41,7 @@ const getExceptionList2 = () => ({
list_id: 'exception_list_2',
});
describe('Duplicate List', () => {
describe('Duplicate List', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
beforeEach(() => {
cy.task('esArchiverResetKibana');
login();

View file

@ -4,6 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { tag } from '../../../../tags';
import { getExceptionList } from '../../../../objects/exception';
import { getNewRule } from '../../../../objects/rule';
import {
@ -34,7 +36,7 @@ const getExceptionList2 = () => ({
name: EXCEPTION_LIST_NAME_TWO,
list_id: 'exception_list_2',
});
describe('Filter Lists', () => {
describe('Filter Lists', { tags: [tag.ESS, tag.SERVERLESS] }, () => {
beforeEach(() => {
cy.task('esArchiverResetKibana');
login();

Some files were not shown because too many files have changed in this diff Show more