diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index 7f02d41ad1b0c..db841d2a732c4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -158,6 +158,7 @@ describe('Alerts', () => { }); }); }); + context('Opening alerts', () => { beforeEach(() => { esArchiverLoad('closed_alerts'); @@ -204,6 +205,7 @@ describe('Alerts', () => { }); }); }); + context('Marking alerts as in-progress', () => { beforeEach(() => { esArchiverLoad('alerts'); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts index 20cf624b3360d..3fa304ab7cf19 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts @@ -43,6 +43,7 @@ describe('Alerts detection rules', () => { waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + cy.get(RULE_NAME) .eq(FIFTH_RULE) .invoke('text') @@ -56,7 +57,6 @@ describe('Alerts detection rules', () => { activateRule(SEVENTH_RULE); waitForRuleToBeActivated(); sortByActivatedRules(); - cy.get(RULE_NAME) .eq(FIRST_RULE) .invoke('text') @@ -70,7 +70,6 @@ describe('Alerts detection rules', () => { cy.wrap(expectedRulesNames).should('include', seventhRuleName); }); }); - cy.get(RULE_SWITCH).eq(FIRST_RULE).should('have.attr', 'role', 'switch'); cy.get(RULE_SWITCH).eq(SECOND_RULE).should('have.attr', 'role', 'switch'); }); 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 17ff1dad79960..f999c5cecc392 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,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newRule, existingRule } from '../objects/rule'; +import { newRule, existingRule, indexPatterns, editedRule } from '../objects/rule'; +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, @@ -12,20 +20,49 @@ import { RULE_NAME, RULES_ROW, RULES_TABLE, + RULE_SWITCH, SEVERITY, SHOWING_RULES_TEXT, } from '../screens/alerts_detection_rules'; import { + ABOUT_CONTINUE_BTN, + ABOUT_EDIT_BUTTON, + ACTIONS_THROTTLE_INPUT, + CUSTOM_QUERY_INPUT, + DEFINE_CONTINUE_BUTTON, + DEFINE_EDIT_BUTTON, + DEFINE_INDEX_INPUT, + RISK_INPUT, + RULE_DESCRIPTION_INPUT, + RULE_NAME_INPUT, + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, + SEVERITY_DROPDOWN, + TAGS_FIELD, +} 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, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -37,46 +74,46 @@ import { changeToThreeHundredRowsPerPage, deleteFirstRule, deleteSelectedRules, + editFirstRule, filterByCustomRules, goToCreateNewRule, goToRuleDetails, selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, - editFirstRule, } from '../tasks/alerts_detection_rules'; import { createAndActivateRule, + fillAboutRule, fillAboutRuleAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, + fillScheduleRuleAndContinue, goToAboutStepTab, - goToScheduleStepTab, goToActionsStepTab, - fillAboutRule, + goToScheduleStepTab, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; +import { saveEditedRule } from '../tasks/edit_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; 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', () => { +const expectedUrls = newRule.referenceUrls.join(''); +const expectedFalsePositives = newRule.falsePositivesExamples.join(''); +const expectedTags = newRule.tags.join(''); +const expectedMitre = newRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = 1; +const expectedEditedtags = editedRule.tags.join(''); +const expectedEditedIndexPatterns = + editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns; + +describe('Custom detection rules creation', () => { before(() => { esArchiverLoad('timeline'); }); @@ -85,7 +122,7 @@ describe('Detection rules, custom', () => { esArchiverUnload('timeline'); }); - it('Creates and activates a new custom rule', () => { + it('Creates and activates a new rule', () => { loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); @@ -94,27 +131,27 @@ describe('Detection rules, custom', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newRule); fillAboutRuleAndContinue(newRule); + fillScheduleRuleAndContinue(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(CUSTOM_QUERY_INPUT).should('have.text', 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(RULE_NAME_INPUT).invoke('val').should('eql', 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)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -124,78 +161,59 @@ describe('Detection rules, custom', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newRule.name); + cy.get(RISK_SCORE).should('have.text', newRule.riskScore); + cy.get(SEVERITY).should('have.text', newRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - 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(RULE_NAME_HEADER).should('have.text', `${newRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newRule.description); 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); + getDetails(SEVERITY_DETAILS).should('have.text', newRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', 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(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); 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'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newRule.runsEvery.interval}${newRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newRule.lookBack.interval}${newRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.above', 0); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', newRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'query'); + cy.get(ALERT_RULE_SEVERITY).first().should('have.text', newRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newRule.riskScore); }); }); -describe('Deletes custom rules', () => { +describe('Custom detection rules deletion and edition', () => { beforeEach(() => { esArchiverLoad('custom_rules'); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); @@ -208,143 +226,132 @@ describe('Deletes custom rules', () => { esArchiverUnload('custom_rules'); }); - it('Deletes one rule', () => { - cy.get(RULES_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; - - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${initialNumberOfRules} rules`); - - deleteFirstRule(); - waitForRulesToBeLoaded(); - - cy.get(RULES_TABLE).then(($table) => { - cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); - }); - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rules`); - cy.get(CUSTOM_RULES_BTN) - .invoke('text') - .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); - }); - }); - - it('Deletes more than one rule', () => { - cy.get(RULES_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const numberOfRulesToBeDeleted = 3; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted; - - selectNumberOfRules(numberOfRulesToBeDeleted); - deleteSelectedRules(); - waitForRulesToBeLoaded(); - - cy.get(RULES_TABLE).then(($table) => { - cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); + context('Deletion', () => { + it('Deletes one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then((rules) => { + const initialNumberOfRules = rules.length; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; + + cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${initialNumberOfRules} rules`); + + deleteFirstRule(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should( + 'eql', + expectedNumberOfRulesAfterDeletion + ); + }); + cy.get(SHOWING_RULES_TEXT).should( + 'have.text', + `Showing ${expectedNumberOfRulesAfterDeletion} rules` + ); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); }); - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rule`); - cy.get(CUSTOM_RULES_BTN) - .invoke('text') - .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'); + it('Deletes more than one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then((rules) => { + const initialNumberOfRules = rules.length; + const numberOfRulesToBeDeleted = 3; + const expectedNumberOfRulesAfterDeletion = + initialNumberOfRules - numberOfRulesToBeDeleted; + + selectNumberOfRules(numberOfRulesToBeDeleted); + deleteSelectedRules(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should( + 'eql', + expectedNumberOfRulesAfterDeletion + ); + }); + cy.get(SHOWING_RULES_TEXT).should( + 'have.text', + `Showing ${expectedNumberOfRulesAfterDeletion} rule` + ); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); + }); }); + }); - if (editedRule.interval) { - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', editedRule.interval); + context('Edition', () => { + it('Allows a rule to be edited', () => { + editFirstRule(); + + // expect define step to populate + cy.get(CUSTOM_QUERY_INPUT).should('have.text', existingRule.customQuery); + if (existingRule.index && existingRule.index.length > 0) { + cy.get(DEFINE_INDEX_INPUT).should('have.text', 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).should('have.text', existingRule.description); + cy.get(TAGS_FIELD).should('have.text', existingRule.tags.join('')); + cy.get(SEVERITY_DROPDOWN).should('have.text', 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(); + fillAboutRule(editedRule); + saveEditedRule(); + + cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', editedRule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', editedRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', editedRule.riskScore); + getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags); + }); + cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE) + .eq(INVESTIGATION_NOTES_TOGGLE) + .click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', editedRule.note); + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(INDEX_PATTERNS_DETAILS).should( + 'have.text', + expectedEditedIndexPatterns.join('') + ); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${editedRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - } + if (editedRule.interval) { + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS).should('have.text', 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 76871929fe050..e2ff51dd544a2 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 @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { eqlRule } from '../objects/rule'; +import { eqlRule, indexPatterns } from '../objects/rule'; import { CUSTOM_RULES_BTN, @@ -12,19 +12,32 @@ import { RULE_NAME, RULES_ROW, RULES_TABLE, + 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, - getDescriptionForTitle, + FALSE_POSITIVES_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -43,14 +56,25 @@ import { import { createAndActivateRule, fillAboutRuleAndContinue, - selectEqlRuleType, fillDefineEqlRuleAndContinue, + fillScheduleRuleAndContinue, + selectEqlRuleType, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = eqlRule.referenceUrls.join(''); +const expectedFalsePositives = eqlRule.falsePositivesExamples.join(''); +const expectedTags = eqlRule.tags.join(''); +const expectedMitre = eqlRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = 1; + describe('Detection rules, EQL', () => { before(() => { esArchiverLoad('timeline'); @@ -70,14 +94,14 @@ describe('Detection rules, EQL', () => { selectEqlRuleType(); fillDefineEqlRuleAndContinue(eqlRule); fillAboutRuleAndContinue(eqlRule); + fillScheduleRuleAndContinue(eqlRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -87,73 +111,40 @@ describe('Detection rules, EQL', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', eqlRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', eqlRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', eqlRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', eqlRule.name); + cy.get(RISK_SCORE).should('have.text', eqlRule.riskScore); + cy.get(SEVERITY).should('have.text', eqlRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - eqlRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - eqlRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - eqlRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - eqlRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - 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(RULE_NAME_HEADER).should('have.text', `${eqlRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', eqlRule.description); 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); + getDetails(SEVERITY_DETAILS).should('have.text', eqlRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', eqlRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', 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(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); 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'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${eqlRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${eqlRule.runsEvery.interval}${eqlRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${eqlRule.lookBack.interval}${eqlRule.lookBack.type}` + ); }); }); }); 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 47e49d48e2aec..49ec6381cbc89 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,14 +16,25 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { + ABOUT_DETAILS, ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + ANOMALY_SCORE_DETAILS, + DEFINITION_DETAILS, + FALSE_POSITIVES_DETAILS, + getDetails, MACHINE_LEARNING_JOB_ID, MACHINE_LEARNING_JOB_STATUS, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -43,6 +54,7 @@ import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineMachineLearningRuleAndContinue, + fillScheduleRuleAndContinue, selectMachineLearningRuleType, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; @@ -50,6 +62,16 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = machineLearningRule.referenceUrls.join(''); +const expectedFalsePositives = machineLearningRule.falsePositivesExamples.join(''); +const expectedTags = machineLearningRule.tags.join(''); +const expectedMitre = machineLearningRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = totalNumberOfPrebuiltRulesInEsArchive + 1; + describe('Detection rules, machine learning', () => { before(() => { esArchiverLoad('prebuilt_rules_loaded'); @@ -69,6 +91,7 @@ describe('Detection rules, machine learning', () => { selectMachineLearningRuleType(); fillDefineMachineLearningRuleAndContinue(machineLearningRule); fillAboutRuleAndContinue(machineLearningRule); + fillScheduleRuleAndContinue(machineLearningRule); createAndActivateRule(); cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); @@ -76,7 +99,6 @@ describe('Detection rules, machine learning', () => { changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = totalNumberOfPrebuiltRulesInEsArchive + 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -86,67 +108,42 @@ describe('Detection rules, machine learning', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', machineLearningRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', machineLearningRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', machineLearningRule.severity); + cy.get(RULE_NAME).should('have.text', machineLearningRule.name); + cy.get(RISK_SCORE).should('have.text', machineLearningRule.riskScore); + cy.get(SEVERITY).should('have.text', machineLearningRule.severity); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - machineLearningRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - machineLearningRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - machineLearningRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - machineLearningRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - - 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(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', machineLearningRule.description); 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); + getDetails(SEVERITY_DETAILS).should('have.text', machineLearningRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', machineLearningRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', 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); + getDetails(ANOMALY_SCORE_DETAILS).should( + 'have.text', + machineLearningRule.anomalyScoreThreshold + ); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + cy.get(MACHINE_LEARNING_JOB_STATUS).should('have.text', 'Stopped'); + cy.get(MACHINE_LEARNING_JOB_ID).should('have.text', 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'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${machineLearningRule.runsEvery.interval}${machineLearningRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${machineLearningRule.lookBack.interval}${machineLearningRule.lookBack.type}` + ); }); }); }); 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 4edf5e1866087..090012de72534 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 @@ -4,33 +4,58 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newOverrideRule } from '../objects/rule'; +import { indexPatterns, newOverrideRule, severitiesOverride } from '../objects/rule'; +import { + NUMBER_OF_ALERTS, + ALERT_RULE_NAME, + ALERT_RULE_METHOD, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, + RULE_SWITCH, RULES_ROW, RULES_TABLE, 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, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, + RISK_SCORE_OVERRIDE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - ABOUT_DETAILS, - getDescriptionForTitle, - DEFINITION_DETAILS, + RULE_NAME_OVERRIDE_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, - DETAILS_TITLE, - DETAILS_DESCRIPTION, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, + TIMESTAMP_OVERRIDE_DETAILS, } from '../screens/rule_details'; import { goToManageAlertsDetectionRules, + sortRiskScore, waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded, } from '../tasks/alerts'; @@ -46,12 +71,24 @@ import { createAndActivateRule, fillAboutRuleWithOverrideAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, + fillScheduleRuleAndContinue, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = newOverrideRule.referenceUrls.join(''); +const expectedFalsePositives = newOverrideRule.falsePositivesExamples.join(''); +const expectedTags = newOverrideRule.tags.join(''); +const expectedMitre = newOverrideRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); + describe('Detection rules, override', () => { before(() => { esArchiverLoad('timeline'); @@ -70,9 +107,10 @@ describe('Detection rules, override', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newOverrideRule); fillAboutRuleWithOverrideAndContinue(newOverrideRule); + fillScheduleRuleAndContinue(newOverrideRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -87,98 +125,75 @@ describe('Detection rules, override', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newOverrideRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newOverrideRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newOverrideRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newOverrideRule.name); + cy.get(RISK_SCORE).should('have.text', newOverrideRule.riskScore); + cy.get(SEVERITY).should('have.text', newOverrideRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newOverrideRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newOverrideRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newOverrideRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newOverrideRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newOverrideRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newOverrideRule.description); - - const expectedOverrideSeverities = ['Low', 'Medium', 'High', 'Critical']; - + cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newOverrideRule.description); 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', `${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); + getDetails(SEVERITY_DETAILS).should('have.text', newOverrideRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newOverrideRule.riskScore); + getDetails(RISK_SCORE_OVERRIDE_DETAILS).should( + 'have.text', + `${newOverrideRule.riskOverride}signal.rule.risk_score` + ); + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', newOverrideRule.nameOverride); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); + getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', 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]}` + 'have.text', + `${severity.sourceField}:${severity.sourceValue}${severitiesOverride[i]}` ); }); }); }); - 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(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); 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'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newOverrideRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newOverrideRule.runsEvery.interval}${newOverrideRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newOverrideRule.lookBack.interval}${newOverrideRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.above', 0); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', 'auditbeat'); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'query'); + cy.get(ALERT_RULE_SEVERITY).first().should('have.text', 'critical'); + + sortRiskScore(); + + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', '80'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts index 986a7c7177a79..6088a9dedbd06 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts @@ -56,7 +56,7 @@ describe('Alerts rules, prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN).invoke('text').should('eql', expectedElasticRulesBtnText); + cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -81,7 +81,7 @@ describe('Deleting prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN).invoke('text').should('eql', expectedElasticRulesBtnText); + cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -113,16 +113,15 @@ describe('Deleting prebuilt rules', () => { changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterDeletion})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterDeletion})` + ); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); }); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); - cy.get(RELOAD_PREBUILT_RULES_BTN) - .invoke('text') - .should('eql', 'Install 1 Elastic prebuilt rule '); + cy.get(RELOAD_PREBUILT_RULES_BTN).should('have.text', 'Install 1 Elastic prebuilt rule '); reloadDeletedRules(); @@ -135,9 +134,10 @@ describe('Deleting prebuilt rules', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterRecovering); }); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterRecovering})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterRecovering})` + ); }); it('Deletes and recovers more than one rule', () => { @@ -152,12 +152,14 @@ describe('Deleting prebuilt rules', () => { waitForRulesToBeLoaded(); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); - cy.get(RELOAD_PREBUILT_RULES_BTN) - .invoke('text') - .should('eql', `Install ${numberOfRulesToBeSelected} Elastic prebuilt rules `); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterDeletion})`); + cy.get(RELOAD_PREBUILT_RULES_BTN).should( + 'have.text', + `Install ${numberOfRulesToBeSelected} Elastic prebuilt rules ` + ); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterDeletion})` + ); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); }); @@ -173,8 +175,9 @@ describe('Deleting prebuilt rules', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterRecovering); }); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterRecovering})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterRecovering})` + ); }); }); 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 00175ed3baeb8..5ee7e69e877e3 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 @@ -4,27 +4,49 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newThresholdRule } from '../objects/rule'; +import { indexPatterns, newThresholdRule } from '../objects/rule'; +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, + RULE_SWITCH, RULES_ROW, RULES_TABLE, 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, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + THRESHOLD_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -44,13 +66,25 @@ import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineThresholdRuleAndContinue, + fillScheduleRuleAndContinue, selectThresholdRuleType, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = newThresholdRule.referenceUrls.join(''); +const expectedFalsePositives = newThresholdRule.falsePositivesExamples.join(''); +const expectedTags = newThresholdRule.tags.join(''); +const expectedMitre = newThresholdRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); + describe('Detection rules, threshold', () => { before(() => { esArchiverLoad('timeline'); @@ -70,9 +104,10 @@ describe('Detection rules, threshold', () => { selectThresholdRuleType(); fillDefineThresholdRuleAndContinue(newThresholdRule); fillAboutRuleAndContinue(newThresholdRule); + fillScheduleRuleAndContinue(newThresholdRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -87,79 +122,60 @@ describe('Detection rules, threshold', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newThresholdRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newThresholdRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newThresholdRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newThresholdRule.name); + cy.get(RISK_SCORE).should('have.text', newThresholdRule.riskScore); + cy.get(SEVERITY).should('have.text', newThresholdRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newThresholdRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newThresholdRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newThresholdRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newThresholdRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - 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(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThresholdRule.description); 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); + getDetails(SEVERITY_DETAILS).should('have.text', newThresholdRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newThresholdRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', 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(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); 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}` - ); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newThresholdRule.customQuery} `); + 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 ${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'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newThresholdRule.runsEvery.interval}${newThresholdRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newThresholdRule.lookBack.interval}${newThresholdRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.below', 100); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', newThresholdRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'threshold'); + cy.get(ALERT_RULE_SEVERITY) + .first() + .should('have.text', newThresholdRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newThresholdRule.riskScore); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts index 2fed23755963b..31d8e4666d91d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts @@ -35,7 +35,7 @@ describe('Alerts timeline', () => { .invoke('text') .then((eventId) => { investigateFirstAlertInTimeline(); - cy.get(PROVIDER_BADGE).invoke('text').should('eql', `_id: "${eventId}"`); + cy.get(PROVIDER_BADGE).should('have.text', `_id: "${eventId}"`); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 2a5c60815f450..e84e2b7b1669f 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -23,6 +23,12 @@ interface SeverityOverride { sourceValue: string; } +interface Interval { + interval: string; + timeType: string; + type: string; +} + export interface CustomRule { customQuery: string; name: string; @@ -38,6 +44,8 @@ export interface CustomRule { mitre: Mitre[]; note: string; timelineId: string; + runsEvery: Interval; + lookBack: Interval; } export interface ThresholdRule extends CustomRule { @@ -65,6 +73,8 @@ export interface MachineLearningRule { falsePositivesExamples: string[]; mitre: Mitre[]; note: string; + runsEvery: Interval; + lookBack: Interval; } const mitre1: Mitre = { @@ -83,8 +93,8 @@ const severityOverride1: SeverityOverride = { }; const severityOverride2: SeverityOverride = { - sourceField: 'agent.type', - sourceValue: 'endpoint', + sourceField: '@timestamp', + sourceValue: '10/02/2020', }; const severityOverride3: SeverityOverride = { @@ -93,8 +103,20 @@ const severityOverride3: SeverityOverride = { }; const severityOverride4: SeverityOverride = { - sourceField: '@timestamp', - sourceValue: '10/02/2020', + sourceField: 'agent.type', + sourceValue: 'auditbeat', +}; + +const runsEvery: Interval = { + interval: '1', + timeType: 'Seconds', + type: 's', +}; + +const lookBack: Interval = { + interval: '17520', + timeType: 'Hours', + type: 'h', }; export const newRule: CustomRule = { @@ -109,6 +131,8 @@ export const newRule: CustomRule = { mitre: [mitre1, mitre2], note: '# test markdown', timelineId: '0162c130-78be-11ea-9718-118a926974a4', + runsEvery, + lookBack, }; export const existingRule: CustomRule = { @@ -132,6 +156,8 @@ export const existingRule: CustomRule = { mitre: [], note: 'This is my note', timelineId: '', + runsEvery, + lookBack, }; export const newOverrideRule: OverrideRule = { @@ -150,6 +176,8 @@ export const newOverrideRule: OverrideRule = { riskOverride: 'destination.port', nameOverride: 'agent.type', timestampOverride: '@timestamp', + runsEvery, + lookBack, }; export const newThresholdRule: ThresholdRule = { @@ -166,6 +194,8 @@ export const newThresholdRule: ThresholdRule = { timelineId: '0162c130-78be-11ea-9718-118a926974a4', thresholdField: 'host.name', threshold: '10', + runsEvery, + lookBack, }; export const machineLearningRule: MachineLearningRule = { @@ -180,6 +210,8 @@ export const machineLearningRule: MachineLearningRule = { falsePositivesExamples: ['False1'], mitre: [mitre1], note: '# test markdown', + runsEvery, + lookBack, }; export const eqlRule: CustomRule = { @@ -194,4 +226,24 @@ export const eqlRule: CustomRule = { mitre: [mitre1, mitre2], note: '# test markdown', timelineId: '0162c130-78be-11ea-9718-118a926974a4', + runsEvery, + lookBack, +}; + +export const indexPatterns = [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', +]; + +export const severitiesOverride = ['Low', 'Medium', 'High', 'Critical']; + +export const editedRule = { + ...existingRule, + severity: 'Medium', + description: 'Edited Rule description', }; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index fb7e7e73986b9..ed05874bd4c4d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -4,45 +4,57 @@ * you may not use this file except in compliance with the Elastic License. */ -export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; +export const ALERTS = '[data-test-subj="event"]'; -export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]'; +export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; -export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-detection-rules"]'; +export const ALERT_ID = '[data-test-subj="draggable-content-_id"]'; -export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text'; +export const ALERT_RISK_SCORE_HEADER = '[data-test-subj="header-text-signal.rule.risk_score"]'; -export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; +export const ALERT_RULE_METHOD = '[data-test-subj="draggable-content-signal.rule.type"]'; -export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; +export const ALERT_RULE_NAME = '[data-test-subj="draggable-content-signal.rule.name"]'; -export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]'; +export const ALERT_RULE_RISK_SCORE = '[data-test-subj="draggable-content-signal.rule.risk_score"]'; -export const SELECTED_ALERTS = '[data-test-subj="selectedAlerts"]'; +export const ALERT_RULE_SEVERITY = '[data-test-subj="draggable-content-signal.rule.severity"]'; -export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; +export const ALERT_RULE_VERSION = '[data-test-subj="draggable-content-signal.rule.version"]'; -export const SHOWING_ALERTS = '[data-test-subj="showingAlerts"]'; +export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; -export const ALERTS = '[data-test-subj="event"]'; +export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closeSelectedAlertsButton"]'; -export const ALERT_ID = '[data-test-subj="draggable-content-_id"]'; +export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; -export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; +export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; -export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="alertActionPopover"] button'; +export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]'; -export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]'; +export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]'; -export const OPEN_SELECTED_ALERTS_BTN = '[data-test-subj="openSelectedAlertsButton"]'; +export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-detection-rules"]'; -export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closeSelectedAlertsButton"]'; +export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]'; export const MARK_SELECTED_ALERTS_IN_PROGRESS_BTN = '[data-test-subj="markSelectedAlertsInProgressButton"]'; +export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text'; + export const OPEN_ALERT_BTN = '[data-test-subj="open-alert-status"]'; -export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; +export const OPEN_SELECTED_ALERTS_BTN = '[data-test-subj="openSelectedAlertsButton"]'; -export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]'; +export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; + +export const SELECTED_ALERTS = '[data-test-subj="selectedAlerts"]'; + +export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; + +export const SHOWING_ALERTS = '[data-test-subj="showingAlerts"]'; + +export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="alertActionPopover"] button'; + +export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-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 dda371126d5aa..e1ab5ff30572f 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 @@ -57,6 +57,12 @@ export const INVESTIGATION_NOTES_TEXTAREA = export const FALSE_POSITIVES_INPUT = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input'; +export const LOOK_BACK_INTERVAL = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="interval"]'; + +export const LOOK_BACK_TIME_TYPE = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="timeType"]'; + export const MACHINE_LEARNING_DROPDOWN = '[data-test-subj="mlJobSelect"] button'; export const MACHINE_LEARNING_LIST = '.euiContextMenuItem__text'; @@ -73,6 +79,8 @@ export const MITRE_TECHNIQUES_INPUT = export const REFERENCE_URLS_INPUT = '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input'; +export const REFRESH_BUTTON = '[data-test-subj="refreshButton"]'; + export const RISK_INPUT = '.euiRangeInput'; export const RISK_MAPPING_OVERRIDE_OPTION = '#risk_score-mapping-override'; @@ -88,21 +96,29 @@ export const RULE_NAME_INPUT = export const RULE_NAME_OVERRIDE = '[data-test-subj="detectionEngineStepAboutRuleRuleNameOverride"]'; +export const RULE_STATUS = '[data-test-subj="ruleStatus"]'; + export const RULE_TIMESTAMP_OVERRIDE = '[data-test-subj="detectionEngineStepAboutRuleTimestampOverride"]'; +export const RUNS_EVERY_INTERVAL = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="interval"]'; + +export const RUNS_EVERY_TIME_TYPE = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="timeType"]'; + 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"]'; + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="interval"]'; export const SCHEDULE_INTERVAL_UNITS_INPUT = - '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-units-input"]'; + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="timeType"]'; export const SCHEDULE_LOOKBACK_AMOUNT_INPUT = - '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-amount-input"]'; + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="timeType"]'; export const SCHEDULE_LOOKBACK_UNITS_INPUT = '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-units-input"]'; 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 98fc7b06a9908..5a376e95e38dd 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const getDescriptionForTitle = (title: string) => - cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); - -export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; -export const DETAILS_TITLE = '.euiDescriptionList__title'; - export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; @@ -17,9 +11,23 @@ export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggl export const ABOUT_DETAILS = '[data-test-subj="aboutRule"] [data-test-subj="listItemColumnStepRuleDescription"]'; +export const ADDITIONAL_LOOK_BACK_DETAILS = 'Additional look-back time'; + +export const ANOMALY_SCORE_DETAILS = 'Anomaly score'; + +export const CUSTOM_QUERY_DETAILS = 'Custom query'; + export const DEFINITION_DETAILS = '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]'; +export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; + +export const DETAILS_TITLE = '.euiDescriptionList__title'; + +export const FALSE_POSITIVES_DETAILS = 'False positive examples'; + +export const INDEX_PATTERNS_DETAILS = 'Index patterns'; + export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; export const INVESTIGATION_NOTES_TOGGLE = 1; @@ -28,11 +36,38 @@ export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]' export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus"]'; +export const MITRE_ATTACK_DETAILS = 'MITRE ATT&CK'; + 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_NAME_OVERRIDE_DETAILS = 'Rule name override'; + +export const RISK_SCORE_DETAILS = 'Risk score'; + +export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; + +export const REFERENCE_URLS_DETAILS = 'Reference URLs'; + +export const RULE_TYPE_DETAILS = 'Rule type'; + +export const RUNS_EVERY_DETAILS = 'Runs every'; + export const SCHEDULE_DETAILS = '[data-test-subj=schedule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; + +export const SEVERITY_DETAILS = 'Severity'; + +export const TAGS_DETAILS = 'Tags'; + +export const THRESHOLD_DETAILS = 'Threshold'; + +export const TIMELINE_TEMPLATE_DETAILS = 'Timeline template'; + +export const TIMESTAMP_OVERRIDE_DETAILS = 'Timestamp override'; + +export const getDetails = (title: string) => + cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index f7ef5a904de99..c846ced2febfd 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -22,8 +22,10 @@ import { OPEN_SELECTED_ALERTS_BTN, MARK_ALERT_IN_PROGRESS_BTN, MARK_SELECTED_ALERTS_IN_PROGRESS_BTN, + ALERT_RISK_SCORE_HEADER, } from '../screens/alerts'; import { REFRESH_BUTTON } from '../screens/security_header'; +import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline'; export const closeFirstAlert = () => { cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true }); @@ -81,6 +83,12 @@ export const selectNumberOfAlerts = (numberOfAlerts: number) => { } }; +export const sortRiskScore = () => { + cy.get(ALERT_RISK_SCORE_HEADER).click(); + cy.get(TIMELINE_COLUMN_SPINNER).should('exist'); + cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); +}; + export const investigateFirstAlertInTimeline = () => { cy.get(SEND_ALERT_TO_TIMELINE_BTN).first().click({ force: true }); }; 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 dc89a39d082bc..914566a13a9a9 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 @@ -28,6 +28,8 @@ import { IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, INVESTIGATION_NOTES_TEXTAREA, + LOOK_BACK_INTERVAL, + LOOK_BACK_TIME_TYPE, MACHINE_LEARNING_DROPDOWN, MACHINE_LEARNING_LIST, MACHINE_LEARNING_TYPE, @@ -36,13 +38,17 @@ import { MITRE_TACTIC_DROPDOWN, MITRE_TECHNIQUES_INPUT, REFERENCE_URLS_INPUT, + REFRESH_BUTTON, RISK_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, RULE_NAME_INPUT, RULE_NAME_OVERRIDE, + RULE_STATUS, RULE_TIMESTAMP_OVERRIDE, + RUNS_EVERY_INTERVAL, + RUNS_EVERY_TIME_TYPE, SCHEDULE_CONTINUE_BUTTON, SCHEDULE_EDIT_TAB, SEVERITY_DROPDOWN, @@ -190,6 +196,13 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; +export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRule) => { + cy.get(RUNS_EVERY_INTERVAL).clear().type(rule.runsEvery.interval); + cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType); + cy.get(LOOK_BACK_INTERVAL).clear().type(rule.lookBack.interval); + cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType); +}; + export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; @@ -251,6 +264,14 @@ export const selectThresholdRuleType = () => { cy.get(THRESHOLD_TYPE).click({ force: true }); }; +export const waitForTheRuleToBeExecuted = async () => { + let status = ''; + while (status !== 'succeeded') { + cy.get(REFRESH_BUTTON).click(); + status = await cy.get(RULE_STATUS).invoke('text').promisify(); + } +}; + export const selectEqlRuleType = () => { cy.get(EQL_TYPE).click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/security_header.ts b/x-pack/plugins/security_solution/cypress/tasks/security_header.ts index 7427104a9d889..28efc47120d32 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/security_header.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/security_header.ts @@ -21,3 +21,7 @@ export const navigateFromHeaderTo = (page: string) => { export const refreshPage = () => { cy.get(REFRESH_BUTTON).click({ force: true }).invoke('text').should('not.equal', 'Updating'); }; + +export const waitForThePageToBeUpdated = () => { + cy.get(REFRESH_BUTTON).should('not.equal', 'Updating'); +}; diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index 6d79557fdaa28..4c9e3bc06037e 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -11,7 +11,7 @@ "cypress:open": "cypress open --config-file ./cypress/cypress.json", "cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/visual_config.ts", "cypress:run": "cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;", - "cypress:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", + "cypress:run-as-ci": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", "test:generate": "node scripts/endpoint/resolver_generator" }, "devDependencies": { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx index 0ddf4d06fb0fc..366bd156e0c1b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx @@ -69,7 +69,9 @@ const RuleStatusComponent: React.FC = ({ ruleId, ruleEnabled }) <> - {currentStatus?.status ?? getEmptyTagValue()} + + {currentStatus?.status ?? getEmptyTagValue()} + {currentStatus?.status_date != null && currentStatus?.status != null && ( @@ -84,6 +86,7 @@ const RuleStatusComponent: React.FC = ({ ruleId, ruleEnabled }) )} } >