Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.12] [Security Solutions][Detection Engine] Fixes bug with not being able to duplicate indicator matches (#92565) #92716

Merged
merged 4 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@ import {
} from '../../tasks/alerts';
import {
changeRowsPerPageTo300,
duplicateFirstRule,
duplicateRuleFromMenu,
filterByCustomRules,
goToCreateNewRule,
goToRuleDetails,
waitForRulesTableToBeLoaded,
} from '../../tasks/alerts_detection_rules';
import { cleanKibana } from '../../tasks/common';
import { createCustomIndicatorRule } from '../../tasks/api_calls/rules';
import { cleanKibana, reload } from '../../tasks/common';
import {
createAndActivateRule,
fillAboutRuleAndContinue,
Expand All @@ -92,8 +95,10 @@ import {
waitForAlertsToPopulate,
waitForTheRuleToBeExecuted,
} from '../../tasks/create_new_rule';
import { waitForKibana } from '../../tasks/edit_rule';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login';
import { goBackToAllRulesTable } from '../../tasks/rule_details';

import { DETECTIONS_URL, RULE_CREATION } from '../../urls/navigation';

Expand Down Expand Up @@ -465,5 +470,30 @@ describe('indicator match', () => {
cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newThreatIndicatorRule.riskScore);
});
});

describe('Duplicates the indicator rule', () => {
beforeEach(() => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
goToManageAlertsDetectionRules();
createCustomIndicatorRule(newThreatIndicatorRule);
reload();
});

it('Allows the rule to be duplicated from the table', () => {
waitForKibana();
duplicateFirstRule();
cy.contains(RULE_NAME, `${newThreatIndicatorRule.name} [Duplicate]`);
});

it('Allows the rule to be duplicated from the edit screen', () => {
waitForKibana();
goToRuleDetails();
duplicateRuleFromMenu();
goBackToAllRulesTable();
reload();
cy.contains(RULE_NAME, `${newThreatIndicatorRule.name} [Duplicate]`);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export const DELETE_RULE_ACTION_BTN = '[data-test-subj="deleteRuleAction"]';

export const EDIT_RULE_ACTION_BTN = '[data-test-subj="editRuleAction"]';

export const DUPLICATE_RULE_ACTION_BTN = '[data-test-subj="duplicateRuleAction"]';

export const DUPLICATE_RULE_MENU_PANEL_BTN = '[data-test-subj="rules-details-duplicate-rule"]';

export const REFRESH_BTN = '[data-test-subj="refreshRulesAction"] button';

export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]';

export const ELASTIC_RULES_BTN = '[data-test-subj="showElasticRulesFilterButton"]';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
RULE_AUTO_REFRESH_IDLE_MODAL_CONTINUE,
rowsPerPageSelector,
pageSelector,
DUPLICATE_RULE_ACTION_BTN,
DUPLICATE_RULE_MENU_PANEL_BTN,
} from '../screens/alerts_detection_rules';
import { ALL_ACTIONS, DELETE_RULE } from '../screens/rule_details';

Expand All @@ -45,6 +47,33 @@ export const editFirstRule = () => {
cy.get(EDIT_RULE_ACTION_BTN).click();
};

export const duplicateFirstRule = () => {
cy.get(COLLAPSED_ACTION_BTN).should('be.visible');
cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true });
cy.get(DUPLICATE_RULE_ACTION_BTN).should('be.visible');
cy.get(DUPLICATE_RULE_ACTION_BTN).click();
};

/**
* Duplicates the rule from the menu and does additional
* pipes and checking that the elements are present on the
* page as well as removed when doing the clicks to help reduce
* flake.
*/
export const duplicateRuleFromMenu = () => {
cy.get(ALL_ACTIONS).should('be.visible');
cy.root()
.pipe(($el) => {
$el.find(ALL_ACTIONS).trigger('click');
return $el.find(DUPLICATE_RULE_MENU_PANEL_BTN);
})
.should(($el) => expect($el).to.be.visible);
// Because of a fade effect and fast clicking this can produce more than one click
cy.get(DUPLICATE_RULE_MENU_PANEL_BTN)
.pipe(($el) => $el.trigger('click'))
.should('not.be.visible');
};

export const deleteFirstRule = () => {
cy.get(COLLAPSED_ACTION_BTN).first().click({ force: true });
cy.get(DELETE_RULE_ACTION_BTN).click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { CustomRule } from '../../objects/rule';
import { CustomRule, ThreatIndicatorRule } from '../../objects/rule';

export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing') =>
cy.request({
Expand All @@ -29,6 +29,44 @@ export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing') =>
failOnStatusCode: false,
});

export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') =>
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: parseInt(rule.riskScore, 10),
description: rule.description,
interval: '10s',
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'threat_match',
threat_mapping: [
{
entries: [
{
field: rule.indicatorMapping,
type: 'mapping',
value: rule.indicatorMapping,
},
],
},
],
threat_query: '*:*',
threat_language: 'kuery',
threat_filters: [],
threat_index: ['mock*'],
threat_indicator_path: '',
from: 'now-17520h',
index: ['exceptions-*'],
query: rule.customQuery || '*:*',
language: 'kuery',
enabled: false,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});

export const createCustomRuleActivated = (rule: CustomRule, ruleId = '1') =>
cy.request({
method: 'POST',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import * as H from 'history';
import React, { Dispatch } from 'react';

import { CreateRulesSchema } from '../../../../../../common/detection_engine/schemas/request';
import {
deleteRules,
duplicateRules,
Expand All @@ -28,6 +29,7 @@ import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../../../common/lib/t

import * as i18n from '../translations';
import { bucketRulesResponse } from './helpers';
import { transformOutput } from '../../../../containers/detection_engine/rules/transforms';

export const editRuleAction = (rule: Rule, history: H.History) => {
history.push(getEditRuleUrl(rule.id));
Expand All @@ -41,7 +43,11 @@ export const duplicateRulesAction = async (
) => {
try {
dispatch({ type: 'loadingRuleIds', ids: ruleIds, actionType: 'duplicate' });
const response = await duplicateRules({ rules });
const response = await duplicateRules({
// We cast this back and forth here as the front end types are not really the right io-ts ones
// and the two types conflict with each other.
rules: rules.map((rule) => transformOutput(rule as CreateRulesSchema) as Rule),
});
const { errors } = bucketRulesResponse(response);
if (errors.length > 0) {
displayErrorToast(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const getActions = (
enabled: (rowItem: Rule) => canEditRuleWithActions(rowItem, actionsPrivileges),
},
{
'data-test-subj': 'duplicateRuleAction',
description: i18n.DUPLICATE_RULE,
icon: 'copy',
name: !actionsPrivileges ? (
Expand Down