From 58d09e65ada98b2562f63a971f6105839d9bd4f7 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Fri, 6 Mar 2020 22:40:02 +0100 Subject: [PATCH 1/3] adds 'Creates and activates new rule' --- .../signal_detection_rules.spec.ts | 245 +++++++++++++++++- .../detection_engine/rules/all/columns.tsx | 13 +- .../components/description_step/index.tsx | 2 +- .../rules/components/mitre/index.tsx | 4 +- .../rules/components/severity_badge/index.tsx | 1 + .../components/step_about_rule/index.tsx | 10 +- .../components/step_define_rule/index.tsx | 2 +- .../components/step_schedule_rule/index.tsx | 1 + .../detection_engine/rules/details/index.tsx | 4 +- .../pages/detection_engine/rules/index.tsx | 1 + 10 files changed, 272 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index f2ed9d48daaf6..23c126814d520 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -25,12 +25,13 @@ import { DETECTIONS } from '../urls/navigation'; describe('Signal detection rules', () => { before(() => { loginAndWaitForPageWithoutDateRange(DETECTIONS); - }); - it('Loads prebuilt rules', () => { waitForSignalsPanelToBeLoaded(); waitForSignalsIndexToBeCreated(); goToManageSignalDetectionRules(); waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + }); + + it('Loads prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); @@ -47,4 +48,244 @@ describe('Signal detection rules', () => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); }); + + it('Creates and activates new rule', () => { + cy.get('[data-test-subj="create-new-rule"]').click({ force: true }); + + interface Mitre { + tactic: string; + techniques: string[]; + } + + interface Rule { + customQuery: string; + name: string; + description: string; + severity: string; + riskScore: string; + tags: string[]; + timelineTemplate?: string; + referenceUrls: string[]; + falsePositivesExamples: string[]; + mitre: Mitre[]; + } + + const mitre1: Mitre = { + tactic: 'Discovery (TA0007)', + techniques: ['Cloud Service Discovery (T1526)', 'File and Directory Discovery (T1083)'], + }; + + const mitre2: Mitre = { + tactic: 'Execution (TA0002)', + techniques: ['CMSTP (T1191)'], + }; + + const newRule: Rule = { + customQuery: 'hosts.name: *', + name: 'New Rule Test', + description: 'The new rule description.', + severity: 'High', + riskScore: '17', + tags: ['test', 'newRule'], + referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + falsePositivesExamples: ['False1', 'False2'], + mitre: [mitre1, mitre2], + }; + + cy.get('[data-test-subj="queryInput"]').type(newRule.customQuery); + cy.get('[data-test-subj="continue"]') + .should('exist') + .click({ force: true }); + cy.get('[data-test-subj="queryInput"]').should('not.exist'); + + cy.get( + '[data-test-subj="detectionEngineStepAboutRuleName"] [data-test-subj="input"]' + ).type(newRule.name, { force: true }); + cy.get( + '[data-test-subj="detectionEngineStepAboutRuleDescription"] [data-test-subj="input"]' + ).type(newRule.description, { force: true }); + cy.get('[data-test-subj="select"]').click({ force: true }); + + cy.get(`#${newRule.severity.toLowerCase()}`).click(); + cy.get('.euiRangeInput') + .clear({ force: true }) + .type(`${newRule.riskScore}`, { force: true }); + + newRule.tags.forEach(tag => { + cy.get( + '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]' + ).type(`${tag}{enter}`, { force: true }); + }); + cy.get('[data-test-subj="advancedSettings"] .euiAccordion__button').click({ force: true }); + + newRule.referenceUrls.forEach((url, index) => { + cy.get('[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input') + .eq(index) + .type(url, { force: true }); + cy.get( + '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] .euiButtonEmpty__text' + ).click({ force: true }); + }); + + newRule.falsePositivesExamples.forEach((falsePositive, index) => { + cy.get('[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input') + .eq(index) + .type(falsePositive, { force: true }); + cy.get( + '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text' + ).click({ force: true }); + }); + + newRule.mitre.forEach((mitre, index) => { + cy.get('[data-test-subj="mitreTactic"]') + .eq(index) + .click({ force: true }); + cy.contains('.euiContextMenuItem__text', mitre.tactic).click(); + + mitre.techniques.forEach(technique => { + cy.get('[data-test-subj="mitreTechniques"] [data-test-subj="comboBoxSearchInput"]') + .eq(index) + .type(`${technique}{enter}`, { force: true }); + }); + cy.get('[data-test-subj="addMitre"]').click({ force: true }); + }); + + cy.get('[data-test-subj="about-continue"]') + .should('exist') + .click({ force: true }); + + cy.get('[data-test-subj="create-activate"]').click({ force: true }); + cy.get('[data-test-subj="create-activate"]').should('not.exist'); + + cy.get('[data-test-subj="show-custom-rules-filter-button"]') + .invoke('text') + .should('eql', 'Custom rules (1)'); + + changeToThreeHundredRowsPerPage(); + waitForRulesToBeLoaded(); + + const expectedNumberOfRules = 93; + cy.get(RULES_TABLE).then($table => { + cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); + }); + + cy.get('[data-test-subj="show-custom-rules-filter-button"]').click({ force: true }); + cy.get('[data-test-subj="loading-spinner"]').should('exist'); + cy.get('[data-test-subj="loading-spinner"]').should('not.exist'); + + cy.get(RULES_TABLE).then($table => { + cy.wrap($table.find(RULES_ROW).length).should('eql', 1); + }); + + cy.get('[data-test-subj="ruleName"]') + .invoke('text') + .should('eql', newRule.name); + cy.get('[data-test-subj="riskScore"]') + .invoke('text') + .should('eql', newRule.riskScore); + cy.get('[data-test-subj="severity"]') + .invoke('text') + .should('eql', newRule.severity); + cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + + cy.get('[data-test-subj="ruleName"]').click({ force: true }); + + const expectedIndexPatterns = [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'packetbeat-*', + 'winlogbeat-*', + ]; + + cy.get('.euiLoadingSpinner').should('exist'); + cy.get('.euiLoadingSpinner').should('not.exist'); + + cy.get('[data-test-subj="definition"] .euiDescriptionList__description .euiBadge__text').then( + patterns => { + cy.wrap(patterns).each((pattern, index) => { + cy.wrap(pattern) + .invoke('text') + .should('eql', expectedIndexPatterns[index]); + }); + } + ); + + cy.get('[data-test-subj="definition"] .euiDescriptionList__description') + .eq(1) + .invoke('text') + .should('eql', `${newRule.customQuery} `); + + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(0) + .invoke('text') + .should('eql', newRule.description); + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(1) + .invoke('text') + .should('eql', newRule.severity); + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(2) + .invoke('text') + .should('eql', newRule.riskScore); + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(3) + .invoke('text') + .should('eql', 'Default blank timeline'); + + let expectedUrls = ''; + + newRule.referenceUrls.forEach(url => { + expectedUrls = expectedUrls + url; + }); + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(4) + .invoke('text') + .should('eql', expectedUrls); + + let expectedFalsePositives = ''; + + newRule.falsePositivesExamples.forEach(falsePositive => { + expectedFalsePositives = expectedFalsePositives + falsePositive; + }); + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(5) + .invoke('text') + .should('eql', expectedFalsePositives); + + let expectedMitre = ''; + + newRule.mitre.forEach(mitre => { + expectedMitre = expectedMitre + mitre.tactic; + mitre.techniques.forEach(technique => { + expectedMitre = expectedMitre + technique; + }); + }); + + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(6) + .invoke('text') + .should('eql', expectedMitre); + + let expectedTags = ''; + + newRule.tags.forEach(tag => { + expectedTags = expectedTags + tag; + }); + + cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') + .eq(7) + .invoke('text') + .should('eql', expectedTags); + + cy.get('[data-test-subj="schedule"] .euiDescriptionList__description') + .eq(0) + .invoke('text') + .should('eql', '5m'); + cy.get('[data-test-subj="schedule"] .euiDescriptionList__description') + .eq(1) + .invoke('text') + .should('eql', '1m'); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index 8cbad4e89c106..be64499fd47fa 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -11,6 +11,7 @@ import { EuiLink, EuiBasicTableColumn, EuiTableActionsColumnType, + EuiText, EuiHealth, } from '@elastic/eui'; import * as H from 'history'; @@ -99,7 +100,9 @@ export const getColumns = ({ field: 'name', name: i18n.COLUMN_RULE, render: (value: Rule['name'], item: Rule) => ( - {value} + + {value} + ), truncateText: true, width: '24%', @@ -107,6 +110,11 @@ export const getColumns = ({ { field: 'risk_score', name: i18n.COLUMN_RISK_SCORE, + render: (value: Rule['risk_score']) => ( + + {value} + + ), truncateText: true, width: '14%', }, @@ -150,7 +158,7 @@ export const getColumns = ({ field: 'tags', name: i18n.COLUMN_TAGS, render: (value: Rule['tags']) => ( - + {value.map((tag, i) => ( {tag} @@ -167,6 +175,7 @@ export const getColumns = ({ name: i18n.COLUMN_ACTIVATE, render: (value: Rule['enabled'], item: Rule) => ( = ({ return ( - + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx index e87dba251ed6d..a2d81e72af40e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx @@ -129,6 +129,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI onChange={updateTactic.bind(null, index)} fullWidth={false} valueOfSelected={camelCase(tacticName)} + data-test-subj="mitreTactic" /> ); @@ -144,6 +145,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI } ))} - + {i18n.ADD_MITRE_ATTACK} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.tsx index 09c02dfca56f9..c85fad7e878ae 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/severity_badge/index.tsx @@ -15,6 +15,7 @@ interface Props { const SeverityBadgeComponent: React.FC = ({ value }) => ( = ({ ) : ( <> -
+ = ({ @@ -269,7 +270,12 @@ const StepAboutRuleComponent: FC = ({ responsive={false} > - + {RuleI18n.CONTINUE} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 837bc79e968e8..490a8d9d194cb 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -238,7 +238,7 @@ const StepDefineRuleComponent: FC = ({ responsive={false} > - + {RuleI18n.CONTINUE} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx index e9632966fdfaf..e2ce9cae754d8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx @@ -133,6 +133,7 @@ const StepScheduleRuleComponent: FC = ({ isDisabled={isLoading} isLoading={isLoading} onClick={onSubmit.bind(null, true)} + data-test-subj="create-activate" > {I18n.COMPLETE_WITH_ACTIVATING} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index cd255b0951597..e73852ec91287 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -309,7 +309,7 @@ const RuleDetailsPageComponent: FC = ({ - + {aboutRuleData != null && ( = ({ - + {scheduleRuleData != null && ( { Date: Mon, 9 Mar 2020 18:54:52 +0100 Subject: [PATCH 2/3] loads data using es_archive --- .../signal_detection_rules.spec.ts | 6 ++++++ .../plugins/siem/cypress/tasks/es_archiver.ts | 21 +++++++++++++++++++ x-pack/test/siem_cypress/runner.ts | 1 + 3 files changed, 28 insertions(+) create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index 23c126814d520..d2ef34d82db2b 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -18,12 +18,14 @@ import { waitForSignalsIndexToBeCreated, waitForSignalsPanelToBeLoaded, } from '../tasks/detections'; +import { esArchiverLoadFolder, esArchiverResetKibana } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Signal detection rules', () => { before(() => { + esArchiverLoadFolder(); loginAndWaitForPageWithoutDateRange(DETECTIONS); waitForSignalsPanelToBeLoaded(); waitForSignalsIndexToBeCreated(); @@ -31,6 +33,10 @@ describe('Signal detection rules', () => { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); }); + after(() => { + esArchiverResetKibana(); + }); + it('Loads prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts new file mode 100644 index 0000000000000..998ea7edb42e3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts @@ -0,0 +1,21 @@ +/* + * 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 esArchiverLoadFolder = () => { + cy.exec( + `node ../../../../scripts/es_archiver load empty_kibana --dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; + +export const esArchiverResetKibana = () => { + cy.exec( + `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; diff --git a/x-pack/test/siem_cypress/runner.ts b/x-pack/test/siem_cypress/runner.ts index 7b2780ec91295..2462f75d4d0a4 100644 --- a/x-pack/test/siem_cypress/runner.ts +++ b/x-pack/test/siem_cypress/runner.ts @@ -27,6 +27,7 @@ export async function SiemCypressTestRunner({ getService }: FtrProviderContext) env: { FORCE_COLOR: '1', CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), + CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), ...process.env, From 7b508d562f5f26eac70a2165104244fd16ab8934 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 10 Mar 2020 11:47:19 +0100 Subject: [PATCH 3/3] refactor test --- .../signal_detection_rules.spec.ts | 246 ++++++------------ .../plugins/siem/cypress/objects/rule.ts | 45 ++++ .../siem/cypress/screens/create_new_rule.ts | 49 ++++ .../siem/cypress/screens/rule_details.ts | 39 +++ .../cypress/screens/signal_detection_rules.ts | 10 + .../siem/cypress/tasks/create_new_rule.ts | 92 +++++++ .../plugins/siem/cypress/tasks/es_archiver.ts | 12 +- .../cypress/tasks/signal_detection_rules.ts | 17 ++ 8 files changed, 345 insertions(+), 165 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/cypress/objects/rule.ts create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index d2ef34d82db2b..8c384c9010665 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -4,10 +4,46 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ELASTIC_RULES_BTN, RULES_TABLE, RULES_ROW } from '../screens/signal_detection_rules'; +import { newRule } from '../objects/rule'; +import { + ABOUT_DESCRIPTION, + ABOUT_EXPECTED_URLS, + ABOUT_FALSE_POSITIVES, + ABOUT_MITRE, + ABOUT_RISK, + ABOUT_RULE_DESCRIPTION, + ABOUT_SEVERITY, + ABOUT_TAGS, + ABOUT_TIMELINE, + DEFINITION_CUSTOM_QUERY, + DEFINITION_DESCRIPTION, + DEFINITION_INDEX_PATTERNS, + RULE_NAME_HEADER, + SCHEDULE_DESCRIPTION, + SCHEDULE_LOOPBACK, + SCHEDULE_RUNS, +} from '../screens/rule_details'; +import { + CUSTOM_RULES_BTN, + ELASTIC_RULES_BTN, + RISK_SCORE, + RULE_NAME, + RULES_TABLE, + RULES_ROW, + SEVERITY, +} from '../screens/signal_detection_rules'; + +import { + createAndActivateRule, + fillAboutRuleAndContinue, + fillDefineRuleAndContinue, +} from '../tasks/create_new_rule'; import { changeToThreeHundredRowsPerPage, + filterByCustomRules, + goToCreateNewRule, + goToRuleDetails, loadPrebuiltDetectionRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForPrebuiltDetectionRulesToBeLoaded, @@ -18,14 +54,14 @@ import { waitForSignalsIndexToBeCreated, waitForSignalsPanelToBeLoaded, } from '../tasks/detections'; -import { esArchiverLoadFolder, esArchiverResetKibana } from '../tasks/es_archiver'; +import { esArchiverLoadEmptyKibana, esArchiverUnloadEmptyKibana } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Signal detection rules', () => { before(() => { - esArchiverLoadFolder(); + esArchiverLoadEmptyKibana(); loginAndWaitForPageWithoutDateRange(DETECTIONS); waitForSignalsPanelToBeLoaded(); waitForSignalsIndexToBeCreated(); @@ -34,7 +70,7 @@ describe('Signal detection rules', () => { }); after(() => { - esArchiverResetKibana(); + esArchiverUnloadEmptyKibana(); }); it('Loads prebuilt rules', () => { @@ -56,114 +92,12 @@ describe('Signal detection rules', () => { }); it('Creates and activates new rule', () => { - cy.get('[data-test-subj="create-new-rule"]').click({ force: true }); - - interface Mitre { - tactic: string; - techniques: string[]; - } - - interface Rule { - customQuery: string; - name: string; - description: string; - severity: string; - riskScore: string; - tags: string[]; - timelineTemplate?: string; - referenceUrls: string[]; - falsePositivesExamples: string[]; - mitre: Mitre[]; - } - - const mitre1: Mitre = { - tactic: 'Discovery (TA0007)', - techniques: ['Cloud Service Discovery (T1526)', 'File and Directory Discovery (T1083)'], - }; - - const mitre2: Mitre = { - tactic: 'Execution (TA0002)', - techniques: ['CMSTP (T1191)'], - }; - - const newRule: Rule = { - customQuery: 'hosts.name: *', - name: 'New Rule Test', - description: 'The new rule description.', - severity: 'High', - riskScore: '17', - tags: ['test', 'newRule'], - referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], - falsePositivesExamples: ['False1', 'False2'], - mitre: [mitre1, mitre2], - }; - - cy.get('[data-test-subj="queryInput"]').type(newRule.customQuery); - cy.get('[data-test-subj="continue"]') - .should('exist') - .click({ force: true }); - cy.get('[data-test-subj="queryInput"]').should('not.exist'); - - cy.get( - '[data-test-subj="detectionEngineStepAboutRuleName"] [data-test-subj="input"]' - ).type(newRule.name, { force: true }); - cy.get( - '[data-test-subj="detectionEngineStepAboutRuleDescription"] [data-test-subj="input"]' - ).type(newRule.description, { force: true }); - cy.get('[data-test-subj="select"]').click({ force: true }); - - cy.get(`#${newRule.severity.toLowerCase()}`).click(); - cy.get('.euiRangeInput') - .clear({ force: true }) - .type(`${newRule.riskScore}`, { force: true }); - - newRule.tags.forEach(tag => { - cy.get( - '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]' - ).type(`${tag}{enter}`, { force: true }); - }); - cy.get('[data-test-subj="advancedSettings"] .euiAccordion__button').click({ force: true }); - - newRule.referenceUrls.forEach((url, index) => { - cy.get('[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input') - .eq(index) - .type(url, { force: true }); - cy.get( - '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] .euiButtonEmpty__text' - ).click({ force: true }); - }); - - newRule.falsePositivesExamples.forEach((falsePositive, index) => { - cy.get('[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input') - .eq(index) - .type(falsePositive, { force: true }); - cy.get( - '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text' - ).click({ force: true }); - }); - - newRule.mitre.forEach((mitre, index) => { - cy.get('[data-test-subj="mitreTactic"]') - .eq(index) - .click({ force: true }); - cy.contains('.euiContextMenuItem__text', mitre.tactic).click(); - - mitre.techniques.forEach(technique => { - cy.get('[data-test-subj="mitreTechniques"] [data-test-subj="comboBoxSearchInput"]') - .eq(index) - .type(`${technique}{enter}`, { force: true }); - }); - cy.get('[data-test-subj="addMitre"]').click({ force: true }); - }); + goToCreateNewRule(); + fillDefineRuleAndContinue(newRule); + fillAboutRuleAndContinue(newRule); + createAndActivateRule(); - cy.get('[data-test-subj="about-continue"]') - .should('exist') - .click({ force: true }); - - cy.get('[data-test-subj="create-activate"]').click({ force: true }); - cy.get('[data-test-subj="create-activate"]').should('not.exist'); - - cy.get('[data-test-subj="show-custom-rules-filter-button"]') + cy.get(CUSTOM_RULES_BTN) .invoke('text') .should('eql', 'Custom rules (1)'); @@ -175,26 +109,27 @@ describe('Signal detection rules', () => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); - cy.get('[data-test-subj="show-custom-rules-filter-button"]').click({ force: true }); - cy.get('[data-test-subj="loading-spinner"]').should('exist'); - cy.get('[data-test-subj="loading-spinner"]').should('not.exist'); + filterByCustomRules(); cy.get(RULES_TABLE).then($table => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - - cy.get('[data-test-subj="ruleName"]') + cy.get(RULE_NAME) .invoke('text') .should('eql', newRule.name); - cy.get('[data-test-subj="riskScore"]') + cy.get(RISK_SCORE) .invoke('text') .should('eql', newRule.riskScore); - cy.get('[data-test-subj="severity"]') + cy.get(SEVERITY) .invoke('text') .should('eql', newRule.severity); cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); - cy.get('[data-test-subj="ruleName"]').click({ force: true }); + goToRuleDetails(); + + cy.get(RULE_NAME_HEADER) + .invoke('text') + .should('eql', `${newRule.name} Beta`); const expectedIndexPatterns = [ 'apm-*-transaction*', @@ -204,93 +139,78 @@ describe('Signal detection rules', () => { 'packetbeat-*', 'winlogbeat-*', ]; - - cy.get('.euiLoadingSpinner').should('exist'); - cy.get('.euiLoadingSpinner').should('not.exist'); - - cy.get('[data-test-subj="definition"] .euiDescriptionList__description .euiBadge__text').then( - patterns => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern) - .invoke('text') - .should('eql', expectedIndexPatterns[index]); - }); - } - ); - - cy.get('[data-test-subj="definition"] .euiDescriptionList__description') - .eq(1) + 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_DESCRIPTION) + .eq(DEFINITION_CUSTOM_QUERY) .invoke('text') .should('eql', `${newRule.customQuery} `); - - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(0) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_RULE_DESCRIPTION) .invoke('text') .should('eql', newRule.description); - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(1) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_SEVERITY) .invoke('text') .should('eql', newRule.severity); - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(2) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_RISK) .invoke('text') .should('eql', newRule.riskScore); - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(3) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_TIMELINE) .invoke('text') .should('eql', 'Default blank timeline'); let expectedUrls = ''; - newRule.referenceUrls.forEach(url => { expectedUrls = expectedUrls + url; }); - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(4) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_EXPECTED_URLS) .invoke('text') .should('eql', expectedUrls); let expectedFalsePositives = ''; - newRule.falsePositivesExamples.forEach(falsePositive => { expectedFalsePositives = expectedFalsePositives + falsePositive; }); - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(5) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_FALSE_POSITIVES) .invoke('text') .should('eql', expectedFalsePositives); let expectedMitre = ''; - newRule.mitre.forEach(mitre => { expectedMitre = expectedMitre + mitre.tactic; mitre.techniques.forEach(technique => { expectedMitre = expectedMitre + technique; }); }); - - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(6) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_MITRE) .invoke('text') .should('eql', expectedMitre); let expectedTags = ''; - newRule.tags.forEach(tag => { expectedTags = expectedTags + tag; }); - - cy.get('[data-test-subj="aboutRule"] .euiDescriptionList__description') - .eq(7) + cy.get(ABOUT_DESCRIPTION) + .eq(ABOUT_TAGS) .invoke('text') .should('eql', expectedTags); - - cy.get('[data-test-subj="schedule"] .euiDescriptionList__description') - .eq(0) + cy.get(SCHEDULE_DESCRIPTION) + .eq(SCHEDULE_RUNS) .invoke('text') .should('eql', '5m'); - cy.get('[data-test-subj="schedule"] .euiDescriptionList__description') - .eq(1) + cy.get(SCHEDULE_DESCRIPTION) + .eq(SCHEDULE_LOOPBACK) .invoke('text') .should('eql', '1m'); }); diff --git a/x-pack/legacy/plugins/siem/cypress/objects/rule.ts b/x-pack/legacy/plugins/siem/cypress/objects/rule.ts new file mode 100644 index 0000000000000..ed9ef12860ab4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/objects/rule.ts @@ -0,0 +1,45 @@ +/* + * 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. + */ + +interface Mitre { + tactic: string; + techniques: string[]; +} + +export interface Rule { + customQuery: string; + name: string; + description: string; + severity: string; + riskScore: string; + tags: string[]; + timelineTemplate?: string; + referenceUrls: string[]; + falsePositivesExamples: string[]; + mitre: Mitre[]; +} + +const mitre1: Mitre = { + tactic: 'Discovery (TA0007)', + techniques: ['Cloud Service Discovery (T1526)', 'File and Directory Discovery (T1083)'], +}; + +const mitre2: Mitre = { + tactic: 'Execution (TA0002)', + techniques: ['CMSTP (T1191)'], +}; + +export const newRule: Rule = { + customQuery: 'hosts.name: *', + name: 'New Rule Test', + description: 'The new rule description.', + severity: 'High', + riskScore: '17', + tags: ['test', 'newRule'], + referenceUrls: ['https://www.google.com/', 'https://elastic.co/'], + falsePositivesExamples: ['False1', 'False2'], + mitre: [mitre1, mitre2], +}; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts b/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts new file mode 100644 index 0000000000000..1ac9278c3ce1c --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/create_new_rule.ts @@ -0,0 +1,49 @@ +/* + * 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 ABOUT_CONTINUE_BTN = '[data-test-subj="about-continue"]'; + +export const ADD_FALSE_POSITIVE_BTN = + '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; + +export const ADD_REFERENCE_URL_BTN = + '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] .euiButtonEmpty__text'; + +export const MITRE_BTN = '[data-test-subj="addMitre"]'; + +export const ADVANCED_SETTINGS_BTN = '[data-test-subj="advancedSettings"] .euiAccordion__button'; + +export const CREATE_AND_ACTIVATE_BTN = '[data-test-subj="create-activate"]'; + +export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]'; + +export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="continue"]'; + +export const FALSE_POSITIVES_INPUT = + '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input'; + +export const MITRE_TACTIC = '.euiContextMenuItem__text'; + +export const MITRE_TACTIC_DROPDOWN = '[data-test-subj="mitreTactic"]'; + +export const MITRE_TECHNIQUES_INPUT = + '[data-test-subj="mitreTechniques"] [data-test-subj="comboBoxSearchInput"]'; + +export const REFERENCE_URLS_INPUT = + '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input'; + +export const RISK_INPUT = '.euiRangeInput'; + +export const RULE_DESCRIPTION_INPUT = + '[data-test-subj="detectionEngineStepAboutRuleDescription"] [data-test-subj="input"]'; + +export const RULE_NAME_INPUT = + '[data-test-subj="detectionEngineStepAboutRuleName"] [data-test-subj="input"]'; + +export const SEVERITY_DROPDOWN = '[data-test-subj="select"]'; + +export const TAGS_INPUT = + '[data-test-subj="detectionEngineStepAboutRuleTags"] [data-test-subj="comboBoxSearchInput"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts new file mode 100644 index 0000000000000..46da52cd0ddd8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts @@ -0,0 +1,39 @@ +/* + * 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 ABOUT_DESCRIPTION = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; + +export const ABOUT_EXPECTED_URLS = 4; + +export const ABOUT_FALSE_POSITIVES = 5; + +export const ABOUT_MITRE = 6; + +export const ABOUT_RULE_DESCRIPTION = 0; + +export const ABOUT_RISK = 2; + +export const ABOUT_SEVERITY = 1; + +export const ABOUT_TAGS = 7; + +export const ABOUT_TIMELINE = 3; + +export const DEFINITION_CUSTOM_QUERY = 1; + +export const DEFINITION_DESCRIPTION = + '[data-test-subj="definition"] .euiDescriptionList__description'; + +export const DEFINITION_INDEX_PATTERNS = + '[data-test-subj="definition"] .euiDescriptionList__description .euiBadge__text'; + +export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; + +export const SCHEDULE_DESCRIPTION = '[data-test-subj="schedule"] .euiDescriptionList__description'; + +export const SCHEDULE_RUNS = 0; + +export const SCHEDULE_LOOPBACK = 1; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts index bfaa86e83f301..db3aaaaa94f4a 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +export const CREATE_NEW_RULE_BTN = '[data-test-subj="create-new-rule"]'; + +export const CUSTOM_RULES_BTN = '[data-test-subj="show-custom-rules-filter-button"]'; + export const ELASTIC_RULES_BTN = '[data-test-subj="show-elastic-rules-filter-button"]'; export const LOAD_PREBUILT_RULES_BTN = '[data-test-subj="load-prebuilt-rules"]'; @@ -15,8 +19,14 @@ export const LOADING_SPINNER = '[data-test-subj="loading-spinner"]'; export const PAGINATION_POPOVER_BTN = '[data-test-subj="tablePaginationPopoverButton"]'; +export const RISK_SCORE = '[data-test-subj="riskScore"]'; + +export const RULE_NAME = '[data-test-subj="ruleName"]'; + export const RULES_TABLE = '[data-test-subj="rules-table"]'; export const RULES_ROW = '.euiTableRow'; +export const SEVERITY = '[data-test-subj="severity"]'; + export const THREE_HUNDRED_ROWS = '[data-test-subj="tablePagination-300-rows"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts b/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts new file mode 100644 index 0000000000000..6bd5e0887e2fc --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/create_new_rule.ts @@ -0,0 +1,92 @@ +/* + * 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 { Rule } from '../objects/rule'; +import { + ABOUT_CONTINUE_BTN, + ADD_FALSE_POSITIVE_BTN, + ADD_REFERENCE_URL_BTN, + ADVANCED_SETTINGS_BTN, + CREATE_AND_ACTIVATE_BTN, + CUSTOM_QUERY_INPUT, + DEFINE_CONTINUE_BUTTON, + FALSE_POSITIVES_INPUT, + MITRE_BTN, + MITRE_TACTIC, + MITRE_TACTIC_DROPDOWN, + MITRE_TECHNIQUES_INPUT, + RISK_INPUT, + REFERENCE_URLS_INPUT, + RULE_DESCRIPTION_INPUT, + RULE_NAME_INPUT, + SEVERITY_DROPDOWN, + TAGS_INPUT, +} from '../screens/create_new_rule'; + +export const createAndActivateRule = () => { + cy.get(CREATE_AND_ACTIVATE_BTN).click({ force: true }); + cy.get(CREATE_AND_ACTIVATE_BTN).should('not.exist'); +}; + +export const fillAboutRuleAndContinue = (rule: Rule) => { + cy.get(RULE_NAME_INPUT).type(rule.name, { force: true }); + cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true }); + + cy.get(SEVERITY_DROPDOWN).click({ force: true }); + cy.get(`#${rule.severity.toLowerCase()}`).click(); + + cy.get(RISK_INPUT) + .clear({ force: true }) + .type(`${rule.riskScore}`, { force: true }); + + rule.tags.forEach(tag => { + cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); + }); + + 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(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(ADD_FALSE_POSITIVE_BTN).click({ force: true }); + }); + + rule.mitre.forEach((mitre, index) => { + cy.get(MITRE_TACTIC_DROPDOWN) + .eq(index) + .click({ force: true }); + 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_BTN).click({ force: true }); + }); + + cy.get(ABOUT_CONTINUE_BTN) + .should('exist') + .click({ force: true }); +}; + +export const fillDefineRuleAndContinue = (rule: Rule) => { + cy.get(CUSTOM_QUERY_INPUT).type(rule.customQuery); + cy.get(CUSTOM_QUERY_INPUT).should('have.attr', 'value', rule.customQuery); + cy.get(DEFINE_CONTINUE_BUTTON) + .should('exist') + .click({ force: true }); + + cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts index 998ea7edb42e3..72c95cba2361b 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts @@ -4,9 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -export const esArchiverLoadFolder = () => { +export const esArchiverLoadEmptyKibana = () => { cy.exec( - `node ../../../../scripts/es_archiver load empty_kibana --dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + `node ../../../../scripts/es_archiver empty_kibana load empty--dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; + +export const esArchiverUnloadEmptyKibana = () => { + cy.exec( + `node ../../../../scripts/es_archiver empty_kibana unload empty--dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( 'ELASTICSEARCH_URL' )} --kibana-url ${Cypress.config().baseUrl}` ); diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts b/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts index cc0e4bce1035a..22e5f166b2981 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts @@ -5,6 +5,7 @@ */ import { + CREATE_NEW_RULE_BTN, LOAD_PREBUILT_RULES_BTN, LOADING_INITIAL_PREBUILT_RULES_TABLE, LOADING_SPINNER, @@ -18,6 +19,22 @@ export const changeToThreeHundredRowsPerPage = () => { cy.get(THREE_HUNDRED_ROWS).click(); }; +export const filterByCustomRules = () => { + cy.get('[data-test-subj="show-custom-rules-filter-button"]').click({ force: true }); + cy.get('[data-test-subj="loading-spinner"]').should('exist'); + cy.get('[data-test-subj="loading-spinner"]').should('not.exist'); +}; + +export const goToCreateNewRule = () => { + cy.get(CREATE_NEW_RULE_BTN).click({ force: true }); +}; + +export const goToRuleDetails = () => { + cy.get('[data-test-subj="ruleName"]').click({ force: true }); + cy.get('.euiLoadingSpinner').should('exist'); + cy.get('.euiLoadingSpinner').should('not.exist'); +}; + export const loadPrebuiltDetectionRules = () => { cy.get(LOAD_PREBUILT_RULES_BTN) .should('exist')