From 4901da6b083eff4bf6157b683a127ef5b58ec46e Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Sun, 6 Apr 2025 17:51:21 -0700 Subject: [PATCH 1/3] testing out if we can simplify our rule creation cypress tests --- .../basic_license_check_rule_details.cy.ts} | 54 +++- .../basic_license_check_rule_form.cy.ts} | 16 +- .../alert_suppression/custom_query_rule.cy.ts | 85 ++++++ .../eql_rule.cy.ts} | 2 +- .../eql_sequence_rule.cy.ts} | 6 +- .../alert_suppression/esql_rule.cy.ts | 107 +++++++ .../essentials_license_check_rule_form.cy.ts} | 2 +- .../indicator_match_rule.cy.ts} | 2 +- .../machine_learning_rule.cy.ts} | 2 +- .../alert_suppression/new_terms_rule.cy.ts | 86 ++++++ .../alert_suppression/threshold_rule.cy.ts | 68 +++++ .../rule_creation/common_flows.cy.ts | 11 + .../rule_creation/custom_query_rule.cy.ts | 113 ++------ .../custom_query_rule_data_view.cy.ts | 195 +++---------- .../custom_saved_query_rule.cy.ts | 261 +++--------------- .../rule_creation/eql_rule.cy.ts | 187 ++----------- .../eql_rule_suppression_ess_basic.cy.ts | 93 ------- ...le_suppression_serverless_essentials.cy.ts | 74 ----- .../rule_creation/esql_rule.cy.ts | 69 +---- .../rule_creation/indicator_match_rule.cy.ts | 2 +- ...le_suppression_serverless_essentials.cy.ts | 70 ----- .../rule_creation/machine_learning_rule.cy.ts | 101 +------ .../rule_creation/new_terms_rule.cy.ts | 191 ++----------- .../rule_creation/override.cy.ts | 144 +--------- .../rule_creation/threshold_rule.cy.ts | 139 +--------- .../rule_edit/custom_saved_query.cy.ts | 189 +++++++++++++ 26 files changed, 780 insertions(+), 1489 deletions(-) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/indicator_match_rule_suppression_ess_basic.cy.ts => alert_suppression/basic_license_check_rule_details.cy.ts} (58%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/common_flows_supression_ess_basic.cy.ts => alert_suppression/basic_license_check_rule_form.cy.ts} (79%) create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/eql_rule_suppression.cy.ts => alert_suppression/eql_rule.cy.ts} (98%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/eql_rule_suppression_sequence.cy.ts => alert_suppression/eql_sequence_rule.cy.ts} (94%) create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/esql_rule.cy.ts rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/common_flows_suppression_serverless_essentials.cy.ts => alert_suppression/essentials_license_check_rule_form.cy.ts} (96%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/indicator_match_rule_suppression.cy.ts => alert_suppression/indicator_match_rule.cy.ts} (98%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/{rule_creation/machine_learning_rule_suppression.cy.ts => alert_suppression/machine_learning_rule.cy.ts} (99%) create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts delete mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts delete mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts delete mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_serverless_essentials.cy.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_ess_basic.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_details.cy.ts similarity index 58% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_ess_basic.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_details.cy.ts index 6223ac017281d..176af020ac052 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_ess_basic.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_details.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getNewThreatIndicatorRule } from '../../../../objects/rule'; +import { getEqlRule, getNewThreatIndicatorRule } from '../../../../objects/rule'; import { SUPPRESS_FOR_DETAILS, @@ -13,6 +13,7 @@ import { SUPPRESS_MISSING_FIELD, DEFINITION_DETAILS, ALERT_SUPPRESSION_INSUFFICIENT_LICENSING_ICON, + DETAILS_TITLE, } from '../../../../screens/rule_details'; import { startBasicLicense } from '../../../../tasks/api_calls/licensing'; @@ -28,19 +29,19 @@ import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; const SUPPRESS_BY_FIELDS = ['myhash.mysha256', 'source.ip.keyword']; describe( - 'Detection rules, Indicator Match, Alert Suppression', + 'Alert Suppression license check - Rule Details', { tags: ['@ess'], }, () => { - describe('Create rule form', () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - startBasicLicense(); - }); + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + startBasicLicense(); + }); + describe('Indicator match', () => { it('shows upselling message on rule details with suppression on basic license', () => { const rule = getNewThreatIndicatorRule(); @@ -71,5 +72,40 @@ describe( }); }); }); + + describe('EQL rule', () => { + it('shows an upselling message on rule suppression details', () => { + const rule = getEqlRule(); + + createRule({ + ...rule, + alert_suppression: { + group_by: SUPPRESS_BY_FIELDS, + duration: { value: 360, unit: 's' }, + missing_fields_strategy: 'doNotSuppress', + }, + }).then((createdRule) => { + visit(ruleDetailsUrl(createdRule.body.id)); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '360s'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + + // suppression functionality should be under Tech Preview + cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).contains('Technical Preview'); + }); + + // Platinum license is required for configuration to apply + cy.get(ALERT_SUPPRESSION_INSUFFICIENT_LICENSING_ICON).eq(2).trigger('mouseover'); + cy.get(TOOLTIP).contains( + 'Alert suppression is configured but will not be applied due to insufficient licensing' + ); + }); + }); + }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts similarity index 79% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts index 99f9e1c145796..87b7da95bb7b6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_supression_ess_basic.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts @@ -10,6 +10,8 @@ import { ALERT_SUPPRESSION_DURATION_VALUE_INPUT, MACHINE_LEARNING_TYPE, ALERT_SUPPRESSION_DURATION_UNIT_INPUT, + ALERT_SUPPRESSION_FIELDS_INPUT, + ALERT_SUPPRESSION_FIELDS, } from '../../../../screens/create_new_rule'; import { @@ -18,6 +20,7 @@ import { selectThresholdRuleType, selectEsqlRuleType, openSuppressionFieldsTooltipAndCheckLicense, + selectEqlRuleType, } from '../../../../tasks/create_new_rule'; import { startBasicLicense } from '../../../../tasks/api_calls/licensing'; import { login } from '../../../../tasks/login'; @@ -28,12 +31,11 @@ import { TOOLTIP } from '../../../../screens/common'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; describe( - 'Detection rules, Common flows Alert Suppression', + 'Alert Suppression basic license check - Rule Form', { tags: ['@ess'], }, () => { - describe('Create rule form', () => { beforeEach(() => { deleteAlertsAndRules(); login(); @@ -41,7 +43,7 @@ describe( startBasicLicense(); }); - it('can not create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { + it('cannot create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { // Default query rule openSuppressionFieldsTooltipAndCheckLicense(); @@ -54,6 +56,13 @@ describe( selectEsqlRuleType(); openSuppressionFieldsTooltipAndCheckLicense(); + selectEqlRuleType(); + cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); + + // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this + cy.get(TOOLTIP).contains('Platinum license'); + // ML Rules require Platinum license cy.get(MACHINE_LEARNING_TYPE).get('button').should('be.disabled'); @@ -65,7 +74,6 @@ describe( cy.get(ALERT_SUPPRESSION_DURATION_VALUE_INPUT).should('be.disabled'); cy.get(ALERT_SUPPRESSION_DURATION_UNIT_INPUT).should('be.disabled'); - }); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts new file mode 100644 index 0000000000000..6cfd441499792 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts @@ -0,0 +1,85 @@ +/* + * 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 { + DEFINITION_DETAILS, + SUPPRESS_FOR_DETAILS, + SUPPRESS_BY_DETAILS, + SUPPRESS_MISSING_FIELD, + DETAILS_TITLE, +} from '../../../../screens/rule_details'; +import { ALERT_SUPPRESSION_FIELDS } from '../../../../screens/create_new_rule'; + +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { + fillAboutRuleMinimumAndContinue, + createRuleWithoutEnabling, + fillAlertSuppressionFields, + skipScheduleRuleAction, + continueFromDefineStep, + fillCustomQueryInput, +} from '../../../../tasks/create_new_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { getDetails } from '../../../../tasks/rule_details'; +import { CREATE_RULE_URL } from '../../../../urls/navigation'; + +describe('Custom Query Rule - Alert suppression', { tags: ['@ess', '@serverless'] }, () => { + const rule = getNewRule(); + + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + }); + + const SUPPRESS_BY_FIELDS = ['source.ip']; + + it('creates rule with suppression', () => { + fillCustomQueryInput('*'); + fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); + // alert suppression fields input should not have Technical Preview label + cy.get(ALERT_SUPPRESSION_FIELDS).should('not.contain.text', 'Technical Preview'); + continueFromDefineStep(); + + // ensures details preview works correctly + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Suppress and group alerts for events with missing fields' + ); + + // suppression functionality should be in GA + cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).should( + 'not.contain.text', + 'Technical Preview' + ); + }); + + fillAboutRuleMinimumAndContinue(rule); + skipScheduleRuleAction(); + createRuleWithoutEnabling(); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Suppress and group alerts for events with missing fields' + ); + + // suppression functionality should be in GA + cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).should( + 'not.contain.text', + 'Technical Preview' + ); + }); + }); +}); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_rule.cy.ts similarity index 98% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_rule.cy.ts index 8f4b6a10faeb8..9f9abceaad182 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_rule.cy.ts @@ -36,7 +36,7 @@ const SUPPRESS_BY_FIELDS = ['agent.type']; // Skip in MKI due to flake describe( - 'Detection Rule Creation - EQL Rules - With Alert Suppression', + 'Detection EQL Rules - Alert suppression', { tags: ['@ess', '@skipInServerlessMKI'], }, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_sequence.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_sequence_rule.cy.ts similarity index 94% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_sequence.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_sequence_rule.cy.ts index 2cf8526a2f9c3..ffc65fdddbf8b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_sequence.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/eql_sequence_rule.cy.ts @@ -35,11 +35,9 @@ import { const SUPPRESS_BY_FIELDS = ['agent.type']; describe( - 'Detection Rule Creation - EQL Rules - With Alert Suppression', + 'EQL Rules - Alert suppression', { - // skipped in MKI as it depends on feature flag alertSuppressionForEsqlRuleEnabled - // alertSuppressionForEsqlRuleEnabled feature flag is also enabled in a global config - tags: ['@ess', '@skipInServerlessMKI'], + tags: ['@ess', '@serverless'], env: { kbnServerArgs: [ `--xpack.securitySolution.enableExperimental=${JSON.stringify([ diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/esql_rule.cy.ts new file mode 100644 index 0000000000000..a6ef61db06a1d --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/esql_rule.cy.ts @@ -0,0 +1,107 @@ +/* + * 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 { getEsqlRule } from '../../../../objects/rule'; +import { + DEFINITION_DETAILS, + SUPPRESS_BY_DETAILS, + SUPPRESS_FOR_DETAILS, + SUPPRESS_MISSING_FIELD, +} from '../../../../screens/rule_details'; +import { getDetails } from '../../../../tasks/rule_details'; +import { + selectEsqlRuleType, + fillEsqlQueryBar, + createRuleWithoutEnabling, + fillAlertSuppressionFields, + selectAlertSuppressionPerInterval, + setAlertSuppressionDuration, + selectDoNotSuppressForMissingFields, + continueFromDefineStep, + fillAboutRuleMinimumAndContinue, + skipScheduleRuleAction, + interceptEsqlQueryFieldsRequest, +} from '../../../../tasks/create_new_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; + +import { CREATE_RULE_URL } from '../../../../urls/navigation'; + +// https://github.com/cypress-io/cypress/issues/22113 +// issue is inside monaco editor, used in ES|QL query input +// calling it after visiting page in each tests, seems fixes the issue +// the only other alternative is patching ResizeObserver, which is something I would like to avoid +const workaroundForResizeObserver = () => + cy.on('uncaught:exception', (err) => { + if (err.message.includes('ResizeObserver loop limit exceeded')) { + return false; + } + }); + +describe( + 'Detection ES|QL - Alert suppression', + { + tags: ['@ess', '@serverless'], + }, + () => { + const rule = getEsqlRule(); + + beforeEach(() => { + login(); + visit(CREATE_RULE_URL); + }); + + it('shows custom ES|QL field in investigation fields autocomplete and saves it in rule', function () { + const CUSTOM_ESQL_FIELD = '_custom_agent_name'; + const SUPPRESS_BY_FIELDS = [CUSTOM_ESQL_FIELD, 'agent.type']; + + const queryWithCustomFields = [ + `from auditbeat* metadata _id, _version, _index`, + `eval ${CUSTOM_ESQL_FIELD} = agent.name`, + `drop agent.*`, + ].join(' | '); + + workaroundForResizeObserver(); + + selectEsqlRuleType(); + + interceptEsqlQueryFieldsRequest(queryWithCustomFields, 'esqlSuppressionFieldsRequest'); + fillEsqlQueryBar(queryWithCustomFields); + + cy.wait('@esqlSuppressionFieldsRequest'); + fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); + selectAlertSuppressionPerInterval(); + setAlertSuppressionDuration(2, 'h'); + selectDoNotSuppressForMissingFields(); + continueFromDefineStep(); + + // ensures details preview works correctly + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + + fillAboutRuleMinimumAndContinue(rule); + skipScheduleRuleAction(); + createRuleWithoutEnabling(); + + // ensures rule details displayed correctly after rule created + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/essentials_license_check_rule_form.cy.ts similarity index 96% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/essentials_license_check_rule_form.cy.ts index 013ed1d6f4f88..707f476a588da 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows_suppression_serverless_essentials.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/essentials_license_check_rule_form.cy.ts @@ -21,7 +21,7 @@ import { import { CREATE_RULE_URL } from '../../../../urls/navigation'; describe( - 'Detection rules, Alert Suppression for Essentials tier', + 'Alert Suppression - Essentials tier license check', { tags: ['@serverless'], env: { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/indicator_match_rule.cy.ts similarity index 98% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/indicator_match_rule.cy.ts index d0539683e5a64..576a22bbd075d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/indicator_match_rule.cy.ts @@ -36,7 +36,7 @@ import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; const SUPPRESS_BY_FIELDS = ['myhash.mysha256', 'source.ip.keyword']; describe( - 'Detection rules, Indicator Match, Alert Suppression', + 'Indicator Match - Alert suppression', { tags: ['@ess', '@serverless'], }, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule_suppression.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/machine_learning_rule.cy.ts similarity index 99% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule_suppression.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/machine_learning_rule.cy.ts index 3ce0003dccf90..8eaac1fcb72c0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule_suppression.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/machine_learning_rule.cy.ts @@ -40,7 +40,7 @@ import { getDetails } from '../../../../tasks/rule_details'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; describe( - 'Machine Learning Detection Rules - Creation', + 'Machine Learning Detection Rules - Alert suppression', { tags: ['@ess', '@serverless'], }, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts new file mode 100644 index 0000000000000..00c180c0e90fe --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts @@ -0,0 +1,86 @@ +/* + * 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 { getNewTermsRule } from '../../../../objects/rule'; + +import { + DEFINITION_DETAILS, + SUPPRESS_BY_DETAILS, + SUPPRESS_FOR_DETAILS, + SUPPRESS_MISSING_FIELD +} from '../../../../screens/rule_details'; + +import { getDetails } from '../../../../tasks/rule_details'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { + fillDefineNewTermsRule, + selectNewTermsRuleType, + fillAlertSuppressionFields, + fillAboutRuleMinimumAndContinue, + createRuleWithoutEnabling, + skipScheduleRuleAction, + continueFromDefineStep, + selectAlertSuppressionPerInterval, + setAlertSuppressionDuration, + selectDoNotSuppressForMissingFields, +} from '../../../../tasks/create_new_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { CREATE_RULE_URL } from '../../../../urls/navigation'; + +describe( + 'New Terms Rule - Alert suppression', + { + tags: ['@ess', '@serverless'], + }, + () => { + const rule = getNewTermsRule(); + + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + selectNewTermsRuleType(); + }); + + it('With time interval suppression', () => { + const SUPPRESS_BY_FIELDS = ['agent.hostname', 'agent.type']; + + fillDefineNewTermsRule(rule); + + // fill suppress by fields and select non-default suppression options + fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); + selectAlertSuppressionPerInterval(); + setAlertSuppressionDuration(45, 'm'); + selectDoNotSuppressForMissingFields(); + continueFromDefineStep(); + + // ensures details preview works correctly + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '45m'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + + fillAboutRuleMinimumAndContinue(rule); + skipScheduleRuleAction(); + createRuleWithoutEnabling(); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '45m'); + getDetails(SUPPRESS_MISSING_FIELD).should( + 'have.text', + 'Do not suppress alerts for events with missing fields' + ); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts new file mode 100644 index 0000000000000..b52f43cf8142f --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts @@ -0,0 +1,68 @@ +/* + * 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 { getNewThresholdRule } from '../../../../objects/rule'; +import { + DEFINITION_DETAILS, + SUPPRESS_FOR_DETAILS, +} from '../../../../screens/rule_details'; +import { goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; + +import { + createRuleWithoutEnabling, + fillAboutRuleMinimumAndContinue, + enablesAndPopulatesThresholdSuppression, + skipScheduleRuleAction, + selectThresholdRuleType, + fillDefineThresholdRule, + continueFromDefineStep, +} from '../../../../tasks/create_new_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { getDetails } from '../../../../tasks/rule_details'; +import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; +import { CREATE_RULE_URL } from '../../../../urls/navigation'; + +describe( + 'Threshold Rule - Alert suppression', + { + tags: ['@ess', '@serverless'], + }, + () => { + const rule = getNewThresholdRule(); + + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + }); + + it('Creates a new threshold rule with suppression enabled', () => { + selectThresholdRuleType(); + + fillDefineThresholdRule(rule); + enablesAndPopulatesThresholdSuppression(5, 'h'); + continueFromDefineStep(); + + // ensures duration displayed on define step in preview mode + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '5h'); + }); + + fillAboutRuleMinimumAndContinue(rule); + skipScheduleRuleAction(); + createRuleWithoutEnabling(); + openRuleManagementPageViaBreadcrumbs(); + goToRuleDetailsOf(rule.name); + + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '5h'); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts index f7f343c11b674..3b7b054ec01f1 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts @@ -43,10 +43,13 @@ import { fillThreatSubtechnique, fillThreatTechnique, importSavedQuery, + waitForAlertsToPopulate, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; import { visit } from '../../../../tasks/navigation'; +import { waitForTheRuleToBeExecuted } from '../../../../tasks/rule_details'; +import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../../screens/alerts'; // This test is meant to test touching all the common various components in rule creation // to ensure we don't miss any changes that maybe affect one of these more obscure UI components @@ -111,5 +114,13 @@ describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => cy.get(DESCRIPTION_SETUP_GUIDE_BUTTON).click(); cy.get(DESCRIPTION_SETUP_GUIDE_CONTENT).should('contain', 'test setup markdown'); // Markdown formatting should be removed + + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); + + cy.get(ALERTS_COUNT) + .invoke('text') + .should('match', /^[1-9].+$/); + cy.get(ALERT_GRID_CELL).contains(ruleFields.ruleName); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts index 4bb45e03a15fa..70283f3be6026 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts @@ -6,15 +6,7 @@ */ import { getNewRule } from '../../../../objects/rule'; -import { - RULE_NAME_HEADER, - DEFINITION_DETAILS, - SUPPRESS_FOR_DETAILS, - SUPPRESS_BY_DETAILS, - SUPPRESS_MISSING_FIELD, - DETAILS_TITLE, -} from '../../../../screens/rule_details'; -import { ALERT_SUPPRESSION_FIELDS } from '../../../../screens/create_new_rule'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; import { GLOBAL_SEARCH_BAR_FILTER_ITEM } from '../../../../screens/search_bar'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; @@ -24,101 +16,42 @@ import { fillDefineCustomRuleAndContinue, createRuleWithoutEnabling, fillDefineCustomRule, - openAddFilterPopover, - fillAlertSuppressionFields, - skipScheduleRuleAction, - continueFromDefineStep, - fillCustomQueryInput, + openAddFilterPopover } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { getDetails } from '../../../../tasks/rule_details'; import { fillAddFilterForm } from '../../../../tasks/search_bar'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -describe('Create custom query rule', { tags: ['@ess', '@serverless'] }, () => { +describe('Custom query rule - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { const rule = getNewRule(); beforeEach(() => { deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); }); - describe('Custom detection rules creation', () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - }); - - it('Creates and enables a rule', function () { - fillDefineCustomRuleAndContinue(rule); - fillAboutRuleMinimumAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); - - cy.log('Asserting we have a new rule created'); - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - }); - - // FLAKEY - see https://github.com/elastic/kibana/issues/182891 - it('Adds filter on define step', { tags: ['@skipInServerless'] }, () => { - visit(CREATE_RULE_URL); - fillDefineCustomRule(rule); - openAddFilterPopover(); - fillAddFilterForm({ - key: 'host.name', - operator: 'exists', - }); - // Check that newly added filter exists - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); - }); + it('Creates and enables a rule', function () { + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleMinimumAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - // https://github.com/elastic/kibana/issues/187277 - describe('Alert suppression', { tags: ['@skipInServerlessMKI'] }, () => { - const SUPPRESS_BY_FIELDS = ['source.ip']; - - it('creates rule with suppression', () => { - fillCustomQueryInput('*'); - fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); - // alert suppression fields input should not have Technical Preview label - cy.get(ALERT_SUPPRESSION_FIELDS).should('not.contain.text', 'Technical Preview'); - continueFromDefineStep(); - - // ensures details preview works correctly - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Suppress and group alerts for events with missing fields' - ); - - // suppression functionality should be in GA - cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).should( - 'not.contain.text', - 'Technical Preview' - ); - }); - - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); - createRuleWithoutEnabling(); - - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', 'One rule execution'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Suppress and group alerts for events with missing fields' - ); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); - // suppression functionality should be in GA - cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).should( - 'not.contain.text', - 'Technical Preview' - ); - }); - }); + // FLAKEY - see https://github.com/elastic/kibana/issues/182891 + it('Adds filter on define step', { tags: ['@skipInServerless'] }, () => { + visit(CREATE_RULE_URL); + fillDefineCustomRule(rule); + openAddFilterPopover(); + fillAddFilterForm({ + key: 'host.name', + operator: 'exists', }); + // Check that newly added filter exists + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts index 50f43149b9b3e..515289c0cfe70 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts @@ -5,198 +5,67 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; import { getDataViewRule } from '../../../../objects/rule'; -import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../../screens/alerts'; -import { - CUSTOM_RULES_BTN, - RISK_SCORE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_CONTINUE_BTN, - RULE_DESCRIPTION_INPUT, - RULE_NAME_INPUT, -} from '../../../../screens/create_new_rule'; - -import { - ADDITIONAL_LOOK_BACK_DETAILS, - ABOUT_DETAILS, - ABOUT_INVESTIGATION_NOTES, - ABOUT_RULE_DESCRIPTION, - CUSTOM_QUERY_DETAILS, - DEFINITION_DETAILS, - FALSE_POSITIVES_DETAILS, - removeExternalLinkText, - INDEX_PATTERNS_DETAILS, - INVESTIGATION_NOTES_MARKDOWN, - INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - DATA_VIEW_DETAILS, - EDIT_RULE_SETTINGS_LINK, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; import { GLOBAL_SEARCH_BAR_FILTER_ITEM } from '../../../../screens/search_bar'; -import { - getRulesManagementTableRows, - goToRuleDetailsOf, -} from '../../../../tasks/alerts_detection_rules'; import { deleteAlertsAndRules, deleteDataView, postDataView, } from '../../../../tasks/api_calls/common'; import { - createAndEnableRule, createRuleWithoutEnabling, fillAboutRuleAndContinue, fillDefineCustomRule, fillDefineCustomRuleAndContinue, fillScheduleRuleAndContinue, openAddFilterPopover, - waitForAlertsToPopulate, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; -import { getDetails, waitForTheRuleToBeExecuted } from '../../../../tasks/rule_details'; import { fillAddFilterForm } from '../../../../tasks/search_bar'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -// Skipping in MKI due to flake -describe('Custom query rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { - describe('Custom detection rules creation with data views', () => { - const rule = getDataViewRule(); - const expectedUrls = rule.references?.join(''); - const expectedFalsePositives = rule.false_positives?.join(''); - const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); - const expectedNumberOfRules = 1; - - beforeEach(() => { - if (rule.data_view_id != null) { - postDataView(rule.data_view_id); - } - deleteAlertsAndRules(); - login(); - }); - - afterEach(() => { - if (rule.data_view_id != null) { - deleteDataView(rule.data_view_id); - } - }); - - it('Creates and enables a new rule', function () { - visit(CREATE_RULE_URL); - fillDefineCustomRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - getRulesManagementTableRows().should('have.length', expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'High'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); +describe('Custom query rules with data views - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { + const rule = getDataViewRule(); - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'High'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(DATA_VIEW_DETAILS).should('have.text', rule.data_view_id); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - }); - cy.get(DEFINITION_DETAILS).should('not.contain', INDEX_PATTERNS_DETAILS); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); - - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT) - .invoke('text') - .should('match', /^[1-9].+$/); - cy.get(ALERT_GRID_CELL).contains(rule.name); - }); - - it('Creates and edits a new rule with a data view', function () { - visit(CREATE_RULE_URL); - fillDefineCustomRuleAndContinue(rule); - cy.get(RULE_NAME_INPUT).clear(); - cy.get(RULE_NAME_INPUT).type(rule.name); - cy.get(RULE_DESCRIPTION_INPUT).clear(); - cy.get(RULE_DESCRIPTION_INPUT).type(rule.description); - - cy.get(ABOUT_CONTINUE_BTN).should('exist').click(); - - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); - openRuleManagementPageViaBreadcrumbs(); + beforeEach(() => { + if (rule.data_view_id != null) { + postDataView(rule.data_view_id); + } + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + }); - goToRuleDetailsOf(rule.name); + afterEach(() => { + if (rule.data_view_id != null) { + deleteDataView(rule.data_view_id); + } + }); - cy.get(EDIT_RULE_SETTINGS_LINK).click(); + it('Creates and enables a new rule', function () { + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.get(RULE_NAME_HEADER).should('contain', 'Edit rule settings'); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); - it('Adds filter on define step', () => { - visit(CREATE_RULE_URL); - fillDefineCustomRule(rule); - openAddFilterPopover(); - fillAddFilterForm({ - key: 'host.name', - operator: 'exists', - }); - // Check that newly added filter exists - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); + it('Adds filter on define step', () => { + fillDefineCustomRule(rule); + openAddFilterPopover(); + fillAddFilterForm({ + key: 'host.name', + operator: 'exists', }); + // Check that newly added filter exists + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts index 48f2bd4002f3b..c03f992e79305 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts @@ -5,259 +5,84 @@ * 2.0. */ -import { getNewRule, getSavedQueryRule } from '../../../../objects/rule'; +import { getSavedQueryRule } from '../../../../objects/rule'; import { DEFINE_CONTINUE_BUTTON, - LOAD_QUERY_DYNAMICALLY_CHECKBOX, QUERY_BAR, } from '../../../../screens/create_new_rule'; import { TOASTER } from '../../../../screens/alerts_detection_rules'; -import { - RULE_NAME_HEADER, - SAVED_QUERY_NAME_DETAILS, - SAVED_QUERY_DETAILS, - SAVED_QUERY_FILTERS_DETAILS, - DEFINE_RULE_PANEL_PROGRESS, - CUSTOM_QUERY_DETAILS, -} from '../../../../screens/rule_details'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; -import { editFirstRule, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { createSavedQuery, deleteSavedQueries } from '../../../../tasks/api_calls/saved_queries'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { - createAndEnableRule, fillAboutRuleAndContinue, fillScheduleRuleAndContinue, selectAndLoadSavedQuery, getCustomQueryInput, checkLoadQueryDynamically, - uncheckLoadQueryDynamically, + createRuleWithoutEnabling, } from '../../../../tasks/create_new_rule'; -import { saveEditedRule, visitEditRulePage } from '../../../../tasks/edit_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { - assertDetailsNotExist, - getDetails, - visitRuleDetailsPage, -} from '../../../../tasks/rule_details'; +import { visitRuleDetailsPage } from '../../../../tasks/rule_details'; import { createRule } from '../../../../tasks/api_calls/rules'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; const savedQueryName = 'custom saved query'; const savedQueryQuery = 'process.name: test'; const savedQueryFilterKey = 'testAgent.value'; -describe('Saved query rules', { tags: ['@ess', '@serverless'] }, () => { - describe('Custom saved_query detection rule creation', () => { - beforeEach(() => { - login(); - deleteAlertsAndRules(); - deleteSavedQueries(); - }); - - it('Creates saved query rule', function () { - const rule = getSavedQueryRule(); - createSavedQuery(savedQueryName, savedQueryQuery, savedQueryFilterKey); - visit(CREATE_RULE_URL); - - selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); - - // edit loaded saved query - getCustomQueryInput() - .type(' AND random query') - .should('have.value', [savedQueryQuery, ' AND random query'].join('')); - - // when clicking load query dynamically checkbox, saved query should be shown in query input and input should be disabled - checkLoadQueryDynamically(); - getCustomQueryInput().should('have.value', savedQueryQuery).should('be.disabled'); - cy.get(QUERY_BAR).should('contain', savedQueryFilterKey); - - cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click(); - - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - cy.intercept('POST', '/api/detection_engine/rules').as('savedQueryRule'); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.wait('@savedQueryRule').then(({ response }) => { - // created rule should have saved_query type - cy.wrap(response?.body.type).should('equal', 'saved_query'); - }); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - - cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); - - getDetails(SAVED_QUERY_NAME_DETAILS).should('contain', savedQueryName); - getDetails(SAVED_QUERY_DETAILS).should('contain', savedQueryQuery); - getDetails(SAVED_QUERY_FILTERS_DETAILS).should('contain', savedQueryFilterKey); - }); - - context('Non existent saved query', () => { - const FAILED_TO_LOAD_ERROR = 'Failed to load the saved query'; - - describe('on rule details page', () => { - beforeEach(() => { - createRule( - getSavedQueryRule({ - saved_id: 'non-existent', - query: undefined, - }) - ).then((rule) => visitRuleDetailsPage(rule.body.id)); - }); - - it('Shows error toast on details page when saved query can not be loaded', function () { - cy.get(TOASTER).should('contain', FAILED_TO_LOAD_ERROR); - }); - }); - - describe('on rule editing page', () => { - beforeEach(() => { - createRule( - getSavedQueryRule({ - saved_id: 'non-existent', - query: undefined, - }) - ).then((rule) => visitEditRulePage(rule.body.id)); - }); - - it('Shows validation error on rule edit when saved query can not be loaded', function () { - cy.get(TOASTER).should('contain', FAILED_TO_LOAD_ERROR); - }); - - // https://github.com/elastic/kibana/issues/187623 - it( - 'Allows to update saved_query rule with non-existent query', - { tags: ['@skipInServerlessMKI'] }, - () => { - cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).should('exist'); - - cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); - saveEditedRule(); - - cy.wait('@editedRule').then(({ response }) => { - // updated rule type shouldn't change - cy.wrap(response?.body.type).should('equal', 'saved_query'); - }); - - cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); - - assertDetailsNotExist(SAVED_QUERY_NAME_DETAILS); - assertDetailsNotExist(SAVED_QUERY_DETAILS); - } - ); - }); - }); - - context('Editing', () => { - it('Allows to update query rule as saved_query rule type', () => { - createSavedQuery(savedQueryName, savedQueryQuery); - createRule(getNewRule()).then((rule) => visitEditRulePage(rule.body.id)); - - selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); - checkLoadQueryDynamically(); - - cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); - saveEditedRule(); - - cy.wait('@editedRule').then(({ response }) => { - // updated rule should be saved as saved_query type once Load query dynamically checkbox was checked - cy.wrap(response?.body.type).should('equal', 'saved_query'); - }); - - cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); - - getDetails(SAVED_QUERY_NAME_DETAILS).should('contain', savedQueryName); - getDetails(SAVED_QUERY_DETAILS).should('contain', savedQueryQuery); - }); - - it('Allows to update saved_query rule as query rule type', () => { - const expectedCustomTestQuery = 'random test query'; - createSavedQuery(savedQueryName, savedQueryQuery).then((response) => { - cy.log(JSON.stringify(response.body, null, 2)); - createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then( - (rule) => visitEditRulePage(rule.body.id) - ); - }); - - // query input should be disabled and has value of saved query - getCustomQueryInput().should('have.value', savedQueryQuery).should('be.disabled'); - - // after unchecking Load Query Dynamically checkbox, query input becomes enabled, type custom query - uncheckLoadQueryDynamically(); - getCustomQueryInput().should('be.enabled').clear().type(expectedCustomTestQuery); - - cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); - saveEditedRule(); +describe('Saved query rules - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { + beforeEach(() => { + login(); + deleteAlertsAndRules(); + deleteSavedQueries(); + }); - cy.wait('@editedRule').then(({ response }) => { - // updated rule should be saved as query type once Load query dynamically checkbox was unchecked - cy.wrap(response?.body.type).should('equal', 'query'); - }); + it('Creates saved query rule', function () { + const rule = getSavedQueryRule(); + createSavedQuery(savedQueryName, savedQueryQuery, savedQueryFilterKey); + visit(CREATE_RULE_URL); - getDetails(CUSTOM_QUERY_DETAILS).should('contain', expectedCustomTestQuery); - }); + selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); - it('Allows to update saved_query rule with non-existent query by adding custom query', () => { - const expectedCustomTestQuery = 'random test query'; - createRule(getSavedQueryRule({ saved_id: 'non-existent', query: undefined })).then((rule) => - visitEditRulePage(rule.body.id) - ); + // edit loaded saved query + getCustomQueryInput() + .type(' AND random query') + .should('have.value', [savedQueryQuery, ' AND random query'].join('')); - uncheckLoadQueryDynamically(); + // when clicking load query dynamically checkbox, saved query should be shown in query input and input should be disabled + checkLoadQueryDynamically(); + getCustomQueryInput().should('have.value', savedQueryQuery).should('be.disabled'); + cy.get(QUERY_BAR).should('contain', savedQueryFilterKey); - // type custom query, ensure Load dynamically checkbox is absent, as rule can't be saved win non valid saved query - getCustomQueryInput().type(expectedCustomTestQuery); - cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).should('not.visible'); + cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click(); - cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); - saveEditedRule(); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.wait('@editedRule').then(({ response }) => { - // updated rule should be saved as query type once Load query dynamically checkbox was unchecked - cy.wrap(response?.body.type).should('equal', 'query'); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); - getDetails(CUSTOM_QUERY_DETAILS).should('contain', expectedCustomTestQuery); + context('Non existent saved query', () => { + const FAILED_TO_LOAD_ERROR = 'Failed to load the saved query'; + + describe('on rule details page', () => { + beforeEach(() => { + createRule( + getSavedQueryRule({ + saved_id: 'non-existent', + query: undefined, + }) + ).then((rule) => visitRuleDetailsPage(rule.body.id)); }); - it('Allows to update saved_query rule with non-existent query by selecting another saved query', () => { - createSavedQuery(savedQueryName, savedQueryQuery); - createRule(getSavedQueryRule({ saved_id: 'non-existent', query: undefined })).then((rule) => - visitEditRulePage(rule.body.id) - ); - - visit(RULES_MANAGEMENT_URL); - - editFirstRule(); - uncheckLoadQueryDynamically(); - - // select another saved query, edit query input, which later should be dismissed once Load query dynamically checkbox checked - selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); - getCustomQueryInput().type('AND this part wil be dismissed'); - - checkLoadQueryDynamically(); - getCustomQueryInput().should('have.value', savedQueryQuery); - - cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); - saveEditedRule(); - - cy.wait('@editedRule').then(({ response }) => { - // updated rule type shouldn't change - cy.wrap(response?.body.type).should('equal', 'saved_query'); - }); - - cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); - - getDetails(SAVED_QUERY_NAME_DETAILS).should('contain', savedQueryName); - getDetails(SAVED_QUERY_DETAILS).should('contain', savedQueryQuery); + it('Shows error toast on details page when saved query can not be loaded', function () { + cy.get(TOASTER).should('contain', FAILED_TO_LOAD_ERROR); }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts index e01e5d8e708a2..b94216cdfd812 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts @@ -5,63 +5,24 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; -import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../../../objects/rule'; +import { getEqlRule, getEqlSequenceRule } from '../../../../objects/rule'; -import { ALERTS_COUNT, ALERT_DATA_GRID } from '../../../../screens/alerts'; -import { - CUSTOM_RULES_BTN, - RISK_SCORE, - RULES_MANAGEMENT_TABLE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_DETAILS, - ABOUT_INVESTIGATION_NOTES, - ABOUT_RULE_DESCRIPTION, - ADDITIONAL_LOOK_BACK_DETAILS, - EQL_QUERY_DETAILS, - DEFINITION_DETAILS, - FALSE_POSITIVES_DETAILS, - removeExternalLinkText, - INDEX_PATTERNS_DETAILS, - INVESTIGATION_NOTES_MARKDOWN, - INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; -import { getDetails } from '../../../../tasks/rule_details'; -import { expectNumberOfRules, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; -import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { continueFromDefineStep, - createAndEnableRule, createRuleWithNonBlockingErrors, + createRuleWithoutEnabling, fillAboutRuleAndContinue, fillDefineEqlRuleAndContinue, fillScheduleRuleAndContinue, getDefineContinueButton, getIndexPatternClearButton, getRuleIndexInput, - selectEqlRuleType, - waitForAlertsToPopulate, + selectEqlRuleType } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; -import { CREATE_RULE_URL } from '../../../../urls/navigation'; import { EQL_OPTIONS_POPOVER_TRIGGER, EQL_OPTIONS_TIMESTAMP_INPUT, @@ -70,133 +31,43 @@ import { EQL_QUERY_VALIDATION_ERROR_CONTENT, RULES_CREATION_FORM, } from '../../../../screens/create_new_rule'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { CREATE_RULE_URL } from '../../../../urls/navigation'; -// Skip in MKI due to flake -describe('EQL rules', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { +describe('EQL Rule - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { - login(); deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); }); - describe('Detection rules, EQL', () => { + it('Creates a new EQL rule', function () { const rule = getEqlRule(); - const expectedUrls = rule.references?.join(''); - const expectedFalsePositives = rule.false_positives?.join(''); - const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); - const expectedNumberOfRules = 1; - const expectedNumberOfAlerts = '1 alert'; - - it('Creates and enables a new EQL rule', function () { - visit(CREATE_RULE_URL); - selectEqlRuleType(); - fillDefineEqlRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'High'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); + selectEqlRuleType(); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'High'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(EQL_QUERY_DETAILS).should('have.text', rule.query); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); - - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); - cy.get(ALERT_DATA_GRID) - .invoke('text') - .then((text) => { - expect(text).contains(rule.name); - expect(text).contains(rule.severity); - expect(text).contains(rule.risk_score); - }); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); - describe('Detection rules, sequence EQL', () => { - const expectedNumberOfSequenceAlerts = '2 alerts'; - - const rule = getEqlSequenceRule(); - - before(() => { - cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); - }); - - after(() => { - cy.task('esArchiverUnload', { archiveName: 'auditbeat_multiple' }); - }); + it( + 'Creates a new EQL rule with a sequence', () => { + const rule = getEqlSequenceRule(); - it( - 'Creates and enables a new EQL rule with a sequence', - { - tags: ['@skipInServerlessMKI'], - }, - function () { - login(); - visit(CREATE_RULE_URL); - selectEqlRuleType(); - fillDefineEqlRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - goToRuleDetailsOf(rule.name); - waitForAlertsToPopulate(); + selectEqlRuleType(); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfSequenceAlerts); - cy.get(ALERT_DATA_GRID) - .invoke('text') - .then((text) => { - cy.log('ALERT_DATA_GRID', text); - expect(text).contains(rule.name); - expect(text).contains(rule.severity); - }); - } - ); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + } + ); describe('with source data requiring EQL overrides', () => { before(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts deleted file mode 100644 index a9ebc451c3e08..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_ess_basic.cy.ts +++ /dev/null @@ -1,93 +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 { getEqlRule } from '../../../../objects/rule'; - -import { login } from '../../../../tasks/login'; -import { visit } from '../../../../tasks/navigation'; -import { getDetails } from '../../../../tasks/rule_details'; -import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -import { selectEqlRuleType } from '../../../../tasks/create_new_rule'; -import { - ALERT_SUPPRESSION_FIELDS_INPUT, - ALERT_SUPPRESSION_FIELDS, -} from '../../../../screens/create_new_rule'; -import { - DEFINITION_DETAILS, - SUPPRESS_FOR_DETAILS, - SUPPRESS_BY_DETAILS, - SUPPRESS_MISSING_FIELD, - DETAILS_TITLE, - ALERT_SUPPRESSION_INSUFFICIENT_LICENSING_ICON, -} from '../../../../screens/rule_details'; -import { startBasicLicense } from '../../../../tasks/api_calls/licensing'; -import { createRule } from '../../../../tasks/api_calls/rules'; -import { TOOLTIP } from '../../../../screens/common'; -import { ruleDetailsUrl } from '../../../../urls/rule_details'; - -const SUPPRESS_BY_FIELDS = ['agent.type']; - -describe( - 'Detection Rule Creation - EQL Rules - With Alert Suppression - Basic License', - { - tags: ['@ess'], - }, - () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - startBasicLicense(); - }); - after(() => { - cy.task('esArchiverUnload', { archiveName: 'auditbeat_multiple' }); - }); - - it('cannot create a rule with "per rule execution" suppression durations', () => { - selectEqlRuleType(); - - cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); - cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); - - // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this - cy.get(TOOLTIP).contains('Platinum license'); - }); - - it('shows an upselling message on rule suppression details', () => { - const rule = getEqlRule(); - - createRule({ - ...rule, - alert_suppression: { - group_by: SUPPRESS_BY_FIELDS, - duration: { value: 360, unit: 's' }, - missing_fields_strategy: 'doNotSuppress', - }, - }).then((createdRule) => { - visit(ruleDetailsUrl(createdRule.body.id)); - - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '360s'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Do not suppress alerts for events with missing fields' - ); - - // suppression functionality should be under Tech Preview - cy.contains(DETAILS_TITLE, SUPPRESS_FOR_DETAILS).contains('Technical Preview'); - }); - - // Platinum license is required for configuration to apply - cy.get(ALERT_SUPPRESSION_INSUFFICIENT_LICENSING_ICON).eq(2).trigger('mouseover'); - cy.get(TOOLTIP).contains( - 'Alert suppression is configured but will not be applied due to insufficient licensing' - ); - }); - }); - } -); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts deleted file mode 100644 index 6c0241f94c1f5..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule_suppression_serverless_essentials.cy.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { getEqlRule } from '../../../../objects/rule'; - -import { login } from '../../../../tasks/login'; -import { visit } from '../../../../tasks/navigation'; -import { getDetails } from '../../../../tasks/rule_details'; -import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -import { - fillAlertSuppressionFields, - fillAboutRuleMinimumAndContinue, - createRuleWithoutEnabling, - skipScheduleRuleAction, - continueFromDefineStep, - fillDefineEqlRule, - selectEqlRuleType, -} from '../../../../tasks/create_new_rule'; - -import { DEFINITION_DETAILS, SUPPRESS_BY_DETAILS } from '../../../../screens/rule_details'; - -const SUPPRESS_BY_FIELDS = ['agent.type']; - -describe( - 'Detection Rule Creation - EQL Rules - With Alert Suppression - Serverless Essentials License', - { - tags: ['@serverless'], - env: { - ftrConfig: { - productTypes: [ - { product_line: 'security', product_tier: 'essentials' }, - { product_line: 'endpoint', product_tier: 'essentials' }, - ], - }, - }, - }, - () => { - const rule = getEqlRule(); - before(() => { - cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); - }); - beforeEach(() => { - deleteAlertsAndRules(); - login(); - }); - after(() => { - cy.task('esArchiverUnload', { archiveName: 'auditbeat_multiple' }); - }); - - describe('with non-sequence queries', () => { - it('creates a rule with a "per rule execution" suppression duration', () => { - visit(CREATE_RULE_URL); - selectEqlRuleType(); - fillDefineEqlRule(rule); - - // selecting only suppression fields, the rest options would be default - fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); - continueFromDefineStep(); - - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); - createRuleWithoutEnabling(); - - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - }); - }); - }); - } -); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts index 0045a79ff4394..0f5407b4f2545 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts @@ -16,10 +16,6 @@ import { RULE_NAME_HEADER, RULE_TYPE_DETAILS, RULE_NAME_OVERRIDE_DETAILS, - DEFINITION_DETAILS, - SUPPRESS_BY_DETAILS, - SUPPRESS_FOR_DETAILS, - SUPPRESS_MISSING_FIELD, } from '../../../../screens/rule_details'; import { ESQL_QUERY_BAR } from '../../../../screens/create_new_rule'; @@ -41,14 +37,6 @@ import { fillRuleName, fillDescription, getAboutContinueButton, - fillAlertSuppressionFields, - selectAlertSuppressionPerInterval, - setAlertSuppressionDuration, - selectDoNotSuppressForMissingFields, - continueFromDefineStep, - fillAboutRuleMinimumAndContinue, - skipScheduleRuleAction, - interceptEsqlQueryFieldsRequest, createRuleWithNonBlockingErrors, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; @@ -68,7 +56,7 @@ const workaroundForResizeObserver = () => }); describe( - 'Detection ES|QL rules, creation', + 'Detection ES|QL rules - Rule Creation', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'], }, @@ -241,60 +229,5 @@ describe( cy.get(INVESTIGATION_FIELDS_VALUE_ITEM).should('have.text', CUSTOM_ESQL_FIELD); }); }); - - describe('Alert suppression', () => { - beforeEach(() => { - login(); - visit(CREATE_RULE_URL); - }); - it('shows custom ES|QL field in investigation fields autocomplete and saves it in rule', function () { - const CUSTOM_ESQL_FIELD = '_custom_agent_name'; - const SUPPRESS_BY_FIELDS = [CUSTOM_ESQL_FIELD, 'agent.type']; - - const queryWithCustomFields = [ - `from auditbeat* metadata _id, _version, _index`, - `eval ${CUSTOM_ESQL_FIELD} = agent.name`, - `drop agent.*`, - ].join(' | '); - - workaroundForResizeObserver(); - - selectEsqlRuleType(); - - interceptEsqlQueryFieldsRequest(queryWithCustomFields, 'esqlSuppressionFieldsRequest'); - fillEsqlQueryBar(queryWithCustomFields); - - cy.wait('@esqlSuppressionFieldsRequest'); - fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); - selectAlertSuppressionPerInterval(); - setAlertSuppressionDuration(2, 'h'); - selectDoNotSuppressForMissingFields(); - continueFromDefineStep(); - - // ensures details preview works correctly - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Do not suppress alerts for events with missing fields' - ); - }); - - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); - createRuleWithoutEnabling(); - - // ensures rule details displayed correctly after rule created - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '2h'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Do not suppress alerts for events with missing fields' - ); - }); - }); - }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts index 89788eb689a1e..fe054c5084987 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts @@ -118,7 +118,7 @@ import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; // Skipping in MKI due to flake -describe('indicator match', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { +describe('Indicator Match - Rule Creation', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { describe('Detection rules, Indicator Match', () => { const expectedUrls = getNewThreatIndicatorRule().references?.join(''); const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_serverless_essentials.cy.ts deleted file mode 100644 index 239c1ba4e9682..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule_suppression_serverless_essentials.cy.ts +++ /dev/null @@ -1,70 +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 { getNewThreatIndicatorRule } from '../../../../objects/rule'; - -import { DEFINITION_DETAILS, SUPPRESS_BY_DETAILS } from '../../../../screens/rule_details'; - -import { - fillDefineIndicatorMatchRule, - fillAlertSuppressionFields, - selectIndicatorMatchType, - fillAboutRuleMinimumAndContinue, - createRuleWithoutEnabling, - skipScheduleRuleAction, - continueFromDefineStep, -} from '../../../../tasks/create_new_rule'; - -import { login } from '../../../../tasks/login'; -import { visit } from '../../../../tasks/navigation'; -import { getDetails } from '../../../../tasks/rule_details'; -import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; - -const SUPPRESS_BY_FIELDS = ['myhash.mysha256', 'source.ip.keyword']; - -describe( - 'Detection rules, Indicator Match, Alert Suppression', - { - tags: ['@serverless'], - env: { - ftrConfig: { - productTypes: [ - { product_line: 'security', product_tier: 'essentials' }, - { product_line: 'endpoint', product_tier: 'essentials' }, - ], - }, - }, - }, - () => { - const rule = getNewThreatIndicatorRule(); - beforeEach(() => { - cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); - cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); - deleteAlertsAndRules(); - login(); - }); - - it('creates rule with per rule execution suppression for essentials license', () => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - fillDefineIndicatorMatchRule(rule); - - // selecting only suppression fields, the rest options would be default - fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); - continueFromDefineStep(); - - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); - createRuleWithoutEnabling(); - - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - }); - }); - } -); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule.cy.ts index 50ca504e7a125..2077d881e5f84 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/machine_learning_rule.cy.ts @@ -5,43 +5,12 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; import { getMachineLearningRule } from '../../../../objects/rule'; -import { - RISK_SCORE, - RULES_MANAGEMENT_TABLE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_DETAILS, - ABOUT_RULE_DESCRIPTION, - ADDITIONAL_LOOK_BACK_DETAILS, - ANOMALY_SCORE_DETAILS, - DEFINITION_DETAILS, - FALSE_POSITIVES_DETAILS, - removeExternalLinkText, - MACHINE_LEARNING_JOB_ID, - // MACHINE_LEARNING_JOB_STATUS, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; -import { getDetails } from '../../../../tasks/rule_details'; -import { expectNumberOfRules, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { - createAndEnableRule, + createRuleWithoutEnabling, fillAboutRuleAndContinue, fillDefineMachineLearningRuleAndContinue, fillScheduleRuleAndContinue, @@ -49,22 +18,11 @@ import { } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; import { forceStopAndCloseJob } from '../../../../support/machine_learning'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -// https://github.com/elastic/kibana/issues/187622 -describe('Machine Learning rules', { tags: ['@ess', '@serverless, @skipInServerlessMKI'] }, () => { - const expectedUrls = (getMachineLearningRule().references ?? []).join(''); - const expectedFalsePositives = (getMachineLearningRule().false_positives ?? []).join(''); - const expectedTags = (getMachineLearningRule().tags ?? []).join(''); - const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().threat ?? []); - const expectedJobText = [ - 'Unusual Linux Network Activity', - 'Anomalous Process for a Linux Population', - ].join(''); - +describe('Machine Learning - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { before(() => { const machineLearningJobIds = ([] as string[]).concat( getMachineLearningRule().machine_learning_job_id @@ -79,60 +37,15 @@ describe('Machine Learning rules', { tags: ['@ess', '@serverless, @skipInServerl visit(CREATE_RULE_URL); }); - it('Creates and enables a new ml rule', () => { + it('Creates a new ml rule', () => { const mlRule = getMachineLearningRule(); selectMachineLearningRuleType(); fillDefineMachineLearningRuleAndContinue(mlRule); fillAboutRuleAndContinue(mlRule); fillScheduleRuleAndContinue(mlRule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1); - - cy.get(RULE_NAME).should('have.text', mlRule.name); - cy.get(RISK_SCORE).should('have.text', mlRule.risk_score); - cy.get(SEVERITY).should('have.text', 'Critical'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(mlRule.name); + createRuleWithoutEnabling(); - cy.get(RULE_NAME_HEADER).should('contain', `${mlRule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', mlRule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); - getDetails(RISK_SCORE_DETAILS).should('have.text', mlRule.risk_score); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(ANOMALY_SCORE_DETAILS).should('have.text', mlRule.anomaly_threshold); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - // With the #1912 ML rule improvement changes we enable jobs on rule creation. - // Though, in cypress jobs enabling does not work reliably and job can be started or stopped. - // Thus, we disable next check till we fix the issue with enabling jobs in cypress. - // Relevant ticket: https://github.com/elastic/security-team/issues/5389 - // cy.get(MACHINE_LEARNING_JOB_STATUS).should('have.text', 'StoppedStopped'); - cy.get(MACHINE_LEARNING_JOB_ID).should('have.text', expectedJobText); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${mlRule.interval}`); - const humanizedDuration = getHumanizedDuration( - mlRule.from ?? 'now-6m', - mlRule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', mlRule.name); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts index 996cf764c736c..75da9f6e6c5df 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts @@ -5,197 +5,44 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; -import { getIndexPatterns, getNewTermsRule } from '../../../../objects/rule'; +import { getNewTermsRule } from '../../../../objects/rule'; -import { ALERT_DATA_GRID } from '../../../../screens/alerts'; -import { - CUSTOM_RULES_BTN, - RISK_SCORE, - RULES_MANAGEMENT_TABLE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_DETAILS, - ABOUT_INVESTIGATION_NOTES, - ABOUT_RULE_DESCRIPTION, - ADDITIONAL_LOOK_BACK_DETAILS, - CUSTOM_QUERY_DETAILS, - DEFINITION_DETAILS, - FALSE_POSITIVES_DETAILS, - removeExternalLinkText, - INDEX_PATTERNS_DETAILS, - INVESTIGATION_NOTES_MARKDOWN, - INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - NEW_TERMS_HISTORY_WINDOW_DETAILS, - NEW_TERMS_FIELDS_DETAILS, - SUPPRESS_BY_DETAILS, - SUPPRESS_FOR_DETAILS, - SUPPRESS_MISSING_FIELD, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; -import { getDetails } from '../../../../tasks/rule_details'; -import { expectNumberOfRules, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { - createAndEnableRule, fillAboutRuleAndContinue, - fillDefineNewTermsRule, fillDefineNewTermsRuleAndContinue, fillScheduleRuleAndContinue, selectNewTermsRuleType, - waitForAlertsToPopulate, - fillAlertSuppressionFields, - fillAboutRuleMinimumAndContinue, - createRuleWithoutEnabling, - skipScheduleRuleAction, - continueFromDefineStep, - selectAlertSuppressionPerInterval, - setAlertSuppressionDuration, - selectDoNotSuppressForMissingFields, + createRuleWithoutEnabling } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; -// Skipped in MKI due to flake describe( - 'New Terms rules', + 'New Terms Rule - Rule Creation', { - tags: ['@ess', '@serverless', '@skipInServerlessMKI'], + tags: ['@ess', '@serverless'], }, () => { - describe('Detection rules, New Terms', () => { - const rule = getNewTermsRule(); - const expectedUrls = rule.references?.join(''); - const expectedFalsePositives = rule.false_positives?.join(''); - const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); - const expectedNumberOfRules = 1; - - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - selectNewTermsRuleType(); - }); - - it('Creates and enables a new terms rule', function () { - fillDefineNewTermsRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'High'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'High'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'New Terms'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - getDetails(NEW_TERMS_FIELDS_DETAILS).should('have.text', 'host.name'); - getDetails(NEW_TERMS_HISTORY_WINDOW_DETAILS).should('have.text', '51000h'); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); - - waitForAlertsToPopulate(); - - cy.get(ALERT_DATA_GRID) - .invoke('text') - .then((text) => { - expect(text).contains(rule.name); - expect(text).contains(rule.severity); - expect(text).contains(rule.risk_score); - }); - }); - - it('Creates rule rule with time interval suppression', () => { - const SUPPRESS_BY_FIELDS = ['agent.hostname', 'agent.type']; - - fillDefineNewTermsRule(rule); - - // fill suppress by fields and select non-default suppression options - fillAlertSuppressionFields(SUPPRESS_BY_FIELDS); - selectAlertSuppressionPerInterval(); - setAlertSuppressionDuration(45, 'm'); - selectDoNotSuppressForMissingFields(); - continueFromDefineStep(); - - // ensures details preview works correctly - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '45m'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Do not suppress alerts for events with missing fields' - ); - }); + const rule = getNewTermsRule(); + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + selectNewTermsRuleType(); + }); - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); - createRuleWithoutEnabling(); + it('Creates a new terms rule', function () { + fillDefineNewTermsRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_BY_DETAILS).should('have.text', SUPPRESS_BY_FIELDS.join('')); - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '45m'); - getDetails(SUPPRESS_MISSING_FIELD).should( - 'have.text', - 'Do not suppress alerts for events with missing fields' - ); - }); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts index b23c68b341a96..825b8300ab360 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts @@ -5,157 +5,35 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; -import { - getIndexPatterns, - getNewOverrideRule, - getSeveritiesOverride, -} from '../../../../objects/rule'; - -import { ALERT_GRID_CELL, ALERTS_COUNT } from '../../../../screens/alerts'; - -import { - CUSTOM_RULES_BTN, - RISK_SCORE, - RULES_MANAGEMENT_TABLE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_INVESTIGATION_NOTES, - ABOUT_DETAILS, - ABOUT_RULE_DESCRIPTION, - ADDITIONAL_LOOK_BACK_DETAILS, - CUSTOM_QUERY_DETAILS, - DEFINITION_DETAILS, - DETAILS_DESCRIPTION, - DETAILS_TITLE, - FALSE_POSITIVES_DETAILS, - removeExternalLinkText, - INDEX_PATTERNS_DETAILS, - INVESTIGATION_NOTES_MARKDOWN, - INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RISK_SCORE_OVERRIDE_DETAILS, - RULE_NAME_HEADER, - RULE_NAME_OVERRIDE_DETAILS, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - TIMESTAMP_OVERRIDE_DETAILS, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; - +import { getNewOverrideRule } from '../../../../objects/rule'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; -import { expectNumberOfRules, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { - createAndEnableRule, + createRuleWithoutEnabling, fillAboutRuleWithOverrideAndContinue, fillDefineCustomRuleAndContinue, - fillScheduleRuleAndContinue, - waitForAlertsToPopulate, + fillScheduleRuleAndContinue } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { getDetails } from '../../../../tasks/rule_details'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; -describe('Rules override', { tags: ['@ess', '@serverless'] }, () => { +describe('Rule Overrides - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { const rule = getNewOverrideRule(); - const expectedUrls = rule.references?.join(''); - const expectedFalsePositives = rule.false_positives?.join(''); - const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); beforeEach(() => { - login(); deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); }); - it('Creates and enables a new custom rule with override option', function () { - visit(CREATE_RULE_URL); + it('Creates a new custom rule with override options', function () { fillDefineCustomRuleAndContinue(rule); fillAboutRuleWithOverrideAndContinue(rule); fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1); - - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'High'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'High'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(RISK_SCORE_OVERRIDE_DETAILS).should( - 'have.text', - `${rule.risk_score_mapping?.[0].field}kibana.alert.risk_score` - ); - getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', rule.rule_name_override); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', rule.timestamp_override); - cy.contains(DETAILS_TITLE, 'Severity override') - .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings - .then((severityOverrideIndex) => { - rule.severity_mapping?.forEach((severity, i) => { - cy.get(DETAILS_DESCRIPTION) - .eq(severityOverrideIndex + i) - .should( - 'have.text', - `${severity.field}:${severity.value}${getSeveritiesOverride()[i]}` - ); - }); - }); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration(rule.from ?? 'now-6m', rule.interval ?? '5m'); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); - - waitForAlertsToPopulate(); + createRuleWithoutEnabling(); - cy.get(ALERTS_COUNT) - .invoke('text') - .should('match', /^[1-9].+$/); // Any number of alerts - cy.get(ALERT_GRID_CELL).contains('winlogbeat'); - cy.get(ALERT_GRID_CELL).contains('high'); - cy.get(ALERT_GRID_CELL).contains('80'); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts index 8af755c6ed328..2a1214e8c919a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts @@ -5,80 +5,28 @@ * 2.0. */ -import { formatMitreAttackDescription, getHumanizedDuration } from '../../../../helpers/rules'; -import { getIndexPatterns, getNewThresholdRule } from '../../../../objects/rule'; - -import { ALERTS_COUNT, ALERT_GRID_CELL } from '../../../../screens/alerts'; - -import { - CUSTOM_RULES_BTN, - RISK_SCORE, - RULES_MANAGEMENT_TABLE, - RULE_NAME, - RULE_SWITCH, - SEVERITY, -} from '../../../../screens/alerts_detection_rules'; -import { - ABOUT_DETAILS, - ABOUT_INVESTIGATION_NOTES, - ABOUT_RULE_DESCRIPTION, - ADDITIONAL_LOOK_BACK_DETAILS, - CUSTOM_QUERY_DETAILS, - FALSE_POSITIVES_DETAILS, - DEFINITION_DETAILS, - removeExternalLinkText, - INDEX_PATTERNS_DETAILS, - INVESTIGATION_NOTES_MARKDOWN, - INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, - REFERENCE_URLS_DETAILS, - RISK_SCORE_DETAILS, - RULE_NAME_HEADER, - RULE_TYPE_DETAILS, - RUNS_EVERY_DETAILS, - SCHEDULE_DETAILS, - SEVERITY_DETAILS, - TAGS_DETAILS, - THRESHOLD_DETAILS, - TIMELINE_TEMPLATE_DETAILS, - SUPPRESS_FOR_DETAILS, - INTERVAL_ABBR_VALUE, -} from '../../../../screens/rule_details'; -import { expectNumberOfRules, goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; +import { getNewThresholdRule } from '../../../../objects/rule'; +import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { - createAndEnableRule, createRuleWithoutEnabling, - fillAboutRuleMinimumAndContinue, - enablesAndPopulatesThresholdSuppression, - skipScheduleRuleAction, fillAboutRuleAndContinue, fillDefineThresholdRuleAndContinue, fillScheduleRuleAndContinue, selectThresholdRuleType, - waitForAlertsToPopulate, - fillDefineThresholdRule, - continueFromDefineStep, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { getDetails, assertDetailsNotExist } from '../../../../tasks/rule_details'; -import { openRuleManagementPageViaBreadcrumbs } from '../../../../tasks/rules_management'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; describe( - 'Threshold rules', + 'Threshold Rule - Rule Creation', { tags: ['@ess', '@serverless'], }, () => { const rule = getNewThresholdRule(); - const expectedUrls = rule.references?.join(''); - const expectedFalsePositives = rule.false_positives?.join(''); - const expectedTags = rule.tags?.join(''); - const mitreAttack = rule.threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); beforeEach(() => { deleteAlertsAndRules(); @@ -91,87 +39,10 @@ describe( fillDefineThresholdRuleAndContinue(rule); fillAboutRuleAndContinue(rule); fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1); - - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'High'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'High'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - getDetails(THRESHOLD_DETAILS).should( - 'have.text', - `Results aggregated by ${rule.threshold.field} >= ${rule.threshold.value}` - ); - assertDetailsNotExist(SUPPRESS_FOR_DETAILS); - }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); - }); - - waitForAlertsToPopulate(); - - cy.get(ALERTS_COUNT).should(($count) => expect(+$count.text().split(' ')[0]).to.be.lt(100)); - cy.get(ALERT_GRID_CELL).contains(rule.name); - }); - - it('Creates a new threshold rule with suppression enabled', () => { - selectThresholdRuleType(); - - fillDefineThresholdRule(rule); - enablesAndPopulatesThresholdSuppression(5, 'h'); - continueFromDefineStep(); - - // ensures duration displayed on define step in preview mode - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '5h'); - }); - - fillAboutRuleMinimumAndContinue(rule); - skipScheduleRuleAction(); createRuleWithoutEnabling(); - openRuleManagementPageViaBreadcrumbs(); - goToRuleDetailsOf(rule.name); - cy.get(DEFINITION_DETAILS).within(() => { - getDetails(SUPPRESS_FOR_DETAILS).should('have.text', '5h'); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts new file mode 100644 index 0000000000000..2e16e722b026f --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts @@ -0,0 +1,189 @@ +/* + * 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, getSavedQueryRule } from '../../../../objects/rule'; + +import { LOAD_QUERY_DYNAMICALLY_CHECKBOX } from '../../../../screens/create_new_rule'; +import { TOASTER } from '../../../../screens/alerts_detection_rules'; +import { + SAVED_QUERY_NAME_DETAILS, + SAVED_QUERY_DETAILS, + DEFINE_RULE_PANEL_PROGRESS, + CUSTOM_QUERY_DETAILS, +} from '../../../../screens/rule_details'; + +import { editFirstRule } from '../../../../tasks/alerts_detection_rules'; +import { createSavedQuery, deleteSavedQueries } from '../../../../tasks/api_calls/saved_queries'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { + selectAndLoadSavedQuery, + getCustomQueryInput, + checkLoadQueryDynamically, + uncheckLoadQueryDynamically, +} from '../../../../tasks/create_new_rule'; +import { saveEditedRule, visitEditRulePage } from '../../../../tasks/edit_rule'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { + assertDetailsNotExist, + getDetails, +} from '../../../../tasks/rule_details'; +import { createRule } from '../../../../tasks/api_calls/rules'; +import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; + +const savedQueryName = 'custom saved query'; +const savedQueryQuery = 'process.name: test'; + +describe('Saved query rules, rule edit', { tags: ['@ess', '@serverless'] }, () => { + beforeEach(() => { + login(); + deleteAlertsAndRules(); + deleteSavedQueries(); + }); + + it('Allows to update query rule as saved_query rule type', () => { + createSavedQuery(savedQueryName, savedQueryQuery); + createRule(getNewRule()).then((rule) => visitEditRulePage(rule.body.id)); + + selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); + checkLoadQueryDynamically(); + + cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); + saveEditedRule(); + + cy.wait('@editedRule').then(({ response }) => { + // updated rule should be saved as saved_query type once Load query dynamically checkbox was checked + cy.wrap(response?.body.type).should('equal', 'saved_query'); + }); + + cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); + + getDetails(SAVED_QUERY_NAME_DETAILS).should('contain', savedQueryName); + getDetails(SAVED_QUERY_DETAILS).should('contain', savedQueryQuery); + }); + + it('Allows to update saved_query rule as query rule type', () => { + const expectedCustomTestQuery = 'random test query'; + createSavedQuery(savedQueryName, savedQueryQuery).then((response) => { + cy.log(JSON.stringify(response.body, null, 2)); + createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then( + (rule) => visitEditRulePage(rule.body.id) + ); + }); + + // query input should be disabled and has value of saved query + getCustomQueryInput().should('have.value', savedQueryQuery).should('be.disabled'); + + // after unchecking Load Query Dynamically checkbox, query input becomes enabled, type custom query + uncheckLoadQueryDynamically(); + getCustomQueryInput().should('be.enabled').clear().type(expectedCustomTestQuery); + + cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); + saveEditedRule(); + + cy.wait('@editedRule').then(({ response }) => { + // updated rule should be saved as query type once Load query dynamically checkbox was unchecked + cy.wrap(response?.body.type).should('equal', 'query'); + }); + + getDetails(CUSTOM_QUERY_DETAILS).should('contain', expectedCustomTestQuery); + }); + + it('Allows to update saved_query rule with non-existent query by adding custom query', () => { + const expectedCustomTestQuery = 'random test query'; + createRule(getSavedQueryRule({ saved_id: 'non-existent', query: undefined })).then((rule) => + visitEditRulePage(rule.body.id) + ); + + uncheckLoadQueryDynamically(); + + // type custom query, ensure Load dynamically checkbox is absent, as rule can't be saved win non valid saved query + getCustomQueryInput().type(expectedCustomTestQuery); + cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).should('not.visible'); + + cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); + saveEditedRule(); + + cy.wait('@editedRule').then(({ response }) => { + // updated rule should be saved as query type once Load query dynamically checkbox was unchecked + cy.wrap(response?.body.type).should('equal', 'query'); + }); + + getDetails(CUSTOM_QUERY_DETAILS).should('contain', expectedCustomTestQuery); + }); + + it('Allows to update saved_query rule with non-existent query by selecting another saved query', () => { + createSavedQuery(savedQueryName, savedQueryQuery); + createRule(getSavedQueryRule({ saved_id: 'non-existent', query: undefined })).then((rule) => + visitEditRulePage(rule.body.id) + ); + + visit(RULES_MANAGEMENT_URL); + + editFirstRule(); + uncheckLoadQueryDynamically(); + + // select another saved query, edit query input, which later should be dismissed once Load query dynamically checkbox checked + selectAndLoadSavedQuery(savedQueryName, savedQueryQuery); + getCustomQueryInput().type('AND this part wil be dismissed'); + + checkLoadQueryDynamically(); + getCustomQueryInput().should('have.value', savedQueryQuery); + + cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); + saveEditedRule(); + + cy.wait('@editedRule').then(({ response }) => { + // updated rule type shouldn't change + cy.wrap(response?.body.type).should('equal', 'saved_query'); + }); + + cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); + + getDetails(SAVED_QUERY_NAME_DETAILS).should('contain', savedQueryName); + getDetails(SAVED_QUERY_DETAILS).should('contain', savedQueryQuery); + }); + + context('Non existent saved query', () => { + const FAILED_TO_LOAD_ERROR = 'Failed to load the saved query'; + + beforeEach(() => { + createRule( + getSavedQueryRule({ + saved_id: 'non-existent', + query: undefined, + }) + ).then((rule) => visitEditRulePage(rule.body.id)); + }); + + it('Shows validation error on rule edit when saved query can not be loaded', function () { + cy.get(TOASTER).should('contain', FAILED_TO_LOAD_ERROR); + }); + + // https://github.com/elastic/kibana/issues/187623 + it( + 'Allows to update saved_query rule with non-existent query', + { tags: ['@skipInServerlessMKI'] }, + () => { + cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).should('exist'); + + cy.intercept('PUT', '/api/detection_engine/rules').as('editedRule'); + saveEditedRule(); + + cy.wait('@editedRule').then(({ response }) => { + // updated rule type shouldn't change + cy.wrap(response?.body.type).should('equal', 'saved_query'); + }); + + cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist'); + + assertDetailsNotExist(SAVED_QUERY_NAME_DETAILS); + assertDetailsNotExist(SAVED_QUERY_DETAILS); + } + ); + }); +}); From 8691e9a8a0607b012e6435e11b21c118dcd041b4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 7 Apr 2025 01:22:12 +0000 Subject: [PATCH 2/3] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../basic_license_check_rule_form.cy.ts | 58 +- .../alert_suppression/custom_query_rule.cy.ts | 2 +- .../alert_suppression/new_terms_rule.cy.ts | 2 +- .../alert_suppression/threshold_rule.cy.ts | 5 +- .../rule_creation/custom_query_rule.cy.ts | 2 +- .../custom_query_rule_data_view.cy.ts | 70 +- .../custom_saved_query_rule.cy.ts | 5 +- .../rule_creation/eql_rule.cy.ts | 24 +- .../rule_creation/indicator_match_rule.cy.ts | 800 +++++++++--------- .../rule_creation/new_terms_rule.cy.ts | 2 +- .../rule_creation/override.cy.ts | 2 +- .../rule_edit/custom_saved_query.cy.ts | 9 +- 12 files changed, 489 insertions(+), 492 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts index 87b7da95bb7b6..3ebe8230a3d04 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts @@ -36,44 +36,44 @@ describe( tags: ['@ess'], }, () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - startBasicLicense(); - }); + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + startBasicLicense(); + }); - it('cannot create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { - // Default query rule - openSuppressionFieldsTooltipAndCheckLicense(); + it('cannot create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { + // Default query rule + openSuppressionFieldsTooltipAndCheckLicense(); - selectIndicatorMatchType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectIndicatorMatchType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectNewTermsRuleType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectNewTermsRuleType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectEsqlRuleType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectEsqlRuleType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectEqlRuleType(); - cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); - cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); + selectEqlRuleType(); + cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); - // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this - cy.get(TOOLTIP).contains('Platinum license'); + // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this + cy.get(TOOLTIP).contains('Platinum license'); - // ML Rules require Platinum license - cy.get(MACHINE_LEARNING_TYPE).get('button').should('be.disabled'); + // ML Rules require Platinum license + cy.get(MACHINE_LEARNING_TYPE).get('button').should('be.disabled'); - selectThresholdRuleType(); - cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.disabled'); - cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).parent().trigger('mouseover'); - // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this - cy.get(TOOLTIP).contains('Platinum license'); + selectThresholdRuleType(); + cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.disabled'); + cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).parent().trigger('mouseover'); + // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this + cy.get(TOOLTIP).contains('Platinum license'); - cy.get(ALERT_SUPPRESSION_DURATION_VALUE_INPUT).should('be.disabled'); - cy.get(ALERT_SUPPRESSION_DURATION_UNIT_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_DURATION_VALUE_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_DURATION_UNIT_INPUT).should('be.disabled'); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts index 6cfd441499792..529f01a9aafd5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts @@ -35,7 +35,7 @@ describe('Custom Query Rule - Alert suppression', { tags: ['@ess', '@serverless' beforeEach(() => { deleteAlertsAndRules(); login(); - visit(CREATE_RULE_URL); + visit(CREATE_RULE_URL); }); const SUPPRESS_BY_FIELDS = ['source.ip']; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts index 00c180c0e90fe..08148bde1f69e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts @@ -11,7 +11,7 @@ import { DEFINITION_DETAILS, SUPPRESS_BY_DETAILS, SUPPRESS_FOR_DETAILS, - SUPPRESS_MISSING_FIELD + SUPPRESS_MISSING_FIELD, } from '../../../../screens/rule_details'; import { getDetails } from '../../../../tasks/rule_details'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts index b52f43cf8142f..a3de0166c72c8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts @@ -6,10 +6,7 @@ */ import { getNewThresholdRule } from '../../../../objects/rule'; -import { - DEFINITION_DETAILS, - SUPPRESS_FOR_DETAILS, -} from '../../../../screens/rule_details'; +import { DEFINITION_DETAILS, SUPPRESS_FOR_DETAILS } from '../../../../screens/rule_details'; import { goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts index 70283f3be6026..354fd97815ccc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts @@ -16,7 +16,7 @@ import { fillDefineCustomRuleAndContinue, createRuleWithoutEnabling, fillDefineCustomRule, - openAddFilterPopover + openAddFilterPopover, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts index 515289c0cfe70..741f90994d211 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts @@ -30,42 +30,46 @@ import { fillAddFilterForm } from '../../../../tasks/search_bar'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -describe('Custom query rules with data views - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { - const rule = getDataViewRule(); +describe( + 'Custom query rules with data views - Rule Creation', + { tags: ['@ess', '@serverless'] }, + () => { + const rule = getDataViewRule(); - beforeEach(() => { - if (rule.data_view_id != null) { - postDataView(rule.data_view_id); - } - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - }); + beforeEach(() => { + if (rule.data_view_id != null) { + postDataView(rule.data_view_id); + } + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + }); - afterEach(() => { - if (rule.data_view_id != null) { - deleteDataView(rule.data_view_id); - } - }); + afterEach(() => { + if (rule.data_view_id != null) { + deleteDataView(rule.data_view_id); + } + }); - it('Creates and enables a new rule', function () { - fillDefineCustomRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); + it('Creates and enables a new rule', function () { + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.log('Asserting we have a new rule created'); - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); - it('Adds filter on define step', () => { - fillDefineCustomRule(rule); - openAddFilterPopover(); - fillAddFilterForm({ - key: 'host.name', - operator: 'exists', + it('Adds filter on define step', () => { + fillDefineCustomRule(rule); + openAddFilterPopover(); + fillAddFilterForm({ + key: 'host.name', + operator: 'exists', + }); + // Check that newly added filter exists + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); }); - // Check that newly added filter exists - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts index c03f992e79305..bb2481a6699bc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts @@ -7,10 +7,7 @@ import { getSavedQueryRule } from '../../../../objects/rule'; -import { - DEFINE_CONTINUE_BUTTON, - QUERY_BAR, -} from '../../../../screens/create_new_rule'; +import { DEFINE_CONTINUE_BUTTON, QUERY_BAR } from '../../../../screens/create_new_rule'; import { TOASTER } from '../../../../screens/alerts_detection_rules'; import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts index b94216cdfd812..5c4eeacc722ac 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts @@ -19,7 +19,7 @@ import { getDefineContinueButton, getIndexPatternClearButton, getRuleIndexInput, - selectEqlRuleType + selectEqlRuleType, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; @@ -54,20 +54,18 @@ describe('EQL Rule - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); - it( - 'Creates a new EQL rule with a sequence', () => { - const rule = getEqlSequenceRule(); + it('Creates a new EQL rule with a sequence', () => { + const rule = getEqlSequenceRule(); - selectEqlRuleType(); - fillDefineEqlRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); + selectEqlRuleType(); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.log('Asserting we have a new rule created'); - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - } - ); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); describe('with source data requiring EQL overrides', () => { before(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts index fe054c5084987..261a0046f8463 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts @@ -118,461 +118,465 @@ import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; // Skipping in MKI due to flake -describe('Indicator Match - Rule Creation', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { - describe('Detection rules, Indicator Match', () => { - const expectedUrls = getNewThreatIndicatorRule().references?.join(''); - const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); - const expectedTags = getNewThreatIndicatorRule().tags?.join(''); - const mitreAttack = getNewThreatIndicatorRule().threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); - const expectedNumberOfRules = 1; - const expectedNumberOfAlerts = '1 alert'; - - beforeEach(() => { - cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); - cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); - deleteAlertsAndRules(); - login(); - }); +describe( + 'Indicator Match - Rule Creation', + { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, + () => { + describe('Detection rules, Indicator Match', () => { + const expectedUrls = getNewThreatIndicatorRule().references?.join(''); + const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); + const expectedTags = getNewThreatIndicatorRule().tags?.join(''); + const mitreAttack = getNewThreatIndicatorRule().threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); + const expectedNumberOfRules = 1; + const expectedNumberOfAlerts = '1 alert'; + + beforeEach(() => { + cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); + cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); + deleteAlertsAndRules(); + login(); + }); - describe('Creating new indicator match rules', () => { - describe('Index patterns', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('Creating new indicator match rules', () => { + describe('Index patterns', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Contains a predefined index pattern', () => { - getRuleIndexInput().should('have.text', getIndexPatterns().join('')); - }); + it('Contains a predefined index pattern', () => { + getRuleIndexInput().should('have.text', getIndexPatterns().join('')); + }); - it('Does NOT show invalidation text on initial page load if indicator index pattern is filled out', () => { - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load if indicator index pattern is filled out', () => { + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('not.exist'); + }); - it('Shows invalidation text when you try to continue without filling it out', () => { - getIndexPatternClearButton().click(); - getIndicatorIndicatorIndex().type(`{backspace}{enter}`); - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('exist'); + it('Shows invalidation text when you try to continue without filling it out', () => { + getIndexPatternClearButton().click(); + getIndicatorIndicatorIndex().type(`{backspace}{enter}`); + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('exist'); + }); }); - }); - describe('Indicator index patterns', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('Indicator index patterns', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Contains a predefined index pattern', () => { - getIndicatorIndicatorIndex().should('have.text', getThreatIndexPatterns().join('')); - }); + it('Contains a predefined index pattern', () => { + getIndicatorIndicatorIndex().should('have.text', getThreatIndexPatterns().join('')); + }); - it('Does NOT show invalidation text on initial page load', () => { - getIndexPatternInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load', () => { + getIndexPatternInvalidationText().should('not.exist'); + }); - it('Shows invalidation text if you try to continue without filling it out', () => { - getIndicatorIndicatorIndex().type(`{backspace}{enter}`); - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('exist'); + it('Shows invalidation text if you try to continue without filling it out', () => { + getIndicatorIndicatorIndex().type(`{backspace}{enter}`); + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('exist'); + }); }); - }); - describe('custom query input', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('custom query input', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Has a default set of *:*', () => { - getCustomQueryInput().should('have.text', '*:*'); - }); + it('Has a default set of *:*', () => { + getCustomQueryInput().should('have.text', '*:*'); + }); - it('Shows invalidation text if text is removed', () => { - getCustomQueryInput().type('{selectall}{del}'); - getCustomQueryInvalidationText().should('exist'); + it('Shows invalidation text if text is removed', () => { + getCustomQueryInput().type('{selectall}{del}'); + getCustomQueryInvalidationText().should('exist'); + }); }); - }); - describe('indicator query input', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('indicator query input', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it(`Has a default set of ${DEFAULT_THREAT_MATCH_QUERY}`, () => { - getCustomIndicatorQueryInput().should('have.text', DEFAULT_THREAT_MATCH_QUERY); - }); + it(`Has a default set of ${DEFAULT_THREAT_MATCH_QUERY}`, () => { + getCustomIndicatorQueryInput().should('have.text', DEFAULT_THREAT_MATCH_QUERY); + }); - it('Shows invalidation text if text is removed', () => { - getCustomIndicatorQueryInput().type('{selectall}{del}'); - getThreatMatchQueryInvalidationText().should('exist'); + it('Shows invalidation text if text is removed', () => { + getCustomIndicatorQueryInput().type('{selectall}{del}'); + getThreatMatchQueryInvalidationText().should('exist'); + }); }); - }); - // FLAKY: https://github.com/elastic/kibana/issues/182669 - describe.skip('Indicator mapping', () => { - beforeEach(() => { - const rule = getNewThreatIndicatorRule(); - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - if (rule.index) { - fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); - } - }); + // FLAKY: https://github.com/elastic/kibana/issues/182669 + describe.skip('Indicator mapping', () => { + beforeEach(() => { + const rule = getNewThreatIndicatorRule(); + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + if (rule.index) { + fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); + } + }); - it('Does NOT show invalidation text on initial page load', () => { - getIndicatorInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load', () => { + getIndicatorInvalidationText().should('not.exist'); + }); - it('Shows invalidation text when you try to press continue without filling anything out', () => { - getDefineContinueButton().click(); - getIndicatorAtLeastOneInvalidationText().should('exist'); - }); + it('Shows invalidation text when you try to press continue without filling anything out', () => { + getDefineContinueButton().click(); + getIndicatorAtLeastOneInvalidationText().should('exist'); + }); - it('Shows invalidation text when the "AND" button is pressed and both the mappings are blank', () => { - getIndicatorAndButton().click(); - getIndicatorInvalidationText().should('exist'); - }); + it('Shows invalidation text when the "AND" button is pressed and both the mappings are blank', () => { + getIndicatorAndButton().click(); + getIndicatorInvalidationText().should('exist'); + }); - it('Shows invalidation text when the "OR" button is pressed and both the mappings are blank', () => { - getIndicatorOrButton().click(); - getIndicatorInvalidationText().should('exist'); - }); + it('Shows invalidation text when the "OR" button is pressed and both the mappings are blank', () => { + getIndicatorOrButton().click(); + getIndicatorInvalidationText().should('exist'); + }); - it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('not.exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('not.exist'); - }); - it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', + it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('exist'); - }); - it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'non-existent-value', - validColumns: 'indexField', + it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'non-existent-value', + validColumns: 'indexField', + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('exist'); - }); - it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'agent.name', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField().find('input').should('have.value', 'agent.name'); - getIndicatorMappingComboField() - .find('input') - .should('have.value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'agent.name', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField().find('input').should('have.value', 'agent.name'); + getIndicatorMappingComboField() + .find('input') + .should('have.value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'non-existent-value', - validColumns: 'indexField', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'second-non-existent-value', - validColumns: 'indexField', - }); - getIndicatorDeleteButton().click(); - getIndicatorMappingComboField() - .find('input') - .should('have.value', 'second-non-existent-value'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'non-existent-value', + validColumns: 'indexField', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'second-non-existent-value', + validColumns: 'indexField', + }); + getIndicatorDeleteButton().click(); + getIndicatorMappingComboField() + .find('input') + .should('have.value', 'second-non-existent-value'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'second-non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('have.value', 'second-non-existent-value'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'second-non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('have.value', 'second-non-existent-value'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('value', '') - .should('have.attr', 'placeholder', 'Search'); - getIndicatorMappingComboField() - .find('input') - .should('value', '') - .should('have.attr', 'placeholder', 'Search'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('value', '') + .should('have.attr', 'placeholder', 'Search'); + getIndicatorMappingComboField() + .find('input') + .should('value', '') + .should('have.attr', 'placeholder', 'Search'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'non-existent-value', - indicatorIndexField: 'non-existent-value', - validColumns: 'none', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 3, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton(2).click(); - getIndicatorIndexComboField(1) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField(1) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField(2) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(3).should('not.exist'); - getIndicatorMappingComboField(3).should('not.exist'); + it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'non-existent-value', + indicatorIndexField: 'non-existent-value', + validColumns: 'none', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 3, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton(2).click(); + getIndicatorIndexComboField(1) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField(1) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField(2) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(3).should('not.exist'); + getIndicatorMappingComboField(3).should('not.exist'); + }); + + it('Can add two OR rows and delete the second row. The first row has invalid data and the second row has valid data. The first row is deleted and the second row shifts up correctly.', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value-one', + indicatorIndexField: 'non-existent-value-two', + validColumns: 'none', + }); + getIndicatorOrButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField() + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); }); - it('Can add two OR rows and delete the second row. The first row has invalid data and the second row has valid data. The first row is deleted and the second row shifts up correctly.', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value-one', - indicatorIndexField: 'non-existent-value-two', - validColumns: 'none', - }); - getIndicatorOrButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField() - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); + describe('Schedule', () => { + it('IM rule has 1h time interval and lookback by default', () => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); + fillAboutRuleAndContinue(getNewThreatIndicatorRule()); + + cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', '1'); + cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', 'h'); + cy.get(SCHEDULE_LOOKBACK_AMOUNT_INPUT).invoke('val').should('eql', '5'); + cy.get(SCHEDULE_LOOKBACK_UNITS_INPUT).invoke('val').should('eql', 'm'); + }); }); }); - describe('Schedule', () => { - it('IM rule has 1h time interval and lookback by default', () => { + describe('Generating signals', () => { + it('Creates and enables a new Indicator Match rule', () => { + const rule = getNewThreatIndicatorRule(); visit(CREATE_RULE_URL); selectIndicatorMatchType(); - fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); - fillAboutRuleAndContinue(getNewThreatIndicatorRule()); + fillDefineIndicatorMatchRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createAndEnableRule(); + openRuleManagementPageViaBreadcrumbs(); + + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); + + expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); + + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'Critical'); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); + + goToRuleDetailsOf(rule.name); + + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); + getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path); + getDetails(REFERENCE_URLS_DETAILS).should((details) => { + expect(removeExternalLinkText(details.text())).equal(expectedUrls); + }); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { + expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); + }); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); + }); + cy.get(INVESTIGATION_NOTES_TOGGLE).click(); + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); + + cy.get(DEFINITION_DETAILS).within(() => { + if (rule.index) { + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); + } + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join('')); + getDetails(INDICATOR_MAPPING).should( + 'have.text', + `${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}` + ); + getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); + }); - cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', '1'); - cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', 'h'); - cy.get(SCHEDULE_LOOKBACK_AMOUNT_INPUT).invoke('val').should('eql', '5'); - cy.get(SCHEDULE_LOOKBACK_UNITS_INPUT).invoke('val').should('eql', 'm'); - }); - }); - }); + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS) + .find(INTERVAL_ABBR_VALUE) + .should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS) + .find(INTERVAL_ABBR_VALUE) + .should('have.text', `${humanizedDuration}`); + }); - describe('Generating signals', () => { - it('Creates and enables a new Indicator Match rule', () => { - const rule = getNewThreatIndicatorRule(); - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - fillDefineIndicatorMatchRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'Critical'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - - cy.get(DEFINITION_DETAILS).within(() => { - if (rule.index) { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); - } - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join('')); - getDetails(INDICATOR_MAPPING).should( - 'have.text', - `${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}` - ); - getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); - }); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); + cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); + cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); + cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score); }); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); + it('Investigate alert in timeline', () => { + loadPrepackagedTimelineTemplates(); + createRule(getNewThreatIndicatorRule({ rule_id: 'rule_testing', enabled: true })).then( + (rule) => visitRuleDetailsPage(rule.body.id) + ); - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); - cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); - cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); - cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score); - }); + waitForAlertsToPopulate(); + investigateFirstAlertInTimeline(); - it('Investigate alert in timeline', () => { - loadPrepackagedTimelineTemplates(); - createRule(getNewThreatIndicatorRule({ rule_id: 'rule_testing', enabled: true })).then( - (rule) => visitRuleDetailsPage(rule.body.id) - ); - - waitForAlertsToPopulate(); - investigateFirstAlertInTimeline(); - - cy.get(PROVIDER_BADGE).should('have.length', 3); - cy.get(PROVIDER_BADGE).should( - 'have.text', - `threat.enrichments.matched.atomic: "${ - indicatorRuleMatchingDoc.atomic - }"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${ - getNewThreatIndicatorRule().threat_mapping[0].entries[0].field - }"` - ); - - cy.get(INDICATOR_MATCH_ROW_RENDER).should( - 'have.text', - `${getNewThreatIndicatorRule().threat_mapping[0].entries[0].field}matched${ - indicatorRuleMatchingDoc.atomic - }indicator_match_ruleprovided` + ` byAbuseCH malware` - ); - }); - }); + cy.get(PROVIDER_BADGE).should('have.length', 3); + cy.get(PROVIDER_BADGE).should( + 'have.text', + `threat.enrichments.matched.atomic: "${ + indicatorRuleMatchingDoc.atomic + }"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${ + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field + }"` + ); - describe('Duplicates the indicator rule', () => { - const TESTED_RULE_DATA = getNewThreatIndicatorRule({ - name: 'Indicator rule duplicate test', - rule_id: 'rule_testing', - enabled: false, + cy.get(INDICATOR_MATCH_ROW_RENDER).should( + 'have.text', + `${getNewThreatIndicatorRule().threat_mapping[0].entries[0].field}matched${ + indicatorRuleMatchingDoc.atomic + }indicator_match_ruleprovided` + ` byAbuseCH malware` + ); + }); }); - describe('on rule editing page', () => { - beforeEach(() => { - createRule(TESTED_RULE_DATA); - visit(RULES_MANAGEMENT_URL); - disableAutoRefresh(); + describe('Duplicates the indicator rule', () => { + const TESTED_RULE_DATA = getNewThreatIndicatorRule({ + name: 'Indicator rule duplicate test', + rule_id: 'rule_testing', + enabled: false, }); - it('Allows the rule to be duplicated from the table', () => { - duplicateFirstRule(); - goBackToRuleDetails(); - goBackToRulesTable(); - checkDuplicatedRule(TESTED_RULE_DATA.name); - }); + describe('on rule editing page', () => { + beforeEach(() => { + createRule(TESTED_RULE_DATA); + visit(RULES_MANAGEMENT_URL); + disableAutoRefresh(); + }); - it("Allows the rule to be duplicated from the table's bulk actions", () => { - selectAllRules(); - duplicateSelectedRulesWithExceptions(); - checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); - }); - }); + it('Allows the rule to be duplicated from the table', () => { + duplicateFirstRule(); + goBackToRuleDetails(); + goBackToRulesTable(); + checkDuplicatedRule(TESTED_RULE_DATA.name); + }); - describe('on rule details page', () => { - beforeEach(() => { - createRule(getNewThreatIndicatorRule(TESTED_RULE_DATA)).then((rule) => - visitRuleDetailsPage(rule.body.id) - ); + it("Allows the rule to be duplicated from the table's bulk actions", () => { + selectAllRules(); + duplicateSelectedRulesWithExceptions(); + checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + }); }); - it('Allows the rule to be duplicated', () => { - duplicateRuleFromMenu(); - goBackToRuleDetails(); - goBackToRulesTable(); - checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + describe('on rule details page', () => { + beforeEach(() => { + createRule(getNewThreatIndicatorRule(TESTED_RULE_DATA)).then((rule) => + visitRuleDetailsPage(rule.body.id) + ); + }); + + it('Allows the rule to be duplicated', () => { + duplicateRuleFromMenu(); + goBackToRuleDetails(); + goBackToRulesTable(); + checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + }); }); }); }); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts index 75da9f6e6c5df..beaac5cd14d8b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts @@ -15,7 +15,7 @@ import { fillDefineNewTermsRuleAndContinue, fillScheduleRuleAndContinue, selectNewTermsRuleType, - createRuleWithoutEnabling + createRuleWithoutEnabling, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts index 825b8300ab360..961c2237f1fbf 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts @@ -12,7 +12,7 @@ import { createRuleWithoutEnabling, fillAboutRuleWithOverrideAndContinue, fillDefineCustomRuleAndContinue, - fillScheduleRuleAndContinue + fillScheduleRuleAndContinue, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts index 2e16e722b026f..0d6c4be9b0c4c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts @@ -28,10 +28,7 @@ import { import { saveEditedRule, visitEditRulePage } from '../../../../tasks/edit_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { - assertDetailsNotExist, - getDetails, -} from '../../../../tasks/rule_details'; +import { assertDetailsNotExist, getDetails } from '../../../../tasks/rule_details'; import { createRule } from '../../../../tasks/api_calls/rules'; import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; @@ -70,8 +67,8 @@ describe('Saved query rules, rule edit', { tags: ['@ess', '@serverless'] }, () = const expectedCustomTestQuery = 'random test query'; createSavedQuery(savedQueryName, savedQueryQuery).then((response) => { cy.log(JSON.stringify(response.body, null, 2)); - createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then( - (rule) => visitEditRulePage(rule.body.id) + createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then((rule) => + visitEditRulePage(rule.body.id) ); }); From c9c7066a3adde2c8e5de6746156b285ce0f86ffc Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 7 Apr 2025 08:01:04 -0700 Subject: [PATCH 3/3] fixing lint errors --- .../basic_license_check_rule_form.cy.ts | 58 +- .../alert_suppression/custom_query_rule.cy.ts | 2 +- .../alert_suppression/new_terms_rule.cy.ts | 2 +- .../alert_suppression/threshold_rule.cy.ts | 5 +- .../rule_creation/custom_query_rule.cy.ts | 2 +- .../custom_query_rule_data_view.cy.ts | 70 +- .../custom_saved_query_rule.cy.ts | 5 +- .../rule_creation/eql_rule.cy.ts | 24 +- .../rule_creation/indicator_match_rule.cy.ts | 800 +++++++++--------- .../rule_creation/new_terms_rule.cy.ts | 2 +- .../rule_creation/override.cy.ts | 2 +- .../rule_edit/custom_saved_query.cy.ts | 9 +- 12 files changed, 489 insertions(+), 492 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts index 87b7da95bb7b6..3ebe8230a3d04 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/basic_license_check_rule_form.cy.ts @@ -36,44 +36,44 @@ describe( tags: ['@ess'], }, () => { - beforeEach(() => { - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - startBasicLicense(); - }); + beforeEach(() => { + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + startBasicLicense(); + }); - it('cannot create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { - // Default query rule - openSuppressionFieldsTooltipAndCheckLicense(); + it('cannot create rule with rule execution suppression on basic license for all rules with enabled suppression', () => { + // Default query rule + openSuppressionFieldsTooltipAndCheckLicense(); - selectIndicatorMatchType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectIndicatorMatchType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectNewTermsRuleType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectNewTermsRuleType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectEsqlRuleType(); - openSuppressionFieldsTooltipAndCheckLicense(); + selectEsqlRuleType(); + openSuppressionFieldsTooltipAndCheckLicense(); - selectEqlRuleType(); - cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); - cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); + selectEqlRuleType(); + cy.get(ALERT_SUPPRESSION_FIELDS_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_FIELDS).trigger('mouseover'); - // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this - cy.get(TOOLTIP).contains('Platinum license'); + // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this + cy.get(TOOLTIP).contains('Platinum license'); - // ML Rules require Platinum license - cy.get(MACHINE_LEARNING_TYPE).get('button').should('be.disabled'); + // ML Rules require Platinum license + cy.get(MACHINE_LEARNING_TYPE).get('button').should('be.disabled'); - selectThresholdRuleType(); - cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.disabled'); - cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).parent().trigger('mouseover'); - // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this - cy.get(TOOLTIP).contains('Platinum license'); + selectThresholdRuleType(); + cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).should('be.disabled'); + cy.get(THRESHOLD_ENABLE_SUPPRESSION_CHECKBOX).parent().trigger('mouseover'); + // Platinum license is required, tooltip on disabled alert suppression checkbox should tell this + cy.get(TOOLTIP).contains('Platinum license'); - cy.get(ALERT_SUPPRESSION_DURATION_VALUE_INPUT).should('be.disabled'); - cy.get(ALERT_SUPPRESSION_DURATION_UNIT_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_DURATION_VALUE_INPUT).should('be.disabled'); + cy.get(ALERT_SUPPRESSION_DURATION_UNIT_INPUT).should('be.disabled'); }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts index 6cfd441499792..529f01a9aafd5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/custom_query_rule.cy.ts @@ -35,7 +35,7 @@ describe('Custom Query Rule - Alert suppression', { tags: ['@ess', '@serverless' beforeEach(() => { deleteAlertsAndRules(); login(); - visit(CREATE_RULE_URL); + visit(CREATE_RULE_URL); }); const SUPPRESS_BY_FIELDS = ['source.ip']; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts index 00c180c0e90fe..08148bde1f69e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/new_terms_rule.cy.ts @@ -11,7 +11,7 @@ import { DEFINITION_DETAILS, SUPPRESS_BY_DETAILS, SUPPRESS_FOR_DETAILS, - SUPPRESS_MISSING_FIELD + SUPPRESS_MISSING_FIELD, } from '../../../../screens/rule_details'; import { getDetails } from '../../../../tasks/rule_details'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts index b52f43cf8142f..a3de0166c72c8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/alert_suppression/threshold_rule.cy.ts @@ -6,10 +6,7 @@ */ import { getNewThresholdRule } from '../../../../objects/rule'; -import { - DEFINITION_DETAILS, - SUPPRESS_FOR_DETAILS, -} from '../../../../screens/rule_details'; +import { DEFINITION_DETAILS, SUPPRESS_FOR_DETAILS } from '../../../../screens/rule_details'; import { goToRuleDetailsOf } from '../../../../tasks/alerts_detection_rules'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts index 70283f3be6026..354fd97815ccc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule.cy.ts @@ -16,7 +16,7 @@ import { fillDefineCustomRuleAndContinue, createRuleWithoutEnabling, fillDefineCustomRule, - openAddFilterPopover + openAddFilterPopover, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts index 515289c0cfe70..741f90994d211 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_query_rule_data_view.cy.ts @@ -30,42 +30,46 @@ import { fillAddFilterForm } from '../../../../tasks/search_bar'; import { CREATE_RULE_URL } from '../../../../urls/navigation'; -describe('Custom query rules with data views - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { - const rule = getDataViewRule(); +describe( + 'Custom query rules with data views - Rule Creation', + { tags: ['@ess', '@serverless'] }, + () => { + const rule = getDataViewRule(); - beforeEach(() => { - if (rule.data_view_id != null) { - postDataView(rule.data_view_id); - } - deleteAlertsAndRules(); - login(); - visit(CREATE_RULE_URL); - }); + beforeEach(() => { + if (rule.data_view_id != null) { + postDataView(rule.data_view_id); + } + deleteAlertsAndRules(); + login(); + visit(CREATE_RULE_URL); + }); - afterEach(() => { - if (rule.data_view_id != null) { - deleteDataView(rule.data_view_id); - } - }); + afterEach(() => { + if (rule.data_view_id != null) { + deleteDataView(rule.data_view_id); + } + }); - it('Creates and enables a new rule', function () { - fillDefineCustomRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); + it('Creates and enables a new rule', function () { + fillDefineCustomRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.log('Asserting we have a new rule created'); - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - }); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); - it('Adds filter on define step', () => { - fillDefineCustomRule(rule); - openAddFilterPopover(); - fillAddFilterForm({ - key: 'host.name', - operator: 'exists', + it('Adds filter on define step', () => { + fillDefineCustomRule(rule); + openAddFilterPopover(); + fillAddFilterForm({ + key: 'host.name', + operator: 'exists', + }); + // Check that newly added filter exists + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); }); - // Check that newly added filter exists - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('have.text', 'host.name: exists'); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts index c03f992e79305..bb2481a6699bc 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/custom_saved_query_rule.cy.ts @@ -7,10 +7,7 @@ import { getSavedQueryRule } from '../../../../objects/rule'; -import { - DEFINE_CONTINUE_BUTTON, - QUERY_BAR, -} from '../../../../screens/create_new_rule'; +import { DEFINE_CONTINUE_BUTTON, QUERY_BAR } from '../../../../screens/create_new_rule'; import { TOASTER } from '../../../../screens/alerts_detection_rules'; import { RULE_NAME_HEADER } from '../../../../screens/rule_details'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts index b94216cdfd812..5c4eeacc722ac 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts @@ -19,7 +19,7 @@ import { getDefineContinueButton, getIndexPatternClearButton, getRuleIndexInput, - selectEqlRuleType + selectEqlRuleType, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; @@ -54,20 +54,18 @@ describe('EQL Rule - Rule Creation', { tags: ['@ess', '@serverless'] }, () => { cy.get(RULE_NAME_HEADER).should('contain', rule.name); }); - it( - 'Creates a new EQL rule with a sequence', () => { - const rule = getEqlSequenceRule(); + it('Creates a new EQL rule with a sequence', () => { + const rule = getEqlSequenceRule(); - selectEqlRuleType(); - fillDefineEqlRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createRuleWithoutEnabling(); + selectEqlRuleType(); + fillDefineEqlRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createRuleWithoutEnabling(); - cy.log('Asserting we have a new rule created'); - cy.get(RULE_NAME_HEADER).should('contain', rule.name); - } - ); + cy.log('Asserting we have a new rule created'); + cy.get(RULE_NAME_HEADER).should('contain', rule.name); + }); describe('with source data requiring EQL overrides', () => { before(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts index fe054c5084987..261a0046f8463 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/indicator_match_rule.cy.ts @@ -118,461 +118,465 @@ import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"'; // Skipping in MKI due to flake -describe('Indicator Match - Rule Creation', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, () => { - describe('Detection rules, Indicator Match', () => { - const expectedUrls = getNewThreatIndicatorRule().references?.join(''); - const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); - const expectedTags = getNewThreatIndicatorRule().tags?.join(''); - const mitreAttack = getNewThreatIndicatorRule().threat; - const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); - const expectedNumberOfRules = 1; - const expectedNumberOfAlerts = '1 alert'; - - beforeEach(() => { - cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); - cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); - deleteAlertsAndRules(); - login(); - }); +describe( + 'Indicator Match - Rule Creation', + { tags: ['@ess', '@serverless', '@skipInServerlessMKI'] }, + () => { + describe('Detection rules, Indicator Match', () => { + const expectedUrls = getNewThreatIndicatorRule().references?.join(''); + const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join(''); + const expectedTags = getNewThreatIndicatorRule().tags?.join(''); + const mitreAttack = getNewThreatIndicatorRule().threat; + const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []); + const expectedNumberOfRules = 1; + const expectedNumberOfAlerts = '1 alert'; + + beforeEach(() => { + cy.task('esArchiverLoad', { archiveName: 'threat_indicator' }); + cy.task('esArchiverLoad', { archiveName: 'suspicious_source_event' }); + deleteAlertsAndRules(); + login(); + }); - describe('Creating new indicator match rules', () => { - describe('Index patterns', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('Creating new indicator match rules', () => { + describe('Index patterns', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Contains a predefined index pattern', () => { - getRuleIndexInput().should('have.text', getIndexPatterns().join('')); - }); + it('Contains a predefined index pattern', () => { + getRuleIndexInput().should('have.text', getIndexPatterns().join('')); + }); - it('Does NOT show invalidation text on initial page load if indicator index pattern is filled out', () => { - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load if indicator index pattern is filled out', () => { + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('not.exist'); + }); - it('Shows invalidation text when you try to continue without filling it out', () => { - getIndexPatternClearButton().click(); - getIndicatorIndicatorIndex().type(`{backspace}{enter}`); - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('exist'); + it('Shows invalidation text when you try to continue without filling it out', () => { + getIndexPatternClearButton().click(); + getIndicatorIndicatorIndex().type(`{backspace}{enter}`); + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('exist'); + }); }); - }); - describe('Indicator index patterns', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('Indicator index patterns', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Contains a predefined index pattern', () => { - getIndicatorIndicatorIndex().should('have.text', getThreatIndexPatterns().join('')); - }); + it('Contains a predefined index pattern', () => { + getIndicatorIndicatorIndex().should('have.text', getThreatIndexPatterns().join('')); + }); - it('Does NOT show invalidation text on initial page load', () => { - getIndexPatternInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load', () => { + getIndexPatternInvalidationText().should('not.exist'); + }); - it('Shows invalidation text if you try to continue without filling it out', () => { - getIndicatorIndicatorIndex().type(`{backspace}{enter}`); - getDefineContinueButton().click(); - getIndexPatternInvalidationText().should('exist'); + it('Shows invalidation text if you try to continue without filling it out', () => { + getIndicatorIndicatorIndex().type(`{backspace}{enter}`); + getDefineContinueButton().click(); + getIndexPatternInvalidationText().should('exist'); + }); }); - }); - describe('custom query input', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('custom query input', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it('Has a default set of *:*', () => { - getCustomQueryInput().should('have.text', '*:*'); - }); + it('Has a default set of *:*', () => { + getCustomQueryInput().should('have.text', '*:*'); + }); - it('Shows invalidation text if text is removed', () => { - getCustomQueryInput().type('{selectall}{del}'); - getCustomQueryInvalidationText().should('exist'); + it('Shows invalidation text if text is removed', () => { + getCustomQueryInput().type('{selectall}{del}'); + getCustomQueryInvalidationText().should('exist'); + }); }); - }); - describe('indicator query input', () => { - beforeEach(() => { - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - }); + describe('indicator query input', () => { + beforeEach(() => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + }); - it(`Has a default set of ${DEFAULT_THREAT_MATCH_QUERY}`, () => { - getCustomIndicatorQueryInput().should('have.text', DEFAULT_THREAT_MATCH_QUERY); - }); + it(`Has a default set of ${DEFAULT_THREAT_MATCH_QUERY}`, () => { + getCustomIndicatorQueryInput().should('have.text', DEFAULT_THREAT_MATCH_QUERY); + }); - it('Shows invalidation text if text is removed', () => { - getCustomIndicatorQueryInput().type('{selectall}{del}'); - getThreatMatchQueryInvalidationText().should('exist'); + it('Shows invalidation text if text is removed', () => { + getCustomIndicatorQueryInput().type('{selectall}{del}'); + getThreatMatchQueryInvalidationText().should('exist'); + }); }); - }); - // FLAKY: https://github.com/elastic/kibana/issues/182669 - describe.skip('Indicator mapping', () => { - beforeEach(() => { - const rule = getNewThreatIndicatorRule(); - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - if (rule.index) { - fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); - } - }); + // FLAKY: https://github.com/elastic/kibana/issues/182669 + describe.skip('Indicator mapping', () => { + beforeEach(() => { + const rule = getNewThreatIndicatorRule(); + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + if (rule.index) { + fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index); + } + }); - it('Does NOT show invalidation text on initial page load', () => { - getIndicatorInvalidationText().should('not.exist'); - }); + it('Does NOT show invalidation text on initial page load', () => { + getIndicatorInvalidationText().should('not.exist'); + }); - it('Shows invalidation text when you try to press continue without filling anything out', () => { - getDefineContinueButton().click(); - getIndicatorAtLeastOneInvalidationText().should('exist'); - }); + it('Shows invalidation text when you try to press continue without filling anything out', () => { + getDefineContinueButton().click(); + getIndicatorAtLeastOneInvalidationText().should('exist'); + }); - it('Shows invalidation text when the "AND" button is pressed and both the mappings are blank', () => { - getIndicatorAndButton().click(); - getIndicatorInvalidationText().should('exist'); - }); + it('Shows invalidation text when the "AND" button is pressed and both the mappings are blank', () => { + getIndicatorAndButton().click(); + getIndicatorInvalidationText().should('exist'); + }); - it('Shows invalidation text when the "OR" button is pressed and both the mappings are blank', () => { - getIndicatorOrButton().click(); - getIndicatorInvalidationText().should('exist'); - }); + it('Shows invalidation text when the "OR" button is pressed and both the mappings are blank', () => { + getIndicatorOrButton().click(); + getIndicatorInvalidationText().should('exist'); + }); - it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('not.exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('not.exist'); - }); - it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', + it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('exist'); - }); - it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'non-existent-value', - validColumns: 'indexField', + it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'non-existent-value', + validColumns: 'indexField', + }); + getDefineContinueButton().click(); + getIndicatorInvalidationText().should('exist'); }); - getDefineContinueButton().click(); - getIndicatorInvalidationText().should('exist'); - }); - it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'agent.name', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField().find('input').should('have.value', 'agent.name'); - getIndicatorMappingComboField() - .find('input') - .should('have.value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'agent.name', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField().find('input').should('have.value', 'agent.name'); + getIndicatorMappingComboField() + .find('input') + .should('have.value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'non-existent-value', - validColumns: 'indexField', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: 'second-non-existent-value', - validColumns: 'indexField', - }); - getIndicatorDeleteButton().click(); - getIndicatorMappingComboField() - .find('input') - .should('have.value', 'second-non-existent-value'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'non-existent-value', + validColumns: 'indexField', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: 'second-non-existent-value', + validColumns: 'indexField', + }); + getIndicatorDeleteButton().click(); + getIndicatorMappingComboField() + .find('input') + .should('have.value', 'second-non-existent-value'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'second-non-existent-value', - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - validColumns: 'indicatorField', - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('have.value', 'second-non-existent-value'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'second-non-existent-value', + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + validColumns: 'indicatorField', + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('have.value', 'second-non-existent-value'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('value', '') - .should('have.attr', 'placeholder', 'Search'); - getIndicatorMappingComboField() - .find('input') - .should('value', '') - .should('have.attr', 'placeholder', 'Search'); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); - }); + it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('value', '') + .should('have.attr', 'placeholder', 'Search'); + getIndicatorMappingComboField() + .find('input') + .should('value', '') + .should('have.attr', 'placeholder', 'Search'); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); - it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => { - fillIndicatorMatchRow({ - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: 'non-existent-value', - indicatorIndexField: 'non-existent-value', - validColumns: 'none', - }); - getIndicatorAndButton().click(); - fillIndicatorMatchRow({ - rowNumber: 3, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton(2).click(); - getIndicatorIndexComboField(1) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField(1) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField(2) - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(3).should('not.exist'); - getIndicatorMappingComboField(3).should('not.exist'); + it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => { + fillIndicatorMatchRow({ + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: 'non-existent-value', + indicatorIndexField: 'non-existent-value', + validColumns: 'none', + }); + getIndicatorAndButton().click(); + fillIndicatorMatchRow({ + rowNumber: 3, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton(2).click(); + getIndicatorIndexComboField(1) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField(1) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField(2) + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(3).should('not.exist'); + getIndicatorMappingComboField(3).should('not.exist'); + }); + + it('Can add two OR rows and delete the second row. The first row has invalid data and the second row has valid data. The first row is deleted and the second row shifts up correctly.', () => { + fillIndicatorMatchRow({ + indexField: 'non-existent-value-one', + indicatorIndexField: 'non-existent-value-two', + validColumns: 'none', + }); + getIndicatorOrButton().click(); + fillIndicatorMatchRow({ + rowNumber: 2, + indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, + indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, + }); + getIndicatorDeleteButton().click(); + getIndicatorIndexComboField() + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); + getIndicatorMappingComboField() + .find('input') + .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); + getIndicatorIndexComboField(2).should('not.exist'); + getIndicatorMappingComboField(2).should('not.exist'); + }); }); - it('Can add two OR rows and delete the second row. The first row has invalid data and the second row has valid data. The first row is deleted and the second row shifts up correctly.', () => { - fillIndicatorMatchRow({ - indexField: 'non-existent-value-one', - indicatorIndexField: 'non-existent-value-two', - validColumns: 'none', - }); - getIndicatorOrButton().click(); - fillIndicatorMatchRow({ - rowNumber: 2, - indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field, - indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value, - }); - getIndicatorDeleteButton().click(); - getIndicatorIndexComboField() - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].field); - getIndicatorMappingComboField() - .find('input') - .should('value', getNewThreatIndicatorRule().threat_mapping[0].entries[0].value); - getIndicatorIndexComboField(2).should('not.exist'); - getIndicatorMappingComboField(2).should('not.exist'); + describe('Schedule', () => { + it('IM rule has 1h time interval and lookback by default', () => { + visit(CREATE_RULE_URL); + selectIndicatorMatchType(); + fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); + fillAboutRuleAndContinue(getNewThreatIndicatorRule()); + + cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', '1'); + cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', 'h'); + cy.get(SCHEDULE_LOOKBACK_AMOUNT_INPUT).invoke('val').should('eql', '5'); + cy.get(SCHEDULE_LOOKBACK_UNITS_INPUT).invoke('val').should('eql', 'm'); + }); }); }); - describe('Schedule', () => { - it('IM rule has 1h time interval and lookback by default', () => { + describe('Generating signals', () => { + it('Creates and enables a new Indicator Match rule', () => { + const rule = getNewThreatIndicatorRule(); visit(CREATE_RULE_URL); selectIndicatorMatchType(); - fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); - fillAboutRuleAndContinue(getNewThreatIndicatorRule()); + fillDefineIndicatorMatchRuleAndContinue(rule); + fillAboutRuleAndContinue(rule); + fillScheduleRuleAndContinue(rule); + createAndEnableRule(); + openRuleManagementPageViaBreadcrumbs(); + + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); + + expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); + + cy.get(RULE_NAME).should('have.text', rule.name); + cy.get(RISK_SCORE).should('have.text', rule.risk_score); + cy.get(SEVERITY).should('have.text', 'Critical'); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); + + goToRuleDetailsOf(rule.name); + + cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); + getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); + getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path); + getDetails(REFERENCE_URLS_DETAILS).should((details) => { + expect(removeExternalLinkText(details.text())).equal(expectedUrls); + }); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { + expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); + }); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); + }); + cy.get(INVESTIGATION_NOTES_TOGGLE).click(); + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); + + cy.get(DEFINITION_DETAILS).within(() => { + if (rule.index) { + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); + } + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join('')); + getDetails(INDICATOR_MAPPING).should( + 'have.text', + `${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}` + ); + getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); + }); - cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', '1'); - cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', 'h'); - cy.get(SCHEDULE_LOOKBACK_AMOUNT_INPUT).invoke('val').should('eql', '5'); - cy.get(SCHEDULE_LOOKBACK_UNITS_INPUT).invoke('val').should('eql', 'm'); - }); - }); - }); + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS) + .find(INTERVAL_ABBR_VALUE) + .should('have.text', `${rule.interval}`); + const humanizedDuration = getHumanizedDuration( + rule.from ?? 'now-6m', + rule.interval ?? '5m' + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS) + .find(INTERVAL_ABBR_VALUE) + .should('have.text', `${humanizedDuration}`); + }); - describe('Generating signals', () => { - it('Creates and enables a new Indicator Match rule', () => { - const rule = getNewThreatIndicatorRule(); - visit(CREATE_RULE_URL); - selectIndicatorMatchType(); - fillDefineIndicatorMatchRuleAndContinue(rule); - fillAboutRuleAndContinue(rule); - fillScheduleRuleAndContinue(rule); - createAndEnableRule(); - openRuleManagementPageViaBreadcrumbs(); - - cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); - - expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules); - - cy.get(RULE_NAME).should('have.text', rule.name); - cy.get(RISK_SCORE).should('have.text', rule.risk_score); - cy.get(SEVERITY).should('have.text', 'Critical'); - cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); - - goToRuleDetailsOf(rule.name); - - cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', 'Critical'); - getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score); - getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path); - getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); - }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); - }); - cy.get(INVESTIGATION_NOTES_TOGGLE).click(); - cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); - - cy.get(DEFINITION_DETAILS).within(() => { - if (rule.index) { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.index.join('')); - } - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*'); - getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match'); - getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); - getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join('')); - getDetails(INDICATOR_MAPPING).should( - 'have.text', - `${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}` - ); - getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*'); - }); + waitForTheRuleToBeExecuted(); + waitForAlertsToPopulate(); - cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${rule.interval}`); - const humanizedDuration = getHumanizedDuration( - rule.from ?? 'now-6m', - rule.interval ?? '5m' - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS) - .find(INTERVAL_ABBR_VALUE) - .should('have.text', `${humanizedDuration}`); + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); + cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); + cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); + cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score); }); - waitForTheRuleToBeExecuted(); - waitForAlertsToPopulate(); + it('Investigate alert in timeline', () => { + loadPrepackagedTimelineTemplates(); + createRule(getNewThreatIndicatorRule({ rule_id: 'rule_testing', enabled: true })).then( + (rule) => visitRuleDetailsPage(rule.body.id) + ); - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts); - cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name); - cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity?.toLowerCase()); - cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score); - }); + waitForAlertsToPopulate(); + investigateFirstAlertInTimeline(); - it('Investigate alert in timeline', () => { - loadPrepackagedTimelineTemplates(); - createRule(getNewThreatIndicatorRule({ rule_id: 'rule_testing', enabled: true })).then( - (rule) => visitRuleDetailsPage(rule.body.id) - ); - - waitForAlertsToPopulate(); - investigateFirstAlertInTimeline(); - - cy.get(PROVIDER_BADGE).should('have.length', 3); - cy.get(PROVIDER_BADGE).should( - 'have.text', - `threat.enrichments.matched.atomic: "${ - indicatorRuleMatchingDoc.atomic - }"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${ - getNewThreatIndicatorRule().threat_mapping[0].entries[0].field - }"` - ); - - cy.get(INDICATOR_MATCH_ROW_RENDER).should( - 'have.text', - `${getNewThreatIndicatorRule().threat_mapping[0].entries[0].field}matched${ - indicatorRuleMatchingDoc.atomic - }indicator_match_ruleprovided` + ` byAbuseCH malware` - ); - }); - }); + cy.get(PROVIDER_BADGE).should('have.length', 3); + cy.get(PROVIDER_BADGE).should( + 'have.text', + `threat.enrichments.matched.atomic: "${ + indicatorRuleMatchingDoc.atomic + }"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${ + getNewThreatIndicatorRule().threat_mapping[0].entries[0].field + }"` + ); - describe('Duplicates the indicator rule', () => { - const TESTED_RULE_DATA = getNewThreatIndicatorRule({ - name: 'Indicator rule duplicate test', - rule_id: 'rule_testing', - enabled: false, + cy.get(INDICATOR_MATCH_ROW_RENDER).should( + 'have.text', + `${getNewThreatIndicatorRule().threat_mapping[0].entries[0].field}matched${ + indicatorRuleMatchingDoc.atomic + }indicator_match_ruleprovided` + ` byAbuseCH malware` + ); + }); }); - describe('on rule editing page', () => { - beforeEach(() => { - createRule(TESTED_RULE_DATA); - visit(RULES_MANAGEMENT_URL); - disableAutoRefresh(); + describe('Duplicates the indicator rule', () => { + const TESTED_RULE_DATA = getNewThreatIndicatorRule({ + name: 'Indicator rule duplicate test', + rule_id: 'rule_testing', + enabled: false, }); - it('Allows the rule to be duplicated from the table', () => { - duplicateFirstRule(); - goBackToRuleDetails(); - goBackToRulesTable(); - checkDuplicatedRule(TESTED_RULE_DATA.name); - }); + describe('on rule editing page', () => { + beforeEach(() => { + createRule(TESTED_RULE_DATA); + visit(RULES_MANAGEMENT_URL); + disableAutoRefresh(); + }); - it("Allows the rule to be duplicated from the table's bulk actions", () => { - selectAllRules(); - duplicateSelectedRulesWithExceptions(); - checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); - }); - }); + it('Allows the rule to be duplicated from the table', () => { + duplicateFirstRule(); + goBackToRuleDetails(); + goBackToRulesTable(); + checkDuplicatedRule(TESTED_RULE_DATA.name); + }); - describe('on rule details page', () => { - beforeEach(() => { - createRule(getNewThreatIndicatorRule(TESTED_RULE_DATA)).then((rule) => - visitRuleDetailsPage(rule.body.id) - ); + it("Allows the rule to be duplicated from the table's bulk actions", () => { + selectAllRules(); + duplicateSelectedRulesWithExceptions(); + checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + }); }); - it('Allows the rule to be duplicated', () => { - duplicateRuleFromMenu(); - goBackToRuleDetails(); - goBackToRulesTable(); - checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + describe('on rule details page', () => { + beforeEach(() => { + createRule(getNewThreatIndicatorRule(TESTED_RULE_DATA)).then((rule) => + visitRuleDetailsPage(rule.body.id) + ); + }); + + it('Allows the rule to be duplicated', () => { + duplicateRuleFromMenu(); + goBackToRuleDetails(); + goBackToRulesTable(); + checkDuplicatedRule(`${TESTED_RULE_DATA.name} [Duplicate]`); + }); }); }); }); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts index 75da9f6e6c5df..beaac5cd14d8b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/new_terms_rule.cy.ts @@ -15,7 +15,7 @@ import { fillDefineNewTermsRuleAndContinue, fillScheduleRuleAndContinue, selectNewTermsRuleType, - createRuleWithoutEnabling + createRuleWithoutEnabling, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts index 825b8300ab360..961c2237f1fbf 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/override.cy.ts @@ -12,7 +12,7 @@ import { createRuleWithoutEnabling, fillAboutRuleWithOverrideAndContinue, fillDefineCustomRuleAndContinue, - fillScheduleRuleAndContinue + fillScheduleRuleAndContinue, } from '../../../../tasks/create_new_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts index 2e16e722b026f..0d6c4be9b0c4c 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/custom_saved_query.cy.ts @@ -28,10 +28,7 @@ import { import { saveEditedRule, visitEditRulePage } from '../../../../tasks/edit_rule'; import { login } from '../../../../tasks/login'; import { visit } from '../../../../tasks/navigation'; -import { - assertDetailsNotExist, - getDetails, -} from '../../../../tasks/rule_details'; +import { assertDetailsNotExist, getDetails } from '../../../../tasks/rule_details'; import { createRule } from '../../../../tasks/api_calls/rules'; import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; @@ -70,8 +67,8 @@ describe('Saved query rules, rule edit', { tags: ['@ess', '@serverless'] }, () = const expectedCustomTestQuery = 'random test query'; createSavedQuery(savedQueryName, savedQueryQuery).then((response) => { cy.log(JSON.stringify(response.body, null, 2)); - createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then( - (rule) => visitEditRulePage(rule.body.id) + createRule(getSavedQueryRule({ saved_id: response.body.id, query: undefined })).then((rule) => + visitEditRulePage(rule.body.id) ); });