From fa6bdaf7d3ce85064ca3c033c51c4c4e481c6f4f Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Fri, 14 Oct 2022 16:17:04 +0200 Subject: [PATCH 1/5] POC reusing existing types + no data driven tests --- .../schemas/common/schemas.ts | 1 + .../cypress/data/detection_engine.ts | 111 +++++++++++++ .../detection_rules/custom_query_rule.cy.ts | 154 ++++++++++++------ .../cypress/screens/create_new_rule.ts | 2 + .../cypress/screens/rule_details.ts | 6 + .../cypress/tasks/create_new_rule.ts | 83 +++++++++- 6 files changed, 299 insertions(+), 58 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/data/detection_engine.ts diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index f7c5fe630773..a7eb920d4f35 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -56,6 +56,7 @@ export const timestamp_field = t.string; export const timestampFieldOrUndefined = t.union([timestamp_field, t.undefined]); export const false_positives = t.array(t.string); +export type FalsePositives = t.TypeOf; export const file_name = t.string; export type FileName = t.TypeOf; diff --git a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts new file mode 100644 index 000000000000..923eb653cfb3 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + From, + RiskScore, + Severity, + Threat, + ThreatSubtechnique, + ThreatTechnique, +} from '@kbn/securitysolution-io-ts-alerting-types'; +import type { + Description, + FalsePositives, + Index, + Interval, + Name, + Note, + Query, + References, + Tags, +} from '../../common/detection_engine/schemas/common'; + +export const getQuery = (): Query => { + return 'host.name: *'; +}; + +export const getRuleName = (): Name => { + return 'Test Rule'; +}; + +export const getDescription = (): Description => { + return 'The rule description'; +}; + +export const getSeverity = (): Severity => { + return 'high'; +}; + +export const getRiskScore = (): RiskScore => { + return 17; +}; + +export const getTags = (): Tags => { + return ['test', 'newRule']; +}; + +export const getReferenceUrls = (): References => { + return ['http://example.com/', 'https://example.com/']; +}; + +export const getFalsePositives = (): FalsePositives => { + return ['False1', 'False2']; +}; + +export const getNote = (): Note => { + return '# test markdown'; +}; + +export const getThreat = (): Threat => { + return { + framework: 'MITRE ATT&CK', + tactic: { + name: 'Credential Access', + id: 'TA0006', + reference: 'https://attack.mitre.org/tactics/TA0006', + }, + }; +}; + +export const getThreatTechnique = (): ThreatTechnique => { + return { + id: 'T1003', + name: 'OS Credential Dumping', + reference: 'https://attack.mitre.org/techniques/T1003', + }; +}; + +export const getThreatSubtechnique = (): ThreatSubtechnique => { + return { + name: '/etc/passwd and /etc/shadow', + id: 'T1003.008', + reference: 'https://attack.mitre.org/techniques/T1003/008', + }; +}; + +export const getInterval = (): Interval => { + return '5m'; +}; + +export const getFrom = (): From => { + return '50000h'; +}; + +export const getDefaultIndexPatterns = (): Index => { + return [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', + '-*elastic-cloud-logs-*', + ]; +}; diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index b81806e2ce65..fa0eaa2264a3 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -5,8 +5,22 @@ * 2.0. */ -import { formatMitreAttackDescription } from '../../helpers/rules'; -import type { Mitre } from '../../objects/rule'; +import { + getDefaultIndexPatterns, + getDescription, + getFalsePositives, + getFrom, + getInterval, + getQuery, + getReferenceUrls, + getRiskScore, + getRuleName, + getSeverity, + getTags, + getThreat, + getThreatSubtechnique, + getThreatTechnique, +} from '../../data/detection_engine'; import { getNewRule, getExistingRule, @@ -14,7 +28,7 @@ import { getEditedRule, getNewOverrideRule, } from '../../objects/rule'; -import type { CompleteTimeline } from '../../objects/timeline'; +import { getTimeline } from '../../objects/timeline'; import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { @@ -56,7 +70,6 @@ import { INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, - MITRE_ATTACK_DETAILS, REFERENCE_URLS_DETAILS, RISK_SCORE_DETAILS, RULE_NAME_HEADER, @@ -66,6 +79,9 @@ import { SEVERITY_DETAILS, TAGS_DETAILS, TIMELINE_TEMPLATE_DETAILS, + THREAT_TACTIC, + THREAT_TECHNIQUE, + THREAT_SUBTECHNIQUE, } from '../../screens/rule_details'; import { @@ -82,14 +98,26 @@ import { createTimeline } from '../../tasks/api_calls/timelines'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; import { addEmailConnectorAndRuleAction } from '../../tasks/common/rule_actions'; import { + continueWithNextSection, createAndEnableRule, + expandAdvancedSettings, fillAboutRule, - fillAboutRuleAndContinue, - fillDefineCustomRuleAndContinue, - fillScheduleRuleAndContinue, + fillDescription, + fillFalsePositiveExamples, + fillFrom, + fillNote, + fillReferenceUrls, + fillRiskScore, + fillRuleName, + fillRuleTags, + fillSeverity, + fillThreat, + fillThreatSubtechnique, + fillThreatTechnique, goToAboutStepTab, goToActionsStepTab, goToScheduleStepTab, + importSavedQuery, waitForAlertsToPopulate, waitForTheRuleToBeExecuted, } from '../../tasks/create_new_rule'; @@ -105,98 +133,122 @@ describe('Custom query rules', () => { login(); }); describe('Custom detection rules creation', () => { - const expectedUrls = getNewRule().referenceUrls?.join(''); - const expectedFalsePositives = getNewRule().falsePositivesExamples?.join(''); - const expectedTags = getNewRule().tags?.join(''); - const mitreAttack = getNewRule().mitre as Mitre[]; - const expectedMitre = formatMitreAttackDescription(mitreAttack); const expectedNumberOfRules = 1; beforeEach(() => { - const timeline = getNewRule().timeline as CompleteTimeline; deleteAlertsAndRules(); - createTimeline(timeline).then((response) => { - cy.wrap({ - ...getNewRule(), - timeline: { - ...timeline, - id: response.body.data.persistTimeline.timeline.savedObjectId, - }, - }).as('rule'); - }); + createTimeline(getTimeline()) + .then((response) => { + return response.body.data.persistTimeline.timeline.savedObjectId; + }) + .as('timelineId'); }); it('Creates and enables a new rule', function () { visit(RULE_CREATION); - fillDefineCustomRuleAndContinue(this.rule); - fillAboutRuleAndContinue(this.rule); - fillScheduleRuleAndContinue(this.rule); + + cy.log('Filling define section'); + importSavedQuery(this.timelineId); + continueWithNextSection(); + + cy.log('Filling about section'); + fillRuleName(); + fillDescription(); + fillSeverity(); + fillRiskScore(); + fillRuleTags(); + expandAdvancedSettings(); + fillReferenceUrls(); + fillFalsePositiveExamples(); + fillThreat(); + fillThreatTechnique(); + fillThreatSubtechnique(); + fillNote(); + continueWithNextSection(); + + cy.log('Filling schedule section'); + fillFrom(); // expect define step to repopulate cy.get(DEFINE_EDIT_BUTTON).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', this.rule.customQuery); + cy.get(CUSTOM_QUERY_INPUT).should('have.value', getQuery()); 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('eql', this.rule.name); + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', getRuleName()); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); createAndEnableRule(); + cy.log('Asserting we have a new rule created'); cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); + cy.log('Asserting rule view in rules list'); cy.get(RULES_TABLE).find(RULES_ROW).should('have.length', expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', this.rule.name); - cy.get(RISK_SCORE).should('have.text', this.rule.riskScore); - cy.get(SEVERITY).should('have.text', this.rule.severity); + cy.get(RULE_NAME).should('have.text', getRuleName()); + cy.get(RISK_SCORE).should('have.text', getRiskScore()); + cy.get(SEVERITY) + .invoke('text') + .then((text) => { + cy.wrap(text.toLowerCase()).should('equal', getSeverity()); + }); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', this.rule.description); + cy.log('Asserting rule details'); + cy.get(RULE_NAME_HEADER).should('contain', getRuleName()); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getDescription()); cy.get(ABOUT_DETAILS).within(() => { - getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity); - getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore); + getDetails(SEVERITY_DETAILS) + .invoke('text') + .then((text) => { + cy.wrap(text.toLowerCase()).should('equal', getSeverity()); + }); + getDetails(RISK_SCORE_DETAILS).should('have.text', getRiskScore()); getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(expectedUrls); - }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); - getDetails(MITRE_ATTACK_DETAILS).should((mitre) => { - expect(removeExternalLinkText(mitre.text())).equal(expectedMitre); + expect(removeExternalLinkText(details.text())).equal(getReferenceUrls().join('')); }); - getDetails(TAGS_DETAILS).should('have.text', expectedTags); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', getFalsePositives().join('')); + getDetails(TAGS_DETAILS).should('have.text', getTags().join('')); }); + cy.get(THREAT_TACTIC).should( + 'contain', + `${getThreat().tactic.name} (${getThreat().tactic.id})` + ); + cy.get(THREAT_TECHNIQUE).should( + 'contain', + `${getThreatTechnique().name} (${getThreatTechnique().id})` + ); + cy.get(THREAT_SUBTECHNIQUE).should( + 'contain', + `${getThreatSubtechnique().name} (${getThreatSubtechnique().id})` + ); cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getDefaultIndexPatterns().join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getQuery()); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should( - 'have.text', - `${getNewRule().runsEvery?.interval}${getNewRule().runsEvery?.type}` - ); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( - 'have.text', - `${getNewRule().lookBack?.interval}${getNewRule().lookBack?.type}` - ); + getDetails(RUNS_EVERY_DETAILS).should('have.text', getInterval()); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', getFrom()); }); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); + cy.log('Asserting that alerts have been generated after the creation'); cy.get(NUMBER_OF_ALERTS) .invoke('text') .should('match', /^[1-9].+$/); // Any number of alerts - cy.get(ALERT_GRID_CELL).contains(this.rule.name); + cy.get(ALERT_GRID_CELL).contains(getRuleName()); }); }); 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 0d14673f4cf2..c57e2c603e46 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 @@ -72,6 +72,8 @@ export const DATA_VIEW_COMBO_BOX = export const DATA_VIEW_OPTION = '[data-test-subj="rule-index-toggle-dataView"]'; +export const CONTINUE_BUTTON = '[data-test-subj$=-continue]'; + export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]'; export const DEFINE_EDIT_BUTTON = '[data-test-subj="edit-define-rule"]'; 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 bad0770ffd76..a256c6386cc9 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -123,3 +123,9 @@ export const DEFINE_RULE_PANEL_PROGRESS = '[data-test-subj="defineRule"] [data-test-subj="stepPanelProgress"]'; export const EDIT_RULE_SETTINGS_LINK = '[data-test-subj="editRuleSettingsLink"]'; + +export const THREAT_TACTIC = '[data-test-subj="threatTacticLink"]'; + +export const THREAT_TECHNIQUE = '[data-test-subj="threatTechniqueLink"]'; + +export const THREAT_SUBTECHNIQUE = '[data-test-subj="threatSubtechniqueLink"]'; 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 7fdc101f8b14..5052d11dd74d 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 @@ -5,6 +5,12 @@ * 2.0. */ +import type { + From, + Threat, + ThreatSubtechnique, + ThreatTechnique, +} from '@kbn/securitysolution-io-ts-alerting-types'; import type { CustomRule, MachineLearningRule, @@ -97,6 +103,7 @@ import { NEW_TERMS_HISTORY_TIME_TYPE, NEW_TERMS_INPUT_AREA, ACTIONS_THROTTLE_INPUT, + CONTINUE_BUTTON, } from '../screens/create_new_rule'; import { INDEX_SELECTOR, @@ -109,6 +116,20 @@ import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { TIMELINE } from '../screens/timelines'; import { refreshPage } from './security_header'; import { EUI_FILTER_SELECT_ITEM, COMBO_BOX_INPUT } from '../screens/common/controls'; +import { + getDescription, + getFalsePositives, + getFrom, + getNote, + getReferenceUrls, + getRiskScore, + getRuleName, + getSeverity, + getTags, + getThreat, + getThreatSubtechnique, + getThreatTechnique, +} from '../data/detection_engine'; export const createAndEnableRule = () => { cy.get(CREATE_AND_ENABLE_BTN).click({ force: true }); @@ -149,11 +170,15 @@ export const fillAboutRule = ( } }; -const fillNote = (note: string) => { +export const expandAdvancedSettings = () => { + cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); +}; + +export const fillNote = (note: string = getNote()) => { cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(note, { force: true }); }; -const fillMitre = (mitreAttacks: Mitre[]) => { +export const fillMitre = (mitreAttacks: Mitre[]) => { let techniqueIndex = 0; let subtechniqueInputIndex = 0; mitreAttacks.forEach((mitre, tacticIndex) => { @@ -180,7 +205,26 @@ const fillMitre = (mitreAttacks: Mitre[]) => { }); }; -const fillFalsePositiveExamples = (falsePositives: string[]) => { +export const fillThreat = (threat: Threat = getThreat()) => { + cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).first().click({ force: true }); + cy.contains(MITRE_TACTIC, threat.tactic.name).click(); +}; + +export const fillThreatTechnique = (technique: ThreatTechnique = getThreatTechnique()) => { + cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).first().click({ force: true }); + cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).first().click({ force: true }); + cy.contains(MITRE_TACTIC, technique.name).click(); +}; + +export const fillThreatSubtechnique = ( + subtechnique: ThreatSubtechnique = getThreatSubtechnique() +) => { + cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).first().click({ force: true }); + cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN).first().click({ force: true }); + cy.contains(MITRE_TACTIC, subtechnique.name).click(); +}; + +export const fillFalsePositiveExamples = (falsePositives: string[] = getFalsePositives()) => { falsePositives.forEach((falsePositive, index) => { cy.get(FALSE_POSITIVES_INPUT) .eq(index) @@ -190,22 +234,30 @@ const fillFalsePositiveExamples = (falsePositives: string[]) => { }); }; -const fillSeverity = (severity: string) => { +export const fillRuleName = (ruleName: string = getRuleName()) => { + cy.get(RULE_NAME_INPUT).clear({ force: true }).type(ruleName, { force: true }); +}; + +export const fillDescription = (description: string = getDescription()) => { + cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(description, { force: true }); +}; + +export const fillSeverity = (severity: string = getSeverity()) => { cy.get(SEVERITY_DROPDOWN).click({ force: true }); cy.get(`#${severity.toLowerCase()}`).click(); }; -const fillRiskScore = (riskScore: string) => { +export const fillRiskScore = (riskScore: string = getRiskScore().toString()) => { cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${riskScore}`, { force: true }); }; -const fillRuleTags = (tags: string[]) => { +export const fillRuleTags = (tags: string[] = getTags()) => { tags.forEach((tag) => { cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); }); }; -const fillReferenceUrls = (referenceUrls: string[]) => { +export const fillReferenceUrls = (referenceUrls: string[] = getReferenceUrls()) => { referenceUrls.forEach((url, index) => { cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); @@ -286,6 +338,16 @@ const fillCustomQuery = (rule: CustomRule | OverrideRule) => { } }; +export const continueWithNextSection = () => { + cy.get(CONTINUE_BUTTON).should('exist').click(); +}; + +export const importSavedQuery = (timelineId: string) => { + cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); + cy.get(TIMELINE(timelineId)).click(); + cy.get(CUSTOM_QUERY_INPUT).should('not.be.empty'); +}; + export const fillDefineCustomRuleAndContinue = (rule: CustomRule | OverrideRule) => { if (rule.dataSource.type === 'dataView') { cy.get(DATA_VIEW_OPTION).click(); @@ -308,6 +370,13 @@ export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRu cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; +export const fillFrom = (from: From = getFrom()) => { + const value = from.slice(0, from.length - 1); + const type = from.slice(from.length - 1); + cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(value); + cy.get(LOOK_BACK_TIME_TYPE).select(type); +}; + export const fillRuleAction = (rule: CustomRule) => { if (rule.actions) { cy.get(ACTIONS_THROTTLE_INPUT).select(rule.actions.throttle); From 1ecb7707ae1caf28914b279ea331d49347e8eee0 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 10 Nov 2022 11:42:56 +0100 Subject: [PATCH 2/5] accommodating schemas file before merging main to avoid conflicts --- .../schemas/common/schemas.ts | 239 +----------------- 1 file changed, 2 insertions(+), 237 deletions(-) diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index a7eb920d4f35..52ba9e06622d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -7,56 +7,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { - IsoDateString, - NonEmptyString, - PositiveInteger, - PositiveIntegerGreaterThanZero, - UUID, - LimitedSizeArray, -} from '@kbn/securitysolution-io-ts-types'; import * as t from 'io-ts'; - -export const author = t.array(t.string); -export type Author = t.TypeOf; - -export const building_block_type = t.string; -export type BuildingBlockType = t.TypeOf; - -export const buildingBlockTypeOrUndefined = t.union([building_block_type, t.undefined]); - -export const description = NonEmptyString; -export type Description = t.TypeOf; - -// outcome is a property of the saved object resolve api -// will tell us info about the rule after 8.0 migrations -export const outcome = t.union([ - t.literal('exactMatch'), - t.literal('aliasMatch'), - t.literal('conflict'), -]); -export type Outcome = t.TypeOf; - -export const alias_target_id = t.string; -export const alias_purpose = t.union([ - t.literal('savedObjectConversion'), - t.literal('savedObjectImport'), -]); -export const enabled = t.boolean; -export type Enabled = t.TypeOf; -export const event_category_override = t.string; -export const eventCategoryOverrideOrUndefined = t.union([event_category_override, t.undefined]); - -export const tiebreaker_field = t.string; - -export const tiebreakerFieldOrUndefined = t.union([tiebreaker_field, t.undefined]); - -export const timestamp_field = t.string; - -export const timestampFieldOrUndefined = t.union([timestamp_field, t.undefined]); - -export const false_positives = t.array(t.string); -export type FalsePositives = t.TypeOf; +import { IsoDateString, PositiveInteger } from '@kbn/securitysolution-io-ts-types'; export const file_name = t.string; export type FileName = t.TypeOf; @@ -64,112 +16,13 @@ export type FileName = t.TypeOf; export const exclude_export_details = t.boolean; export type ExcludeExportDetails = t.TypeOf; -export const namespace = t.string; -export type Namespace = t.TypeOf; - -/** - * TODO: Right now the filters is an "unknown", when it could more than likely - * become the actual ESFilter as a type. - */ -export const filters = t.array(t.unknown); // Filters are not easily type-able yet -export type Filters = t.TypeOf; // Filters are not easily type-able yet - -export const filtersOrUndefined = t.union([filters, t.undefined]); -export type FiltersOrUndefined = t.TypeOf; - -export const immutable = t.boolean; -export type Immutable = t.TypeOf; - -// Note: Never make this a strict uuid, we allow the rule_id to be any string at the moment -// in case we encounter 3rd party rule systems which might be using auto incrementing numbers -// or other different things. -export const rule_id = t.string; -export type RuleId = t.TypeOf; - -export const ruleIdOrUndefined = t.union([rule_id, t.undefined]); -export type RuleIdOrUndefined = t.TypeOf; - -export const id = UUID; -export type Id = t.TypeOf; - -export const idOrUndefined = t.union([id, t.undefined]); -export type IdOrUndefined = t.TypeOf; - -export const index = t.array(t.string); -export type Index = t.TypeOf; - -export const data_view_id = t.string; - -export const dataViewIdOrUndefined = t.union([data_view_id, t.undefined]); - -export const indexOrUndefined = t.union([index, t.undefined]); -export type IndexOrUndefined = t.TypeOf; - -export const interval = t.string; -export type Interval = t.TypeOf; - -export const query = t.string; -export type Query = t.TypeOf; - -export const queryOrUndefined = t.union([query, t.undefined]); -export type QueryOrUndefined = t.TypeOf; - -export const license = t.string; -export type License = t.TypeOf; - -export const licenseOrUndefined = t.union([license, t.undefined]); - -export const objects = t.array(t.type({ rule_id })); - -export const output_index = t.string; - export const saved_id = t.string; export const savedIdOrUndefined = t.union([saved_id, t.undefined]); export type SavedIdOrUndefined = t.TypeOf; -export const timeline_id = t.string; -export type TimelineId = t.TypeOf; - -export const timelineIdOrUndefined = t.union([timeline_id, t.undefined]); - -export const timeline_title = t.string; - -export const timelineTitleOrUndefined = t.union([timeline_title, t.undefined]); - -export const timestamp_override = t.string; -export type TimestampOverride = t.TypeOf; - -export const timestampOverrideOrUndefined = t.union([timestamp_override, t.undefined]); -export type TimestampOverrideOrUndefined = t.TypeOf; - export const anomaly_threshold = PositiveInteger; -export const timestamp_override_fallback_disabled = t.boolean; - -export const timestampOverrideFallbackDisabledOrUndefined = t.union([ - timestamp_override_fallback_disabled, - t.undefined, -]); - -/** - * Note that this is a non-exact io-ts type as we allow extra meta information - * to be added to the meta object - */ -export const meta = t.object; -export type Meta = t.TypeOf; -export const metaOrUndefined = t.union([meta, t.undefined]); -export type MetaOrUndefined = t.TypeOf; - -export const name = NonEmptyString; -export type Name = t.TypeOf; - -export const rule_name_override = t.string; -export type RuleNameOverride = t.TypeOf; - -export const ruleNameOverrideOrUndefined = t.union([rule_name_override, t.undefined]); -export type RuleNameOverrideOrUndefined = t.TypeOf; - export const status = t.keyof({ open: null, closed: null, @@ -180,122 +33,34 @@ export type Status = t.TypeOf; export const conflicts = t.keyof({ abort: null, proceed: null }); -// TODO: Create a regular expression type or custom date math part type here -export const to = t.string; -export type To = t.TypeOf; - export const queryFilter = t.string; export type QueryFilter = t.TypeOf; export const queryFilterOrUndefined = t.union([queryFilter, t.undefined]); export type QueryFilterOrUndefined = t.TypeOf; -export const references = t.array(t.string); -export type References = t.TypeOf; - export const signal_ids = t.array(t.string); export type SignalIds = t.TypeOf; // TODO: Can this be more strict or is this is the set of all Elastic Queries? export const signal_status_query = t.object; -export const tags = t.array(t.string); -export type Tags = t.TypeOf; - export const fields = t.array(t.string); export type Fields = t.TypeOf; export const fieldsOrUndefined = t.union([fields, t.undefined]); export type FieldsOrUndefined = t.TypeOf; -export const thresholdField = t.exact( - t.type({ - field: t.union([t.string, t.array(t.string)]), // Covers pre- and post-7.12 - value: PositiveIntegerGreaterThanZero, - }) -); - -export const thresholdFieldNormalized = t.exact( - t.type({ - field: t.array(t.string), - value: PositiveIntegerGreaterThanZero, - }) -); - -export const thresholdCardinalityField = t.exact( - t.type({ - field: t.string, - value: PositiveInteger, - }) -); - -export const threshold = t.intersection([ - thresholdField, - t.exact( - t.partial({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); -export type Threshold = t.TypeOf; - -export const thresholdNormalized = t.intersection([ - thresholdFieldNormalized, - t.exact( - t.partial({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); -export type ThresholdNormalized = t.TypeOf; - -export const thresholdWithCardinality = t.intersection([ - thresholdFieldNormalized, - t.exact( - t.type({ - cardinality: t.array(thresholdCardinalityField), - }) - ), -]); -export type ThresholdWithCardinality = t.TypeOf; - -// New terms rule type currently only supports a single term, but should support more in the future -export const newTermsFields = LimitedSizeArray({ codec: t.string, minSize: 1, maxSize: 1 }); -export type NewTermsFields = t.TypeOf; - -export const historyWindowStart = NonEmptyString; -export type HistoryWindowStart = t.TypeOf; - export const created_at = IsoDateString; - export const updated_at = IsoDateString; - -export const updated_by = t.string; - export const created_by = t.string; +export const updated_by = t.string; -export const rules_installed = PositiveInteger; -export const rules_updated = PositiveInteger; export const status_code = PositiveInteger; export const message = t.string; export const perPage = PositiveInteger; export const total = PositiveInteger; export const success = t.boolean; export const success_count = PositiveInteger; -export const rules_custom_installed = PositiveInteger; -export const rules_not_installed = PositiveInteger; -export const rules_not_updated = PositiveInteger; - -export const timelines_installed = PositiveInteger; -export const timelines_updated = PositiveInteger; -export const timelines_not_installed = PositiveInteger; -export const timelines_not_updated = PositiveInteger; - -export const note = t.string; -export type Note = t.TypeOf; - -export const namespaceOrUndefined = t.union([namespace, t.undefined]); - -export const noteOrUndefined = t.union([note, t.undefined]); export const indexRecord = t.record( t.string, From 604ce5e99b3bb185f4a90b96d7cf540841f5d00a Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 10 Nov 2022 13:06:19 +0100 Subject: [PATCH 3/5] updates types --- .../cypress/data/detection_engine.ts | 45 ++++++++++--------- .../cypress/tasks/create_new_rule.ts | 8 ++-- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts index 923eb653cfb3..5ddd10c04b23 100644 --- a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts +++ b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts @@ -6,34 +6,35 @@ */ import type { - From, RiskScore, + RuleInterval, + RuleIntervalFrom, Severity, Threat, ThreatSubtechnique, ThreatTechnique, } from '@kbn/securitysolution-io-ts-alerting-types'; + import type { - Description, - FalsePositives, - Index, - Interval, - Name, - Note, - Query, - References, - Tags, -} from '../../common/detection_engine/schemas/common'; - -export const getQuery = (): Query => { + IndexPatternArray, + InvestigationGuide, + RuleDescription, + RuleFalsePositiveArray, + RuleQuery, + RuleName, + RuleReferenceArray, + RuleTagArray, +} from '../../common/detection_engine/rule_schema'; + +export const getQuery = (): RuleQuery => { return 'host.name: *'; }; -export const getRuleName = (): Name => { +export const getRuleName = (): RuleName => { return 'Test Rule'; }; -export const getDescription = (): Description => { +export const getDescription = (): RuleDescription => { return 'The rule description'; }; @@ -45,19 +46,19 @@ export const getRiskScore = (): RiskScore => { return 17; }; -export const getTags = (): Tags => { +export const getTags = (): RuleTagArray => { return ['test', 'newRule']; }; -export const getReferenceUrls = (): References => { +export const getReferenceUrls = (): RuleReferenceArray => { return ['http://example.com/', 'https://example.com/']; }; -export const getFalsePositives = (): FalsePositives => { +export const getFalsePositives = (): RuleFalsePositiveArray => { return ['False1', 'False2']; }; -export const getNote = (): Note => { +export const getInvestigationGuide = (): InvestigationGuide => { return '# test markdown'; }; @@ -88,15 +89,15 @@ export const getThreatSubtechnique = (): ThreatSubtechnique => { }; }; -export const getInterval = (): Interval => { +export const getInterval = (): RuleInterval => { return '5m'; }; -export const getFrom = (): From => { +export const getFrom = (): RuleIntervalFrom => { return '50000h'; }; -export const getDefaultIndexPatterns = (): Index => { +export const getDefaultIndexPatterns = (): IndexPatternArray => { return [ 'apm-*-transaction*', 'auditbeat-*', 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 5052d11dd74d..b4a3146dd40b 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 @@ -6,7 +6,7 @@ */ import type { - From, + RuleIntervalFrom, Threat, ThreatSubtechnique, ThreatTechnique, @@ -120,7 +120,7 @@ import { getDescription, getFalsePositives, getFrom, - getNote, + getInvestigationGuide, getReferenceUrls, getRiskScore, getRuleName, @@ -174,7 +174,7 @@ export const expandAdvancedSettings = () => { cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); }; -export const fillNote = (note: string = getNote()) => { +export const fillNote = (note: string = getInvestigationGuide()) => { cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(note, { force: true }); }; @@ -370,7 +370,7 @@ export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRu cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; -export const fillFrom = (from: From = getFrom()) => { +export const fillFrom = (from: RuleIntervalFrom = getFrom()) => { const value = from.slice(0, from.length - 1); const type = from.slice(from.length - 1); cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(value); From db09f554b035b1bf15afc141b41a40cb2fb2824b Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Sun, 13 Nov 2022 11:12:40 +0100 Subject: [PATCH 4/5] please comments --- .../cypress/data/detection_engine.ts | 124 +++++++----------- .../detection_rules/custom_query_rule.cy.ts | 60 ++++----- .../cypress/tasks/create_new_rule.ts | 80 ++++++----- 3 files changed, 122 insertions(+), 142 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts index 5ddd10c04b23..ea75943148ea 100644 --- a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts +++ b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts @@ -26,79 +26,26 @@ import type { RuleTagArray, } from '../../common/detection_engine/rule_schema'; -export const getQuery = (): RuleQuery => { - return 'host.name: *'; -}; - -export const getRuleName = (): RuleName => { - return 'Test Rule'; -}; - -export const getDescription = (): RuleDescription => { - return 'The rule description'; -}; - -export const getSeverity = (): Severity => { - return 'high'; -}; - -export const getRiskScore = (): RiskScore => { - return 17; -}; - -export const getTags = (): RuleTagArray => { - return ['test', 'newRule']; -}; - -export const getReferenceUrls = (): RuleReferenceArray => { - return ['http://example.com/', 'https://example.com/']; -}; - -export const getFalsePositives = (): RuleFalsePositiveArray => { - return ['False1', 'False2']; -}; - -export const getInvestigationGuide = (): InvestigationGuide => { - return '# test markdown'; -}; - -export const getThreat = (): Threat => { - return { - framework: 'MITRE ATT&CK', - tactic: { - name: 'Credential Access', - id: 'TA0006', - reference: 'https://attack.mitre.org/tactics/TA0006', - }, - }; -}; - -export const getThreatTechnique = (): ThreatTechnique => { - return { - id: 'T1003', - name: 'OS Credential Dumping', - reference: 'https://attack.mitre.org/techniques/T1003', - }; -}; - -export const getThreatSubtechnique = (): ThreatSubtechnique => { - return { - name: '/etc/passwd and /etc/shadow', - id: 'T1003.008', - reference: 'https://attack.mitre.org/techniques/T1003/008', - }; -}; - -export const getInterval = (): RuleInterval => { - return '5m'; -}; - -export const getFrom = (): RuleIntervalFrom => { - return '50000h'; -}; - -export const getDefaultIndexPatterns = (): IndexPatternArray => { - return [ +interface RuleFields { + defaultIndexPatterns: IndexPatternArray; + falsePositives: RuleFalsePositiveArray; + investigationGuide: InvestigationGuide; + referenceUrls: RuleReferenceArray; + riskScore: RiskScore; + ruleDescription: RuleDescription; + ruleInterval: RuleInterval; + ruleIntervalFrom: RuleIntervalFrom; + ruleQuery: RuleQuery; + ruleName: RuleName; + ruleTags: RuleTagArray; + severity: Severity; + threat: Threat; + threatSubtechnique: ThreatSubtechnique; + threatTechnique: ThreatTechnique; +} + +export const ruleFields: RuleFields = { + defaultIndexPatterns: [ 'apm-*-transaction*', 'auditbeat-*', 'endgame-*', @@ -108,5 +55,34 @@ export const getDefaultIndexPatterns = (): IndexPatternArray => { 'traces-apm*', 'winlogbeat-*', '-*elastic-cloud-logs-*', - ]; + ], + falsePositives: ['False1', 'False2'], + investigationGuide: '# test markdown', + referenceUrls: ['http://example.com/', 'https://example.com/'], + riskScore: 17, + ruleDescription: 'The rule description', + ruleInterval: '5m', + ruleIntervalFrom: '50000h', + ruleQuery: 'host.name: *', + ruleName: 'Test Rule', + ruleTags: ['test', 'newRule'], + ruleSeverity: 'high', + threat: { + framework: 'MITRE ATT&CK', + tactic: { + name: 'Credential Access', + id: 'TA0006', + reference: 'https://attack.mitre.org/tactics/TA0006', + }, + }, + threatSubtechnique: { + name: '/etc/passwd and /etc/shadow', + id: 'T1003.008', + reference: 'https://attack.mitre.org/techniques/T1003/008', + }, + threatTechnique: { + id: 'T1003', + name: 'OS Credential Dumping', + reference: 'https://attack.mitre.org/techniques/T1003', + }, }; diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index fa0eaa2264a3..b5869987f4d5 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -5,22 +5,7 @@ * 2.0. */ -import { - getDefaultIndexPatterns, - getDescription, - getFalsePositives, - getFrom, - getInterval, - getQuery, - getReferenceUrls, - getRiskScore, - getRuleName, - getSeverity, - getTags, - getThreat, - getThreatSubtechnique, - getThreatTechnique, -} from '../../data/detection_engine'; +import { ruleFields } from '../../data/detection_engine'; import { getNewRule, getExistingRule, @@ -171,13 +156,13 @@ describe('Custom query rules', () => { // expect define step to repopulate cy.get(DEFINE_EDIT_BUTTON).click(); - cy.get(CUSTOM_QUERY_INPUT).should('have.value', getQuery()); + cy.get(CUSTOM_QUERY_INPUT).should('have.value', ruleFields.ruleQuery); 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('eql', getRuleName()); + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', ruleFields.ruleName); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); @@ -189,56 +174,59 @@ describe('Custom query rules', () => { cy.log('Asserting rule view in rules list'); cy.get(RULES_TABLE).find(RULES_ROW).should('have.length', expectedNumberOfRules); - cy.get(RULE_NAME).should('have.text', getRuleName()); - cy.get(RISK_SCORE).should('have.text', getRiskScore()); + cy.get(RULE_NAME).should('have.text', ruleFields.ruleName); + cy.get(RISK_SCORE).should('have.text', ruleFields.riskScore); cy.get(SEVERITY) .invoke('text') .then((text) => { - cy.wrap(text.toLowerCase()).should('equal', getSeverity()); + cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity); }); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); cy.log('Asserting rule details'); - cy.get(RULE_NAME_HEADER).should('contain', getRuleName()); - cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getDescription()); + cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', ruleFields.ruleDescription); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS) .invoke('text') .then((text) => { - cy.wrap(text.toLowerCase()).should('equal', getSeverity()); + cy.wrap(text.toLowerCase()).should('equal', ruleFields.ruleSeverity); }); - getDetails(RISK_SCORE_DETAILS).should('have.text', getRiskScore()); + getDetails(RISK_SCORE_DETAILS).should('have.text', ruleFields.riskScore); getDetails(REFERENCE_URLS_DETAILS).should((details) => { - expect(removeExternalLinkText(details.text())).equal(getReferenceUrls().join('')); + expect(removeExternalLinkText(details.text())).equal(ruleFields.referenceUrls.join('')); }); - getDetails(FALSE_POSITIVES_DETAILS).should('have.text', getFalsePositives().join('')); - getDetails(TAGS_DETAILS).should('have.text', getTags().join('')); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', ruleFields.falsePositives.join('')); + getDetails(TAGS_DETAILS).should('have.text', ruleFields.ruleTags.join('')); }); cy.get(THREAT_TACTIC).should( 'contain', - `${getThreat().tactic.name} (${getThreat().tactic.id})` + `${ruleFields.threat.tactic.name} (${ruleFields.threat.tactic.id})` ); cy.get(THREAT_TECHNIQUE).should( 'contain', - `${getThreatTechnique().name} (${getThreatTechnique().id})` + `${ruleFields.threatTechnique.name} (${ruleFields.threatTechnique.id})` ); cy.get(THREAT_SUBTECHNIQUE).should( 'contain', - `${getThreatSubtechnique().name} (${getThreatSubtechnique().id})` + `${ruleFields.threatSubtechnique.name} (${ruleFields.threatSubtechnique.id})` ); cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getDefaultIndexPatterns().join('')); - getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getQuery()); + getDetails(INDEX_PATTERNS_DETAILS).should( + 'have.text', + ruleFields.defaultIndexPatterns.join('') + ); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', ruleFields.ruleQuery); getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); cy.get(SCHEDULE_DETAILS).within(() => { - getDetails(RUNS_EVERY_DETAILS).should('have.text', getInterval()); - getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', getFrom()); + getDetails(RUNS_EVERY_DETAILS).should('have.text', ruleFields.ruleInterval); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', ruleFields.ruleIntervalFrom); }); waitForTheRuleToBeExecuted(); @@ -248,7 +236,7 @@ describe('Custom query rules', () => { cy.get(NUMBER_OF_ALERTS) .invoke('text') .should('match', /^[1-9].+$/); // Any number of alerts - cy.get(ALERT_GRID_CELL).contains(getRuleName()); + cy.get(ALERT_GRID_CELL).contains(ruleFields.ruleName); }); }); 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 b4a3146dd40b..fc1f3b389bdf 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 @@ -116,20 +116,7 @@ import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { TIMELINE } from '../screens/timelines'; import { refreshPage } from './security_header'; import { EUI_FILTER_SELECT_ITEM, COMBO_BOX_INPUT } from '../screens/common/controls'; -import { - getDescription, - getFalsePositives, - getFrom, - getInvestigationGuide, - getReferenceUrls, - getRiskScore, - getRuleName, - getSeverity, - getTags, - getThreat, - getThreatSubtechnique, - getThreatTechnique, -} from '../data/detection_engine'; +import { ruleFields } from '../data/detection_engine'; export const createAndEnableRule = () => { cy.get(CREATE_AND_ENABLE_BTN).click({ force: true }); @@ -174,8 +161,9 @@ export const expandAdvancedSettings = () => { cy.get(ADVANCED_SETTINGS_BTN).click({ force: true }); }; -export const fillNote = (note: string = getInvestigationGuide()) => { +export const fillNote = (note: string = ruleFields.investigationGuide) => { cy.get(INVESTIGATION_NOTES_TEXTAREA).clear({ force: true }).type(note, { force: true }); + return note; }; export const fillMitre = (mitreAttacks: Mitre[]) => { @@ -203,28 +191,32 @@ export const fillMitre = (mitreAttacks: Mitre[]) => { cy.get(MITRE_ATTACK_ADD_TACTIC_BUTTON).click({ force: true }); }); + return mitreAttacks; }; -export const fillThreat = (threat: Threat = getThreat()) => { +export const fillThreat = (threat: Threat = ruleFields.threat) => { cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).first().click({ force: true }); cy.contains(MITRE_TACTIC, threat.tactic.name).click(); + return threat; }; -export const fillThreatTechnique = (technique: ThreatTechnique = getThreatTechnique()) => { +export const fillThreatTechnique = (technique: ThreatTechnique = ruleFields.threatTechnique) => { cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).first().click({ force: true }); cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).first().click({ force: true }); cy.contains(MITRE_TACTIC, technique.name).click(); + return technique; }; export const fillThreatSubtechnique = ( - subtechnique: ThreatSubtechnique = getThreatSubtechnique() + subtechnique: ThreatSubtechnique = ruleFields.threatSubtechnique ) => { cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).first().click({ force: true }); cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN).first().click({ force: true }); cy.contains(MITRE_TACTIC, subtechnique.name).click(); + return subtechnique; }; -export const fillFalsePositiveExamples = (falsePositives: string[] = getFalsePositives()) => { +export const fillFalsePositiveExamples = (falsePositives: string[] = ruleFields.falsePositives) => { falsePositives.forEach((falsePositive, index) => { cy.get(FALSE_POSITIVES_INPUT) .eq(index) @@ -232,36 +224,49 @@ export const fillFalsePositiveExamples = (falsePositives: string[] = getFalsePos .type(falsePositive, { force: true }); cy.get(ADD_FALSE_POSITIVE_BTN).click({ force: true }); }); + return falsePositives; +}; + +export const importSavedQuery = (timelineId: string) => { + cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); + cy.get(TIMELINE(timelineId)).click(); + cy.get(CUSTOM_QUERY_INPUT).should('not.be.empty'); }; -export const fillRuleName = (ruleName: string = getRuleName()) => { +export const fillRuleName = (ruleName: string = ruleFields.ruleName) => { cy.get(RULE_NAME_INPUT).clear({ force: true }).type(ruleName, { force: true }); + return ruleName; }; -export const fillDescription = (description: string = getDescription()) => { +export const fillDescription = (description: string = ruleFields.ruleDescription) => { cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(description, { force: true }); + return description; }; -export const fillSeverity = (severity: string = getSeverity()) => { +export const fillSeverity = (severity: string = ruleFields.ruleSeverity) => { cy.get(SEVERITY_DROPDOWN).click({ force: true }); cy.get(`#${severity.toLowerCase()}`).click(); + return severity; }; -export const fillRiskScore = (riskScore: string = getRiskScore().toString()) => { +export const fillRiskScore = (riskScore: string = ruleFields.riskScore.toString()) => { cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${riskScore}`, { force: true }); + return riskScore; }; -export const fillRuleTags = (tags: string[] = getTags()) => { +export const fillRuleTags = (tags: string[] = ruleFields.ruleTags) => { tags.forEach((tag) => { cy.get(TAGS_INPUT).type(`${tag}{enter}`, { force: true }); }); + return tags; }; -export const fillReferenceUrls = (referenceUrls: string[] = getReferenceUrls()) => { +export const fillReferenceUrls = (referenceUrls: string[] = ruleFields.referenceUrls) => { referenceUrls.forEach((url, index) => { cy.get(REFERENCE_URLS_INPUT).eq(index).clear({ force: true }).type(url, { force: true }); cy.get(ADD_REFERENCE_URL_BTN).click({ force: true }); }); + return referenceUrls; }; export const fillAboutRuleAndContinue = ( @@ -342,12 +347,6 @@ export const continueWithNextSection = () => { cy.get(CONTINUE_BUTTON).should('exist').click(); }; -export const importSavedQuery = (timelineId: string) => { - cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(timelineId)).click(); - cy.get(CUSTOM_QUERY_INPUT).should('not.be.empty'); -}; - export const fillDefineCustomRuleAndContinue = (rule: CustomRule | OverrideRule) => { if (rule.dataSource.type === 'dataView') { cy.get(DATA_VIEW_OPTION).click(); @@ -370,7 +369,7 @@ export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRu cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; -export const fillFrom = (from: RuleIntervalFrom = getFrom()) => { +export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) => { const value = from.slice(0, from.length - 1); const type = from.slice(from.length - 1); cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(value); @@ -683,3 +682,20 @@ export const checkLoadQueryDynamically = () => { export const uncheckLoadQueryDynamically = () => { cy.get(LOAD_QUERY_DYNAMICALLY_CHECKBOX).click({ force: true }).should('not.be.checked'); }; + +export const defineSection = { importSavedQuery }; +export const aboutSection = { + fillRuleName, + fillDescription, + fillSeverity, + fillRiskScore, + fillRuleTags, + expandAdvancedSettings, + fillReferenceUrls, + fillFalsePositiveExamples, + fillThreat, + fillThreatTechnique, + fillThreatSubtechnique, + fillNote, +}; +export const scheduleSection = { fillFrom }; From 5914b303490466b8f057b51598dcd5312030895f Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 16 Nov 2022 15:36:11 +0100 Subject: [PATCH 5/5] fixes --- .../plugins/security_solution/cypress/data/detection_engine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts index ea75943148ea..cdd72af22b78 100644 --- a/x-pack/plugins/security_solution/cypress/data/detection_engine.ts +++ b/x-pack/plugins/security_solution/cypress/data/detection_engine.ts @@ -38,7 +38,7 @@ interface RuleFields { ruleQuery: RuleQuery; ruleName: RuleName; ruleTags: RuleTagArray; - severity: Severity; + ruleSeverity: Severity; threat: Threat; threatSubtechnique: ThreatSubtechnique; threatTechnique: ThreatTechnique;