From 6dd558e59cc85ebf3d49c0278c2372aa978f276b Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 15 Sep 2020 15:30:41 -0500 Subject: [PATCH] [Security Solution][Detections] Integration test for Editing a Rule (#77090) * Add cypress test around editing a detection rule Right now this just navigates around and verifies that the form is correctly repopulated; next step will be to modify/asset some changes. * Add assertions for editing a rule We already were asserting on the population of the Edit form after creation; this additionally makes modifications, saves them, and asserts the resulting values on the Rule Details page. * Remove unused imports * Inline our cypress expectations So that expectation failures are less obfuscated, the decision was previously made to abstract user navigation into functions, but to leave expectations directly within the test body. * Dynamically assert Rule Details based on titles Rule Details are unfortunately unstructured: they're an array of
s and
s without any hierarchy. To address this, tests were previously hardcoding the order of these fields, and assertions were performed by querying for all
s and then indexing with the hardcoded number (e.g. ABOUT_FALSE_POSITIVES). However, in addition to being unstructured, these fields are also _dynamic_, and will be present/absent depending on the data of the given rule. Thus, we started needing multiple orderings for the different combinations of rule fields/rule types. In the absence of refactoring how we build rule details, I'm introducing a simple helper function to fetch the relevant
by the corresponding
s text. This should be more robust to change and more declarative. * Fix bad merge conflict Lots of these variables no longer exist upstream and this new test needed to be refactored. Co-authored-by: Elastic Machine --- .../alerts_detection_rules_custom.spec.ts | 194 ++++++++++++++---- .../alerts_detection_rules_eql.spec.ts | 64 +++--- .../alerts_detection_rules_ml.spec.ts | 81 ++++---- .../alerts_detection_rules_override.spec.ts | 112 +++++----- .../alerts_detection_rules_threshold.spec.ts | 77 +++---- .../security_solution/cypress/objects/rule.ts | 25 +++ .../cypress/screens/alerts_detection_rules.ts | 2 + .../cypress/screens/create_new_rule.ts | 29 +++ .../cypress/screens/edit_rule.ts | 7 + .../cypress/screens/rule_details.ts | 61 ++---- .../cypress/tasks/alerts_detection_rules.ts | 6 + .../cypress/tasks/create_new_rule.ts | 67 +++--- .../cypress/tasks/edit_rule.ts | 12 ++ .../rules/schedule_item_form/index.tsx | 2 + .../detection_engine/rules/all/columns.tsx | 1 + .../detection_engine/rules/edit/index.tsx | 5 + .../es_archives/custom_rules/data.json.gz | Bin 2885 -> 2975 bytes 17 files changed, 443 insertions(+), 302 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/screens/edit_rule.ts create mode 100644 x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index d9d9fde8fc8cc9..17ff1dad799606 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newRule } from '../objects/rule'; +import { newRule, existingRule } from '../objects/rule'; import { CUSTOM_RULES_BTN, @@ -16,26 +16,16 @@ import { SHOWING_RULES_TEXT, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -53,18 +43,38 @@ import { selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, + editFirstRule, } from '../tasks/alerts_detection_rules'; import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, - expectDefineFormToRepopulateAndContinue, - expectAboutFormToRepopulateAndContinue, + goToAboutStepTab, + goToScheduleStepTab, + goToActionsStepTab, + fillAboutRule, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +import { + ACTIONS_THROTTLE_INPUT, + CUSTOM_QUERY_INPUT, + DEFINE_INDEX_INPUT, + RULE_NAME_INPUT, + RULE_DESCRIPTION_INPUT, + TAGS_FIELD, + SEVERITY_DROPDOWN, + RISK_INPUT, + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, + DEFINE_EDIT_BUTTON, + DEFINE_CONTINUE_BUTTON, + ABOUT_EDIT_BUTTON, + ABOUT_CONTINUE_BTN, +} from '../screens/create_new_rule'; +import { saveEditedRule } from '../tasks/edit_rule'; describe('Detection rules, custom', () => { before(() => { @@ -84,8 +94,19 @@ describe('Detection rules, custom', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newRule); fillAboutRuleAndContinue(newRule); - expectDefineFormToRepopulateAndContinue(newRule); - expectAboutFormToRepopulateAndContinue(newRule); + + // expect define step to repopulate + cy.get(DEFINE_EDIT_BUTTON).click(); + cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', newRule.customQuery); + cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); + cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); + + // expect about step to populate + cy.get(ABOUT_EDIT_BUTTON).click(); + cy.get(RULE_NAME_INPUT).invoke('val').should('eq', newRule.name); + cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); + cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); + createAndActivateRule(); cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); @@ -142,32 +163,35 @@ describe('Detection rules, custom', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); @@ -233,4 +257,94 @@ describe('Deletes custom rules', () => { .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); }); }); + + it('Allows a rule to be edited', () => { + editFirstRule(); + + // expect define step to populate + cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', existingRule.customQuery); + if (existingRule.index && existingRule.index.length > 0) { + cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', existingRule.index.join('')); + } + + goToAboutStepTab(); + + // expect about step to populate + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name); + cy.get(RULE_DESCRIPTION_INPUT).invoke('text').should('eql', existingRule.description); + cy.get(TAGS_FIELD).invoke('text').should('eql', existingRule.tags.join('')); + + cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', existingRule.severity); + cy.get(RISK_INPUT).invoke('val').should('eql', existingRule.riskScore); + + goToScheduleStepTab(); + + // expect schedule step to populate + const intervalParts = existingRule.interval && existingRule.interval.match(/[0-9]+|[a-zA-Z]+/g); + if (intervalParts) { + const [amount, unit] = intervalParts; + cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount); + cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit); + } else { + throw new Error('Cannot assert scheduling info on a rule without an interval'); + } + + goToActionsStepTab(); + + cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); + + goToAboutStepTab(); + + const editedRule = { + ...existingRule, + severity: 'Medium', + description: 'Edited Rule description', + }; + + fillAboutRule(editedRule); + saveEditedRule(); + + const expectedTags = editedRule.tags.join(''); + const expectedIndexPatterns = + editedRule.index && editedRule.index.length + ? editedRule.index + : [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ]; + + cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${editedRule.name} Beta`); + + cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', editedRule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', editedRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', editedRule.riskScore); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); + + cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', editedRule.note); + + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${editedRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + if (editedRule.interval) { + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', editedRule.interval); + }); + } + }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index c65cd8406099a9..76871929fe0502 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -15,26 +15,16 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, + ABOUT_DETAILS, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, + DEFINITION_DETAILS, + getDescriptionForTitle, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -136,32 +126,34 @@ describe('Detection rules, EQL', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${eqlRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', eqlRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', eqlRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', eqlRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', eqlRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', eqlRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${eqlRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Event Correlation'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${eqlRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(2).invoke('text').should('eql', 'Event Correlation'); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index b6b30ef550eb18..47e49d48e2aec2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -16,24 +16,14 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - ANOMALY_SCORE, - DEFINITION_TIMELINE, - DEFINITION_STEP, MACHINE_LEARNING_JOB_ID, MACHINE_LEARNING_JOB_STATUS, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, - RULE_TYPE, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -126,36 +116,37 @@ describe('Detection rules, machine learning', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${machineLearningRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', machineLearningRule.description); - cy.get(ABOUT_STEP) - .eq(ABOUT_SEVERITY) - .invoke('text') - .should('eql', machineLearningRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', machineLearningRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); - - cy.get(DEFINITION_STEP).eq(RULE_TYPE).invoke('text').should('eql', 'Machine Learning'); - cy.get(DEFINITION_STEP) - .eq(ANOMALY_SCORE) - .invoke('text') - .should('eql', machineLearningRule.anomalyScoreThreshold); - cy.get(DEFINITION_STEP) - .get(MACHINE_LEARNING_JOB_STATUS) - .invoke('text') - .should('eql', 'Stopped'); - cy.get(DEFINITION_STEP) - .get(MACHINE_LEARNING_JOB_ID) - .invoke('text') - .should('eql', machineLearningRule.machineLearningJob); - - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', machineLearningRule.severity); + getDescriptionForTitle('Risk score') + .invoke('text') + .should('eql', machineLearningRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); + + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Anomaly score') + .invoke('text') + .should('eql', machineLearningRule.anomalyScoreThreshold); + getDescriptionForTitle('Anomaly score') + .invoke('text') + .should('eql', machineLearningRule.anomalyScoreThreshold); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Machine Learning'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + cy.get(MACHINE_LEARNING_JOB_STATUS).invoke('text').should('eql', 'Stopped'); + cy.get(MACHINE_LEARNING_JOB_ID) + .invoke('text') + .should('eql', machineLearningRule.machineLearningJob); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index e3526c63e2310d..4edf5e18660875 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -16,29 +16,17 @@ import { } from '../screens/alerts_detection_rules'; import { ABOUT_INVESTIGATION_NOTES, - ABOUT_OVERRIDE_FALSE_POSITIVES, - ABOUT_OVERRIDE_MITRE, - ABOUT_OVERRIDE_NAME_OVERRIDE, - ABOUT_OVERRIDE_RISK, - ABOUT_OVERRIDE_RISK_OVERRIDE, - ABOUT_OVERRIDE_SEVERITY_OVERRIDE, - ABOUT_OVERRIDE_TAGS, - ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE, - ABOUT_OVERRIDE_URLS, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + ABOUT_DETAILS, + getDescriptionForTitle, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, + DETAILS_TITLE, + DETAILS_DESCRIPTION, } from '../screens/rule_details'; import { @@ -141,56 +129,56 @@ describe('Detection rules, override', () => { const expectedOverrideSeverities = ['Low', 'Medium', 'High', 'Critical']; - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newOverrideRule.severity); - newOverrideRule.severityOverride.forEach((severity, i) => { - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_SEVERITY_OVERRIDE + i) + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newOverrideRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newOverrideRule.riskScore); + getDescriptionForTitle('Risk score override') .invoke('text') - .should( - 'eql', - `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}` - ); + .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`); + getDescriptionForTitle('Rule name override') + .invoke('text') + .should('eql', newOverrideRule.nameOverride); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDescriptionForTitle('Timestamp override') + .invoke('text') + .should('eql', newOverrideRule.timestampOverride); + cy.contains(DETAILS_TITLE, 'Severity override') + .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings + .then((severityOverrideIndex) => { + newOverrideRule.severityOverride.forEach((severity, i) => { + cy.get(DETAILS_DESCRIPTION) + .eq(severityOverrideIndex + i) + .invoke('text') + .should( + 'eql', + `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}` + ); + }); + }); }); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_RISK) - .invoke('text') - .should('eql', newOverrideRule.riskScore); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_RISK_OVERRIDE) - .invoke('text') - .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_NAME_OVERRIDE) - .invoke('text') - .should('eql', newOverrideRule.nameOverride); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP) - .eq(ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE) - .invoke('text') - .should('eql', newOverrideRule.timestampOverride); - cy.get(ABOUT_STEP).eq(ABOUT_OVERRIDE_TAGS).invoke('text').should('eql', expectedTags); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newOverrideRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newOverrideRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 10f9ebb5623df5..00175ed3baeb85 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -15,27 +15,16 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { - ABOUT_FALSE_POSITIVES, ABOUT_INVESTIGATION_NOTES, - ABOUT_MITRE, - ABOUT_RISK, ABOUT_RULE_DESCRIPTION, - ABOUT_SEVERITY, - ABOUT_STEP, - ABOUT_TAGS, - ABOUT_URLS, - DEFINITION_CUSTOM_QUERY, - DEFINITION_INDEX_PATTERNS, - DEFINITION_THRESHOLD, - DEFINITION_TIMELINE, - DEFINITION_STEP, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - SCHEDULE_LOOPBACK, - SCHEDULE_RUNS, - SCHEDULE_STEP, + getDescriptionForTitle, + ABOUT_DETAILS, + DEFINITION_DETAILS, + SCHEDULE_DETAILS, } from '../screens/rule_details'; import { @@ -137,38 +126,40 @@ describe('Detection rules, threshold', () => { cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newThresholdRule.name} Beta`); cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newThresholdRule.description); - cy.get(ABOUT_STEP).eq(ABOUT_SEVERITY).invoke('text').should('eql', newThresholdRule.severity); - cy.get(ABOUT_STEP).eq(ABOUT_RISK).invoke('text').should('eql', newThresholdRule.riskScore); - cy.get(ABOUT_STEP).eq(ABOUT_URLS).invoke('text').should('eql', expectedUrls); - cy.get(ABOUT_STEP) - .eq(ABOUT_FALSE_POSITIVES) - .invoke('text') - .should('eql', expectedFalsePositives); - cy.get(ABOUT_STEP).eq(ABOUT_MITRE).invoke('text').should('eql', expectedMitre); - cy.get(ABOUT_STEP).eq(ABOUT_TAGS).invoke('text').should('eql', expectedTags); + cy.get(ABOUT_DETAILS).within(() => { + getDescriptionForTitle('Severity').invoke('text').should('eql', newThresholdRule.severity); + getDescriptionForTitle('Risk score').invoke('text').should('eql', newThresholdRule.riskScore); + getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); + getDescriptionForTitle('False positive examples') + .invoke('text') + .should('eql', expectedFalsePositives); + getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); + getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + }); cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - cy.get(DEFINITION_INDEX_PATTERNS).then((patterns) => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern).invoke('text').should('eql', expectedIndexPatterns[index]); - }); + cy.get(DEFINITION_DETAILS).within(() => { + getDescriptionForTitle('Index patterns') + .invoke('text') + .should('eql', expectedIndexPatterns.join('')); + getDescriptionForTitle('Custom query') + .invoke('text') + .should('eql', `${newThresholdRule.customQuery} `); + getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Threshold'); + getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + getDescriptionForTitle('Threshold') + .invoke('text') + .should( + 'eql', + `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` + ); + }); + + cy.get(SCHEDULE_DETAILS).within(() => { + getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); + getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); }); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_CUSTOM_QUERY) - .invoke('text') - .should('eql', `${newThresholdRule.customQuery} `); - cy.get(DEFINITION_STEP).eq(DEFINITION_TIMELINE).invoke('text').should('eql', 'None'); - cy.get(DEFINITION_STEP) - .eq(DEFINITION_THRESHOLD) - .invoke('text') - .should( - 'eql', - `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` - ); - - cy.get(SCHEDULE_STEP).eq(SCHEDULE_RUNS).invoke('text').should('eql', '5m'); - cy.get(SCHEDULE_STEP).eq(SCHEDULE_LOOPBACK).invoke('text').should('eql', '1m'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 0624606fe8481a..2a5c60815f450d 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -27,6 +27,8 @@ export interface CustomRule { customQuery: string; name: string; description: string; + index?: string[]; + interval?: string; severity: string; riskScore: string; tags: string[]; @@ -109,6 +111,29 @@ export const newRule: CustomRule = { timelineId: '0162c130-78be-11ea-9718-118a926974a4', }; +export const existingRule: CustomRule = { + customQuery: 'host.name:*', + name: 'Rule 1', + description: 'Description for Rule 1', + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + interval: '4m', + severity: 'High', + riskScore: '19', + tags: ['rule1'], + referenceUrls: [], + falsePositivesExamples: [], + mitre: [], + note: 'This is my note', + timelineId: '', +}; + export const newOverrideRule: OverrideRule = { customQuery: 'host.name:*', name: 'New Rule Test', diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index a41b8296f83e4d..14f5383939a948 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -14,6 +14,8 @@ export const CUSTOM_RULES_BTN = '[data-test-subj="show-custom-rules-filter-butto export const DELETE_RULE_ACTION_BTN = '[data-test-subj="deleteRuleAction"]'; +export const EDIT_RULE_ACTION_BTN = '[data-test-subj="editRuleAction"]'; + export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]'; export const ELASTIC_RULES_BTN = '[data-test-subj="show-elastic-rules-filter-button"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 1c25ed88c3beee..dda371126d5aa6 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -8,6 +8,13 @@ export const ABOUT_CONTINUE_BTN = '[data-test-subj="about-continue"]'; export const ABOUT_EDIT_BUTTON = '[data-test-subj="edit-about-rule"]'; +export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]'; + +export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]'; + +export const ACTIONS_THROTTLE_INPUT = + '[data-test-subj="stepRuleActions"] [data-test-subj="select"]'; + export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; @@ -30,6 +37,11 @@ export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]'; export const DEFINE_EDIT_BUTTON = '[data-test-subj="edit-define-rule"]'; +export const DEFINE_EDIT_TAB = '[data-test-subj="edit-rule-define-tab"]'; + +export const DEFINE_INDEX_INPUT = + '[data-test-subj="detectionEngineStepDefineRuleIndices"] [data-test-subj="input"]'; + export const EQL_TYPE = '[data-test-subj="eqlRuleType"]'; export const EQL_QUERY_INPUT = '[data-test-subj="eqlQueryBarTextInput"]'; @@ -81,6 +93,20 @@ export const RULE_TIMESTAMP_OVERRIDE = export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]'; +export const SCHEDULE_EDIT_TAB = '[data-test-subj="edit-rule-schedule-tab"]'; + +export const SCHEDULE_INTERVAL_AMOUNT_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-amount-input"]'; + +export const SCHEDULE_INTERVAL_UNITS_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-units-input"]'; + +export const SCHEDULE_LOOKBACK_AMOUNT_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-amount-input"]'; + +export const SCHEDULE_LOOKBACK_UNITS_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-units-input"]'; + export const SEVERITY_DROPDOWN = '[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]'; @@ -88,6 +114,9 @@ export const SEVERITY_MAPPING_OVERRIDE_OPTION = '#severity-mapping-override'; export const SEVERITY_OVERRIDE_ROW = '[data-test-subj="severityOverrideRow"]'; +export const TAGS_FIELD = + '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxInput"]'; + export const TAGS_INPUT = '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts new file mode 100644 index 00000000000000..1bf0ff34ebd941 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const EDIT_SUBMIT_BUTTON = '[data-test-subj="ruleEditSubmitButton"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index b221709966943f..98fc7b06a99083 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -4,55 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ABOUT_FALSE_POSITIVES = 3; +export const getDescriptionForTitle = (title: string) => + cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); -export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; - -export const ABOUT_MITRE = 4; - -export const ABOUT_OVERRIDE_FALSE_POSITIVES = 8; - -export const ABOUT_OVERRIDE_MITRE = 10; - -export const ABOUT_OVERRIDE_NAME_OVERRIDE = 9; - -export const ABOUT_OVERRIDE_RISK = 5; +export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; +export const DETAILS_TITLE = '.euiDescriptionList__title'; -export const ABOUT_OVERRIDE_RISK_OVERRIDE = 6; - -export const ABOUT_OVERRIDE_SEVERITY_OVERRIDE = 1; - -export const ABOUT_OVERRIDE_TAGS = 12; - -export const ABOUT_OVERRIDE_TIMESTAMP_OVERRIDE = 11; - -export const ABOUT_OVERRIDE_URLS = 7; +export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; -export const ABOUT_RISK = 1; +export const ABOUT_DETAILS = + '[data-test-subj="aboutRule"] [data-test-subj="listItemColumnStepRuleDescription"]'; -export const ABOUT_SEVERITY = 0; - -export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; - -export const ABOUT_TAGS = 5; - -export const ABOUT_URLS = 2; - -export const ANOMALY_SCORE = 1; - -export const DEFINITION_CUSTOM_QUERY = 1; - -export const DEFINITION_THRESHOLD = 4; - -export const DEFINITION_TIMELINE = 3; - -export const DEFINITION_INDEX_PATTERNS = - '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text'; - -export const DEFINITION_STEP = - '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description'; +export const DEFINITION_DETAILS = + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; @@ -60,16 +26,13 @@ export const INVESTIGATION_NOTES_TOGGLE = 1; export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]'; -export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus" ]'; +export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus"]'; export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]'; export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; -export const RULE_TYPE = 0; +export const SCHEDULE_DETAILS = + '[data-test-subj=schedule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; - -export const SCHEDULE_RUNS = 0; - -export const SCHEDULE_LOOPBACK = 1; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 5ec5bb97250db2..c530594508f953 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -24,6 +24,7 @@ import { SORT_RULES_BTN, THREE_HUNDRED_ROWS, EXPORT_ACTION_BTN, + EDIT_RULE_ACTION_BTN, } from '../screens/alerts_detection_rules'; export const activateRule = (rulePosition: number) => { @@ -35,6 +36,11 @@ export const changeToThreeHundredRowsPerPage = () => { cy.get(THREE_HUNDRED_ROWS).click(); }; +export const editFirstRule = () => { + cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true }); + cy.get(EDIT_RULE_ACTION_BTN).click(); +}; + export const deleteFirstRule = () => { cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true }); cy.get(DELETE_RULE_ACTION_BTN).click(); diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index f26a77171462c6..0daff52de70633 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -13,14 +13,17 @@ import { } from '../objects/rule'; import { ABOUT_CONTINUE_BTN, - ANOMALY_THRESHOLD_INPUT, + ABOUT_EDIT_TAB, + ACTIONS_EDIT_TAB, ADD_FALSE_POSITIVE_BTN, ADD_REFERENCE_URL_BTN, ADVANCED_SETTINGS_BTN, + ANOMALY_THRESHOLD_INPUT, COMBO_BOX_INPUT, CREATE_AND_ACTIVATE_BTN, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, + DEFINE_EDIT_TAB, FALSE_POSITIVES_INPUT, IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, @@ -32,8 +35,8 @@ import { MITRE_TACTIC, MITRE_TACTIC_DROPDOWN, MITRE_TECHNIQUES_INPUT, - RISK_INPUT, REFERENCE_URLS_INPUT, + RISK_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, @@ -41,6 +44,7 @@ import { RULE_NAME_OVERRIDE, RULE_TIMESTAMP_OVERRIDE, SCHEDULE_CONTINUE_BUTTON, + SCHEDULE_EDIT_TAB, SEVERITY_DROPDOWN, SEVERITY_MAPPING_OVERRIDE_OPTION, SEVERITY_OVERRIDE_ROW, @@ -48,8 +52,6 @@ import { THRESHOLD_FIELD_SELECTION, THRESHOLD_INPUT_AREA, THRESHOLD_TYPE, - DEFINE_EDIT_BUTTON, - ABOUT_EDIT_BUTTON, EQL_TYPE, EQL_QUERY_INPUT, } from '../screens/create_new_rule'; @@ -61,11 +63,9 @@ export const createAndActivateRule = () => { cy.get(CREATE_AND_ACTIVATE_BTN).should('not.exist'); }; -export const fillAboutRuleAndContinue = ( - rule: CustomRule | MachineLearningRule | ThresholdRule -) => { - cy.get(RULE_NAME_INPUT).type(rule.name, { force: true }); - cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true }); +export const fillAboutRule = (rule: CustomRule | MachineLearningRule | ThresholdRule) => { + cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true }); + cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true }); cy.get(SEVERITY_DROPDOWN).click({ force: true }); cy.get(`#${rule.severity.toLowerCase()}`).click(); @@ -79,12 +79,15 @@ export const fillAboutRuleAndContinue = ( cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); rule.referenceUrls.forEach((url, index) => { - cy.get(REFERENCE_URLS_INPUT).eq(index).type(url, { force: true }); + cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); }); rule.falsePositivesExamples.forEach((falsePositive, index) => { - cy.get(FALSE_POSITIVES_INPUT).eq(index).type(falsePositive, { force: true }); + cy.get(FALSE_POSITIVES_INPUT) + .eq(index) + .clear({ force: true }) + .type(falsePositive, { force: true }); cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); }); @@ -93,14 +96,22 @@ export const fillAboutRuleAndContinue = ( cy.contains(MITRE_TACTIC, mitre.tactic).click(); mitre.techniques.forEach((technique) => { - cy.get(MITRE_TECHNIQUES_INPUT).eq(index).type(`${technique}{enter}`, { force: true }); + cy.get(MITRE_TECHNIQUES_INPUT) + .eq(index) + .clear({ force: true }) + .type(`${technique}{enter}`, { force: true }); }); cy.get(MITRE_BTN).click({ force: true }); }); - cy.get(INVESTIGATION_NOTES_TEXTAREA).type(rule.note, { force: true }); + cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(rule.note, { force: true }); +}; +export const fillAboutRuleAndContinue = ( + rule: CustomRule | MachineLearningRule | ThresholdRule +) => { + fillAboutRule(rule); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); }; @@ -179,20 +190,6 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; -export const expectDefineFormToRepopulateAndContinue = (rule: CustomRule) => { - cy.get(DEFINE_EDIT_BUTTON).click(); - cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', rule.customQuery); - cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); - cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); -}; - -export const expectAboutFormToRepopulateAndContinue = (rule: CustomRule) => { - cy.get(ABOUT_EDIT_BUTTON).click(); - cy.get(RULE_NAME_INPUT).invoke('val').should('eq', rule.name); - cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); - cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); -}; - export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; @@ -230,6 +227,22 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu cy.get(MACHINE_LEARNING_DROPDOWN).should('not.exist'); }; +export const goToDefineStepTab = () => { + cy.get(DEFINE_EDIT_TAB).click({ force: true }); +}; + +export const goToAboutStepTab = () => { + cy.get(ABOUT_EDIT_TAB).click({ force: true }); +}; + +export const goToScheduleStepTab = () => { + cy.get(SCHEDULE_EDIT_TAB).click({ force: true }); +}; + +export const goToActionsStepTab = () => { + cy.get(ACTIONS_EDIT_TAB).click({ force: true }); +}; + export const selectMachineLearningRuleType = () => { cy.get(MACHINE_LEARNING_TYPE).click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts new file mode 100644 index 00000000000000..690a36058ec333 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; + +export const saveEditedRule = () => { + cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); + cy.get(EDIT_SUBMIT_BUTTON).should('not.exist'); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx index bb33767f4f5d51..867be1c1270e82 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/schedule_item_form/index.tsx @@ -145,6 +145,7 @@ export const ScheduleItem = ({ void ) => [ { + 'data-test-subj': 'editRuleAction', description: i18n.EDIT_RULE_SETTINGS, icon: 'controlsHorizontal', name: i18n.EDIT_RULE_SETTINGS, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx index 5f4fd596691034..e2772af72da06b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx @@ -118,6 +118,7 @@ const EditRulePageComponent: FC = () => { const tabs = useMemo( () => [ { + 'data-test-subj': 'edit-rule-define-tab', id: RuleStep.defineRule, name: ruleI18n.DEFINITION, disabled: rule?.immutable, @@ -140,6 +141,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-about-tab', id: RuleStep.aboutRule, name: ruleI18n.ABOUT, disabled: rule?.immutable, @@ -163,6 +165,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-schedule-tab', id: RuleStep.scheduleRule, name: ruleI18n.SCHEDULE, disabled: rule?.immutable, @@ -185,6 +188,7 @@ const EditRulePageComponent: FC = () => { ), }, { + 'data-test-subj': 'edit-rule-actions-tab', id: RuleStep.ruleActions, name: ruleI18n.ACTIONS, content: ( @@ -387,6 +391,7 @@ const EditRulePageComponent: FC = () => { QOZ*BnXTWNFZHW&VWe+6$owN33eK|XhY zJ^kRgT(z@ zOy+S{c4TSDMK|ECCh#I(e~^${kDe-si}cX)+<+GTeqZ>zjfcT=l=uKq1KkJXD^X9T+%_`U3`4KLSkNfhWX7L5#x^fLIbh$i( zSLf1ULk&(w^=ncOFN~qvb>XlvTU=aUU0MMcct+b*Oli?1z9F>dliB4_wdMB?-RkAV zkvH>-Q*c$COR(ma#p;FW*rUP(Ra|mp4~r7qy|oSNrkR+QB19UTgbM}!dF31YA9J=&aUE4}YktjWS zg15u`!h}9@gB4GH$qX8|h_DrE6eeb?in!RMO5CoXiy^zajFCPfj|?}nT-!6(b&o2+ zMy?qlGl6at5r2`@f`jIrHF@(nz;W415~2-QQ)&gy=8VFj?}cHaQL6NtJ5Q2o^jBtl z*9()j-Juoa=*L;)lA=r_FAB#|cz*BHViGZHhr?iEpvVrhJtoOAY1^R}glufL`K<*yOo?xWtJz!TFx!#pm!kdTltarQw&jxD!{60*trbe!4ZJmBHUzJ? zs>!wi(fI}|ii7*&6v_vIJ|sA@$)>KpRuA{IV53X0z{`waG|Ml(LpZfayY8tGH@50KcN`8$ZoP_J z((8X|W??Hxf{jz^k|LixA2^iO3fY?QomN*fd#m9ovEhAy#i<-816AckC__$F!5gjX zc|KAy3Q`_Otg6B{@Xh>&kQz7vsq2Nv-c=cpFtl8AD`XFb-i(6Qvw{fO z)-rKHi8k&+!Iucnf_frk&bm#Yi;s0~zGy<@5v>?~$u#@M z?4EkQUvEmq_eB|+>uaOhc7o4f0s#wn^$nOHsz*ytEb}G`C>A%V!x*yjM3I3ZA+Se$ z__9RF({eYw;CQ@>R)-YK^qxtn9{a|I+o30UlF**zpePFIKQX;`Mvg}OhEb%#N68Uw zgLw4Z?u?&T^=rb$XIA_Y$c4Ai@HxmazzXJ!LWi1-uw7;ii;qn z|K0;}G0#I*k>r0H@r`-B3i0Gp!w>)zB|0`hoWwwEsEnc;kUnC-Q~l{04?pXr#%;A! zwSv;{qSLdWqXc24doj7bJtMb+u2#0qnRzALO7qEK&zPfAL%AHDbmp>;>)hFyI5fOP z!|F{21FteO&YLwuRB^R2)l6GFop^&YFE|bh;^jiEAIZg|u;rKOuwkj$yjmFEtc^jT zcYRssJCbOSuDdh~ZS8n)>OdeA%csK05ig0u(!`rcUMaX-I6^II8s%lZQ}Ao;?s5zi zl}^l?G|hffcG~TK>QaM#c$Jk^{&QYx@Grj9*y@)1=u3=uy2eOdQamd8JGeR6%3r_X zvVhujh%9RJtWOU&)DP$KxnFW`zBdrFig~yEdT_yj00D4B zM_N|;p7#d!R`&h}y*2>caPNBqPSgwp#UzG6ue>+daY^uU5WoLR0{Rg+R^X*K1ON#y zF$qaRbcVPlH7_RCDnp%i7zKGw76Ih&K7hSvF67yXZNwF^+Ey0!f@mh`szvswm~4Gr zO0QD1%J@INRhdaPb;AZ?)K8IRM?Oh5+{deHBnVc{d`FuL&l?qxJ}RLdZZ&DqiFB&6 z;E`Qj5Lf|%%&wm5^dR~mh)W12>6%~1j z`z`L=Uran}6nhgV7WJ{j!J?q57{2>y>BETgg5Ng%Sj6GgJOC`*_>Iqp5C^o zk_?;kctetbAXTEl3Zn}G$3THl$D#tr5JiWgfFS^q@L7`#C%)6+7oBRW&iDD!>8Lj; zjc)_(n)J2hq=p7PrG3=AnqjwmDs{a{K`<{G9^n=(F)*9u#;`I}PtN)kQ|!pSqNI@H zX^$({@ytWlV+Yx?!S%0n(!HGa&66PD4vW=}5wxLGghs`gmu6F?fosFgNUD_lS>!dP z7PipQKpc*WT_EGyVyGVa$CYK-U5xqpRampd`gnOXy_9+N+zhVz;t+|I0;iuW-DviY zBpIYU2lG&2RppcZa+1&eUW!%>A%A=&9MXNi5)L1lZ~&a4D5^ovss^NSmZ5O;tf~`+ z23Qh3YiZO$Y*^M;!r}D^2Swxz0F^kNkhN7jfkX%xq6!=jp&&w-iE8&uIJ_Lh?>*tL z^5`ndi*GqMt%3nj5e$k7)tNys1Bs|G2uhfdGz5si8HNtj^9PGg+9g2Z{n94|aa(w1YW9}*ys+OBR4f{tr1N4>gKQ;uhi`s@lg2n19@Z36tz4m~1Y3r$K>xNg7inzX=Yjlld0 z29oz5-X08my+U!fnE&!C6z>_AyysgJj|L<3u+8(T1Q=crHF_-5Wd;!h8A;(WJ#Qmf zSER3CGs`AlkT_Ca|c;~!sX8K#?dDpz|ohX5O%j*sMF=u`C`+YFy1u4rf=DdzM VkQLM)fjOId&foFMcHvn-004vgz_(-RVC{TO3U06) zCnf*z2ma`gq!zn0jPl4JreYE(n1m<-(k1?lN~&O*8o)YK2qk7ZNYc+IbeeP(hmMJT zdxuR7uRw} zjw@AEKJ4zgv-VjpY^q+B&ByKLr1|Hc_g8s3@oAVWmAaTkj+Jd;ab3pVqB_ylN?k~C zM9F@Fz>WkYAOKmA)D8rBVB~=&Dynh0p+jM|MHuz5>Cyz3D2!>Q!iyui@4CFg<~Xg_ zkYk@k*ta;O+i~}?mzrxUc4-b~rH*ijJ+@*+yp&L8h0Z83rj6ZrlXNbPfH{$KLsuoQ z$WEF%ZbZXJ+Ju3Zi1LGpAgh3_3UfTZ)I>e^m5?D#cJvfqPxwQKLhQ2zFaB5=G-(kN zCo(xK!o4c$lRj74{R+Mqy1B~;TSK~KxQXMtL4R4dRS7Qk`!VjPPz_@m&Q`VH;c0tG z)_e{Euv$qYyar=NtWIWmpDt&HynQPS3rU3aBCh@(*)9pCw_ zgeSt%rwD}FICi7eWsK96@wS72MPg!_`Bi=Ggw&=Xi7*@DFI%LZI`m^GjzcImnUaD2{K zzNaO_D&Ee6eMtwi;X>I2nNYzR5L6o(0)!L`x}quk8zdSpM+Vfg-Y3HSb%;~w)_l#? zRXQJTQdddsl|%B+hcz?Eojb{=&SYn!&&&{H6^Na9MDBa?FKY_gY{Yj0?m%1Y;&;W% zv9Iu!eMJ|61TPoTzI{plzkT`g&(lK^zNOhtKd*{F1Oj$P0}Njhdn|@o4L;1+aY;Q+ z{Ed!OEu*kxNryq`;V2)?6A5RX;)@lw6#ZfiFz%;me$_KQYwe!X4{_oxyLaTuXdxXw z8A%nnlApcG+wPeW*EZwMz+pN5i3T*cTtzPJ^`9)Wh{Muw;*h(fIHZq<2BoD!?oIes ztBaYv*6{Si@ZQ1V3;;A}7?OfC1PlYd(z+h!BPXM*=AkMY26_b_Jv?GUL|w}F?{Jo1 zeSDF1u8y#JJcy#uF?rC)c>G<%qkCZ=j&M62krjj3kM744_$Gk>ti=*MFxgbby$pDXP&yCR2#QJ@y2;2K}8Xd5+^`5ZYfX_ zfetQT?2k8^?BhGxirAMhCw7EHqGRP3q!408!z&&=`%%6Y%pKRodx{8hztynCcm9A4 z{+m1I5rCWH1-a99?t;hAR}fvPzvuQxBbT02v+6{9imZvUn)|Iy#aLS*FH*v6r=?Di7IK2gti>&>Y6u4skk*3zi%JHbaVK~RK} z@d`{Z*P~;lmU$I3j>T2x_=Vj()ir3V7(O9Bd|oQ#VYzEwa5P#(s{;;ZcF*KgPkiI4 z-{xm^n$lhbNKxeMpOW1>W6z}Fsa@p4hv}hgjd=WI?u{N-^-IFWM^^kC$Yt=3kP}FB z0_^lipnU+yp&~+lfY84q{t)2c34n7gD=3eU$d4ueCg7QQ$nVc_@N;l{J#BX%a3qVm ztN{JZ@cus|II>Nd`$+I{d^*QPRFkf^KLzm53XJYIx&dTn9L5Gg7j8)Bde zh}edpTQ=g41WH_gmMNj1_1faPTBMw{obL6pj<2;$_IOrstiiw zV5|lub~f|mTHLV9^Lo1w)>@tU2ALyV8bQB^FjRhmjwJH15uXLR|Ehl zFA0rkN_2txeQsVtqE&%A?I>n>peYam@)W?{G8gjb#Ma`9L~RQTdqy;~Of9>^V!HKp zIK4{oBICb(tLmrO)HNGOP(Q?u8;3OAaJN_2C}Rs}zM;)Y5DW|08kTUhrA0foxXRd; zU0s$%8NroZJ=5vC=pB}nP(rgeXWV(=&>HO9oEFzhfqXozv6ip;=s2jPG7X(&4vsD=eWk5+i>gldZ@QpdCzAkQjy&vY zHn=#qXy@$aqJO{`*ezDuHftfTi0q0tElqCpQ&JnWhiau1PU4`UHi?7xddgr}>_Cmw zW&>k4+^@{b{%j;o&!d{F)JOBZn=?%^j{EHVLK$GCQUKP$+>a+eB*~!W0m>s?H1wbJ zw~tsr;qaMo_+beL2y9(9Y<^a?5s$NM9q_ZNMFk#Usr;;Eat9%OCLG=> z;h-zPhDcB13C&!@6Iek|poR=2gk%LF^92cq=Y#ms6AlZHZiteizB(9CblK*paGlwd z2#6}WfRRcB)x?m>z_u+&e%4@sA;YpKWI9!Cvp{FE;g+J=G;B5-bspo?b*xM){n0G& zRC7K;)-HwGu;p8JkF|YwKJ3=jn!Z0dtxwLOhvC3b#?|6c>!zknJwIr)3%z!?L?on2 z<=NbAgZ*YhnaSm9Tj|MIi+n6QOmwIts7by+QkYc2VlPk*Ute(JAYH1_S9E53dgfK3|&nd}-nXzBIuP z+md9cP>^KB