[Security Solution] Adds basic test for rule data view (#136822)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Gloria Hornero 2022-07-25 19:40:19 +02:00 committed by GitHub
parent 711d337820
commit 691e21ec2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 386 additions and 211 deletions

View file

@ -74,12 +74,6 @@ const DEFAULT_INDEX_PATTERNS = ['index-1-*', 'index-2-*'];
const TAGS = ['cypress-tag-1', 'cypress-tag-2'];
const OVERWRITE_INDEX_PATTERNS = ['overwrite-index-1-*', 'overwrite-index-2-*'];
const customRule = {
...getNewRule(),
index: DEFAULT_INDEX_PATTERNS,
name: RULE_NAME,
};
const expectedNumberOfCustomRulesToBeEdited = 6;
const expectedNumberOfMachineLearningRulesToBeEdited = 1;
const numberOfRulesPerPage = 5;
@ -92,7 +86,14 @@ describe('Detection rules, bulk edit', () => {
beforeEach(() => {
deleteAlertsAndRules();
esArchiverResetKibana();
createCustomRule(customRule, '1');
createCustomRule(
{
...getNewRule(),
name: RULE_NAME,
dataSource: { index: DEFAULT_INDEX_PATTERNS, type: 'indexPatterns' },
},
'1'
);
createCustomRule(getExistingRule(), '2');
createCustomRule(getNewOverrideRule(), '3');
createCustomRule(getNewThresholdRule(), '4');

View file

@ -295,10 +295,13 @@ describe('Custom query rules', () => {
});
context('Edition', () => {
const expectedEditedtags = getEditedRule().tags.join('');
const rule = getEditedRule();
const expectedEditedtags = rule.tags.join('');
const expectedEditedIndexPatterns =
getEditedRule().index && getEditedRule().index.length
? getEditedRule().index
rule.dataSource.type === 'indexPatterns' &&
rule.dataSource.index &&
rule.dataSource.index.length
? rule.dataSource.index
: getIndexPatterns();
before(() => {
@ -325,27 +328,32 @@ describe('Custom query rules', () => {
});
it('Allows a rule to be edited', () => {
const existingRule = getExistingRule();
editFirstRule();
// expect define step to populate
cy.get(CUSTOM_QUERY_INPUT).should('have.value', getExistingRule().customQuery);
if (getExistingRule().index && getExistingRule().index.length > 0) {
cy.get(DEFINE_INDEX_INPUT).should('have.text', getExistingRule().index.join(''));
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.customQuery);
if (
existingRule.dataSource.type === 'indexPatterns' &&
existingRule.dataSource.index.length > 0
) {
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.dataSource.index.join(''));
}
goToAboutStepTab();
// expect about step to populate
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', getExistingRule().name);
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', getExistingRule().description);
cy.get(TAGS_FIELD).should('have.text', getExistingRule().tags.join(''));
cy.get(SEVERITY_DROPDOWN).should('have.text', getExistingRule().severity);
cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', getExistingRule().riskScore);
cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name);
cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description);
cy.get(TAGS_FIELD).should('have.text', existingRule.tags.join(''));
cy.get(SEVERITY_DROPDOWN).should('have.text', existingRule.severity);
cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', existingRule.riskScore);
goToScheduleStepTab();
// expect schedule step to populate
const interval = getExistingRule().interval;
const interval = existingRule.interval;
const intervalParts = interval != null && interval.match(/[0-9]+|[a-zA-Z]+/g);
if (intervalParts) {
const [amount, unit] = intervalParts;
@ -381,7 +389,7 @@ describe('Custom query rules', () => {
cy.wait('@getRule').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
// ensure that editing rule does not modify max_signals
cy.wrap(response?.body.max_signals).should('eql', getExistingRule().maxSignals);
cy.wrap(response?.body.max_signals).should('eql', existingRule.maxSignals);
});
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
@ -396,7 +404,7 @@ describe('Custom query rules', () => {
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(INDEX_PATTERNS_DETAILS).should(
'have.text',
expectedEditedIndexPatterns.join('')
expectedEditedIndexPatterns?.join('')
);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');

View file

@ -0,0 +1,158 @@
/*
* 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 { formatMitreAttackDescription } from '../../helpers/rules';
import { getDataViewRule } from '../../objects/rule';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
import {
CUSTOM_RULES_BTN,
RISK_SCORE,
RULE_NAME,
RULES_ROW,
RULES_TABLE,
RULE_SWITCH,
SEVERITY,
} from '../../screens/alerts_detection_rules';
import {
ADDITIONAL_LOOK_BACK_DETAILS,
ABOUT_DETAILS,
ABOUT_INVESTIGATION_NOTES,
ABOUT_RULE_DESCRIPTION,
CUSTOM_QUERY_DETAILS,
DEFINITION_DETAILS,
FALSE_POSITIVES_DETAILS,
removeExternalLinkText,
INDEX_PATTERNS_DETAILS,
INVESTIGATION_NOTES_MARKDOWN,
INVESTIGATION_NOTES_TOGGLE,
MITRE_ATTACK_DETAILS,
REFERENCE_URLS_DETAILS,
RISK_SCORE_DETAILS,
RULE_NAME_HEADER,
RULE_TYPE_DETAILS,
RUNS_EVERY_DETAILS,
SCHEDULE_DETAILS,
SEVERITY_DETAILS,
TAGS_DETAILS,
TIMELINE_TEMPLATE_DETAILS,
DATA_VIEW_DETAILS,
} from '../../screens/rule_details';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { postDataView } from '../../tasks/common';
import {
createAndEnableRule,
fillAboutRuleAndContinue,
fillDefineCustomRuleWithImportedQueryAndContinue,
fillScheduleRuleAndContinue,
waitForAlertsToPopulate,
waitForTheRuleToBeExecuted,
} from '../../tasks/create_new_rule';
import { esArchiverResetKibana } from '../../tasks/es_archiver';
import { login, visit } from '../../tasks/login';
import { getDetails } from '../../tasks/rule_details';
import { RULE_CREATION } from '../../urls/navigation';
describe('Custom query rules', () => {
before(() => {
login();
});
describe('Custom detection rules creation with data views', () => {
const rule = getDataViewRule();
const expectedUrls = rule.referenceUrls.join('');
const expectedFalsePositives = rule.falsePositivesExamples.join('');
const expectedTags = rule.tags.join('');
const expectedMitre = formatMitreAttackDescription(rule.mitre);
const expectedNumberOfRules = 1;
beforeEach(() => {
/* We don't call cleanKibana method on the before hook, instead we call esArchiverReseKibana on the before each. This is because we
are creating a data view we'll use after and cleanKibana does not delete all the data views created, esArchiverReseKibana does.
We don't use esArchiverReseKibana in all the tests because is a time-consuming method and we don't need to perform an exhaustive
cleaning in all the other tests. */
esArchiverResetKibana();
createTimeline(rule.timeline).then((response) => {
cy.wrap({
...rule,
timeline: {
...rule.timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
if (rule.dataSource.type === 'dataView') {
postDataView(rule.dataSource.dataView);
}
});
it('Creates and enables a new rule', function () {
visit(RULE_CREATION);
fillDefineCustomRuleWithImportedQueryAndContinue(this.rule);
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
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_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.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', this.rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore);
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);
});
getDetails(TAGS_DETAILS).should('have.text', expectedTags);
});
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(DATA_VIEW_DETAILS).should('have.text', this.rule.dataSource.dataView);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
});
cy.get(DEFINITION_DETAILS).should('not.contain', INDEX_PATTERNS_DETAILS);
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${getDataViewRule().runsEvery.interval}${getDataViewRule().runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${getDataViewRule().lookBack.interval}${getDataViewRule().lookBack.type}`
);
});
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
cy.get(NUMBER_OF_ALERTS)
.invoke('text')
.should('match', /^[1-9].+$/);
cy.get(ALERT_GRID_CELL).contains(this.rule.name);
});
});
});

View file

@ -205,12 +205,12 @@ describe('indicator match', () => {
describe('Indicator mapping', () => {
beforeEach(() => {
const rule = getNewThreatIndicatorRule();
visitWithoutDateRange(RULE_CREATION);
selectIndicatorMatchType();
fillIndexAndIndicatorIndexPattern(
getNewThreatIndicatorRule().index,
getNewThreatIndicatorRule().indicatorIndexPattern
);
if (rule.dataSource.type === 'indexPatterns') {
fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern);
}
});
it('Does NOT show invalidation text on initial page load', () => {
@ -419,11 +419,12 @@ describe('indicator match', () => {
});
it('Creates and enables a new Indicator Match rule', () => {
const rule = getNewThreatIndicatorRule();
visitWithoutDateRange(RULE_CREATION);
selectIndicatorMatchType();
fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule());
fillAboutRuleAndContinue(getNewThreatIndicatorRule());
fillScheduleRuleAndContinue(getNewThreatIndicatorRule());
fillDefineIndicatorMatchRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
@ -432,22 +433,19 @@ describe('indicator match', () => {
cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules);
});
cy.get(RULE_NAME).should('have.text', getNewThreatIndicatorRule().name);
cy.get(RISK_SCORE).should('have.text', getNewThreatIndicatorRule().riskScore);
cy.get(SEVERITY).should('have.text', getNewThreatIndicatorRule().severity);
cy.get(RULE_NAME).should('have.text', rule.name);
cy.get(RISK_SCORE).should('have.text', rule.riskScore);
cy.get(SEVERITY).should('have.text', rule.severity);
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
cy.get(RULE_NAME_HEADER).should('contain', `${getNewThreatIndicatorRule().name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getNewThreatIndicatorRule().description);
cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', rule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', getNewThreatIndicatorRule().severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', getNewThreatIndicatorRule().riskScore);
getDetails(INDICATOR_PREFIX_OVERRIDE).should(
'have.text',
getNewThreatIndicatorRule().threatIndicatorPath
);
getDetails(SEVERITY_DETAILS).should('have.text', rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore);
getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threatIndicatorPath);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -461,22 +459,19 @@ describe('indicator match', () => {
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(INDEX_PATTERNS_DETAILS).should(
'have.text',
getNewThreatIndicatorRule().index.join('')
);
if (rule.dataSource.type === 'indexPatterns') {
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.dataSource.index?.join(''));
}
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', '*:*');
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Indicator Match');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
getDetails(INDICATOR_INDEX_PATTERNS).should(
'have.text',
getNewThreatIndicatorRule().indicatorIndexPattern.join('')
rule.indicatorIndexPattern.join('')
);
getDetails(INDICATOR_MAPPING).should(
'have.text',
`${getNewThreatIndicatorRule().indicatorMappingField} MATCHES ${
getNewThreatIndicatorRule().indicatorIndexField
}`
`${rule.indicatorMappingField} MATCHES ${rule.indicatorIndexField}`
);
getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*');
});
@ -484,15 +479,11 @@ describe('indicator match', () => {
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${getNewThreatIndicatorRule().runsEvery.interval}${
getNewThreatIndicatorRule().runsEvery.type
}`
`${rule.runsEvery.interval}${rule.runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${getNewThreatIndicatorRule().lookBack.interval}${
getNewThreatIndicatorRule().lookBack.type
}`
`${rule.lookBack.interval}${rule.lookBack.type}`
);
});
@ -500,11 +491,9 @@ describe('indicator match', () => {
waitForAlertsToPopulate();
cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
cy.get(ALERT_RULE_NAME).first().should('have.text', getNewThreatIndicatorRule().name);
cy.get(ALERT_SEVERITY)
.first()
.should('have.text', getNewThreatIndicatorRule().severity.toLowerCase());
cy.get(ALERT_RISK_SCORE).first().should('have.text', getNewThreatIndicatorRule().riskScore);
cy.get(ALERT_RULE_NAME).first().should('have.text', rule.name);
cy.get(ALERT_SEVERITY).first().should('have.text', rule.severity.toLowerCase());
cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.riskScore);
});
it('Investigate alert in timeline', () => {

View file

@ -6,8 +6,7 @@
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { ThresholdRule } from '../../objects/rule';
import { getIndexPatterns, getNewRule, getNewThresholdRule } from '../../objects/rule';
import { getIndexPatterns, getNewThresholdRule } from '../../objects/rule';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
@ -20,7 +19,6 @@ import {
RULES_TABLE,
SEVERITY,
} from '../../screens/alerts_detection_rules';
import { PREVIEW_HEADER_SUBTITLE } from '../../screens/create_new_rule';
import {
ABOUT_DETAILS,
ABOUT_INVESTIGATION_NOTES,
@ -47,22 +45,14 @@ import {
} from '../../screens/rule_details';
import { getDetails } from '../../tasks/rule_details';
import { goToManageAlertsDetectionRules } from '../../tasks/alerts';
import {
goToCreateNewRule,
goToRuleDetails,
waitForRulesTableToBeLoaded,
} from '../../tasks/alerts_detection_rules';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
createAndEnableRule,
fillAboutRuleAndContinue,
fillDefineThresholdRuleAndContinue,
fillDefineThresholdRule,
fillScheduleRuleAndContinue,
previewResults,
selectThresholdRuleType,
waitForAlertsToPopulate,
waitForTheRuleToBeExecuted,
@ -156,37 +146,4 @@ describe('Detection rules, threshold', () => {
cy.get(NUMBER_OF_ALERTS).should(($count) => expect(+$count.text().split(' ')[0]).to.be.lt(100));
cy.get(ALERT_GRID_CELL).contains(rule.name);
});
it.skip('Preview results of keyword using "host.name"', () => {
rule.index = [...rule.index, '.siem-signals*'];
createCustomRuleEnabled(getNewRule());
goToManageAlertsDetectionRules();
waitForRulesTableToBeLoaded();
goToCreateNewRule();
selectThresholdRuleType();
fillDefineThresholdRule(rule);
previewResults();
cy.get(PREVIEW_HEADER_SUBTITLE).should('have.text', '3 unique hits');
});
it.skip('Preview results of "ip" using "source.ip"', () => {
const previewRule: ThresholdRule = {
...rule,
thresholdField: 'source.ip',
threshold: '1',
};
previewRule.index = [...previewRule.index, '.siem-signals*'];
createCustomRuleEnabled(getNewRule());
goToManageAlertsDetectionRules();
waitForRulesTableToBeLoaded();
goToCreateNewRule();
selectThresholdRuleType();
fillDefineThresholdRule(previewRule);
previewResults();
cy.get(PREVIEW_HEADER_SUBTITLE).should('have.text', '10 unique hits');
});
});

View file

@ -40,7 +40,11 @@ describe('Adds rule exception', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(
{ ...getNewRule(), customQuery: 'agent.name:*', index: ['exceptions*'] },
{
...getNewRule(),
customQuery: 'agent.name:*',
dataSource: { index: ['exceptions*'], type: 'indexPatterns' },
},
'rule_testing',
'1s'
);

View file

@ -70,7 +70,7 @@ describe('Exceptions flyout', () => {
createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) =>
createCustomRule({
...getNewRule(),
index: ['exceptions-*'],
dataSource: { index: ['exceptions-*'], type: 'indexPatterns' },
exceptionLists: [
{
id: response.body.id,

View file

@ -37,11 +37,15 @@ interface Interval {
type: string;
}
export type RuleDataSource =
| { type: 'indexPatterns'; index: string[] }
| { type: 'dataView'; dataView: string };
export interface CustomRule {
customQuery?: string;
name: string;
description: string;
index: string[];
dataSource: RuleDataSource;
interval?: string;
severity: string;
riskScore: string;
@ -176,15 +180,39 @@ const getRunsEvery = (): Interval => ({
type: 's',
});
const getRunsEveryFiveMinutes = (): Interval => ({
interval: '5',
timeType: 'Minutes',
type: 'm',
});
const getLookBack = (): Interval => ({
interval: '50000',
timeType: 'Hours',
type: 'h',
});
export const getDataViewRule = (): CustomRule => ({
customQuery: 'host.name: *',
dataSource: { dataView: 'auditbeat-2022', type: 'dataView' },
name: 'New Data View Rule',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEveryFiveMinutes(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
});
export const getNewRule = (): CustomRule => ({
customQuery: 'host.name: *',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
name: 'New Rule Test',
description: 'The new rule description.',
severity: 'High',
@ -202,7 +230,7 @@ export const getNewRule = (): CustomRule => ({
export const getBuildingBlockRule = (): CustomRule => ({
customQuery: 'host.name: *',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
name: 'Building Block Rule Test',
description: 'The new rule description.',
severity: 'High',
@ -221,7 +249,7 @@ export const getBuildingBlockRule = (): CustomRule => ({
export const getUnmappedRule = (): CustomRule => ({
customQuery: '*:*',
index: ['unmapped*'],
dataSource: { index: ['unmapped*'], type: 'indexPatterns' },
name: 'Rule with unmapped fields',
description: 'The new rule description.',
severity: 'High',
@ -239,7 +267,7 @@ export const getUnmappedRule = (): CustomRule => ({
export const getUnmappedCCSRule = (): CustomRule => ({
customQuery: '*:*',
index: [`${ccsRemoteName}:unmapped*`],
dataSource: { index: [`${ccsRemoteName}:unmapped*`], type: 'indexPatterns' },
name: 'Rule with unmapped fields',
description: 'The new rule description.',
severity: 'High',
@ -259,7 +287,7 @@ export const getExistingRule = (): CustomRule => ({
customQuery: 'host.name: *',
name: 'Rule 1',
description: 'Description for Rule 1',
index: ['auditbeat-*'],
dataSource: { index: ['auditbeat-*'], type: 'indexPatterns' },
interval: '100m',
severity: 'High',
riskScore: '19',
@ -278,7 +306,7 @@ export const getExistingRule = (): CustomRule => ({
export const getNewOverrideRule = (): OverrideRule => ({
customQuery: 'host.name: *',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
name: 'Override Rule',
description: 'The new rule description.',
severity: 'High',
@ -305,7 +333,7 @@ export const getNewOverrideRule = (): OverrideRule => ({
export const getNewThresholdRule = (): ThresholdRule => ({
customQuery: 'host.name: *',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
name: 'Threshold Rule',
description: 'The new rule description.',
severity: 'High',
@ -325,7 +353,7 @@ export const getNewThresholdRule = (): ThresholdRule => ({
export const getNewTermsRule = (): NewTermsRule => ({
customQuery: 'host.name: *',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
name: 'New Terms Rule',
description: 'The new rule description.',
severity: 'High',
@ -365,7 +393,7 @@ export const getMachineLearningRule = (): MachineLearningRule => ({
export const getEqlRule = (): CustomRule => ({
customQuery: 'any where process.name == "zsh"',
name: 'New EQL Rule',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
@ -383,7 +411,7 @@ export const getEqlRule = (): CustomRule => ({
export const getCCSEqlRule = (): CustomRule => ({
customQuery: 'any where process.name == "run-parts"',
name: 'New EQL Rule',
index: [`${ccsRemoteName}:run-parts`],
dataSource: { index: [`${ccsRemoteName}:run-parts`], type: 'indexPatterns' },
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
@ -404,7 +432,7 @@ export const getEqlSequenceRule = (): CustomRule => ({
[any where agent.name == "test.local"]\
[any where host.name == "test.local"]',
name: 'New EQL Sequence Rule',
index: getIndexPatterns(),
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
@ -422,7 +450,7 @@ export const getEqlSequenceRule = (): CustomRule => ({
export const getNewThreatIndicatorRule = (): ThreatIndicatorRule => ({
name: 'Threat Indicator Rule Test',
description: 'The threat indicator rule description.',
index: ['suspicious-*'],
dataSource: { index: ['suspicious-*'], type: 'indexPatterns' },
severity: 'Critical',
riskScore: '20',
tags: ['test', 'threat'],

View file

@ -93,6 +93,11 @@ export const AT_LEAST_ONE_INDEX_PATTERN = 'A minimum of one index pattern is req
export const CUSTOM_QUERY_REQUIRED = 'A custom query is required.';
export const DATA_VIEW_COMBO_BOX =
'[data-test-subj="pick-rule-data-source"] [data-test-subj="comboBoxInput"]';
export const DATA_VIEW_OPTION = '[data-test-subj="rule-index-toggle-dataView"]';
export const DEFINE_CONTINUE_BUTTON = '[data-test-subj="define-continue"]';
export const DEFINE_EDIT_BUTTON = '[data-test-subj="edit-define-rule"]';

View file

@ -22,6 +22,8 @@ export const ANOMALY_SCORE_DETAILS = 'Anomaly score';
export const CUSTOM_QUERY_DETAILS = 'Custom query';
export const DATA_VIEW_DETAILS = 'Data View';
export const DEFINITION_DETAILS =
'[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]';

View file

@ -28,7 +28,11 @@ export const createMachineLearningRule = (rule: MachineLearningRule, ruleId = 'm
failOnStatusCode: false,
});
export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing', interval = '100m') =>
export const createCustomRule = (
rule: CustomRule,
ruleId = 'rule_testing',
interval = '100m'
): Cypress.Chainable<Cypress.Response<unknown>> =>
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
@ -41,7 +45,7 @@ export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing', inte
severity: rule.severity.toLocaleLowerCase(),
type: 'query',
from: 'now-50000h',
index: rule.index,
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : '',
query: rule.customQuery,
language: 'kuery',
enabled: false,
@ -51,98 +55,107 @@ export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing', inte
failOnStatusCode: false,
});
export const createEventCorrelationRule = (rule: CustomRule, 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: `${rule.runsEvery.interval}${rule.runsEvery.type}`,
from: `now-${rule.lookBack.interval}${rule.lookBack.type}`,
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'eql',
index: rule.index,
query: rule.customQuery,
language: 'eql',
enabled: true,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
});
export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_testing') => {
if (rule.dataSource.type === 'indexPatterns') {
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: parseInt(rule.riskScore, 10),
description: rule.description,
interval: `${rule.runsEvery.interval}${rule.runsEvery.type}`,
from: `now-${rule.lookBack.interval}${rule.lookBack.type}`,
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'eql',
index: rule.dataSource.index,
query: rule.customQuery,
language: 'eql',
enabled: true,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
});
}
};
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,
// Default interval is 1m, our tests config overwrite this to 1s
// See https://github.com/elastic/kibana/pull/125396 for details
interval: '10s',
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'threat_match',
timeline_id: rule.timeline.templateTimelineId,
timeline_title: rule.timeline.title,
threat_mapping: [
{
entries: [
{
field: rule.indicatorMappingField,
type: 'mapping',
value: rule.indicatorIndexField,
},
],
},
],
threat_query: '*:*',
threat_language: 'kuery',
threat_filters: [],
threat_index: rule.indicatorIndexPattern,
threat_indicator_path: rule.threatIndicatorPath,
from: 'now-50000h',
index: rule.index,
query: rule.customQuery || '*:*',
language: 'kuery',
enabled: true,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') => {
if (rule.dataSource.type === 'indexPatterns') {
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: parseInt(rule.riskScore, 10),
description: rule.description,
// Default interval is 1m, our tests config overwrite this to 1s
// See https://github.com/elastic/kibana/pull/125396 for details
interval: '10s',
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'threat_match',
timeline_id: rule.timeline.templateTimelineId,
timeline_title: rule.timeline.title,
threat_mapping: [
{
entries: [
{
field: rule.indicatorMappingField,
type: 'mapping',
value: rule.indicatorIndexField,
},
],
},
],
threat_query: '*:*',
threat_language: 'kuery',
threat_filters: [],
threat_index: rule.indicatorIndexPattern,
threat_indicator_path: rule.threatIndicatorPath,
from: 'now-50000h',
index: rule.dataSource.index,
query: rule.customQuery || '*:*',
language: 'kuery',
enabled: true,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
}
};
export const createCustomRuleEnabled = (
rule: CustomRule,
ruleId = '1',
interval = '100m',
maxSignals = 500
) =>
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: parseInt(rule.riskScore, 10),
description: rule.description,
interval,
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'query',
from: 'now-50000h',
index: rule.index,
query: rule.customQuery,
language: 'kuery',
enabled: true,
tags: ['rule1'],
max_signals: maxSignals,
building_block_type: rule.buildingBlockType,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
) => {
if (rule.dataSource.type === 'indexPatterns') {
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: parseInt(rule.riskScore, 10),
description: rule.description,
interval,
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'query',
from: 'now-50000h',
index: rule.dataSource.index,
query: rule.customQuery,
language: 'kuery',
enabled: true,
tags: ['rule1'],
max_signals: maxSignals,
building_block_type: rule.buildingBlockType,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
}
};
export const deleteCustomRule = (ruleId = '1') => {
cy.request({

View file

@ -179,14 +179,14 @@ export const deleteCases = () => {
});
};
export const postDataView = (indexPattern: string) => {
export const postDataView = (dataSource: string) => {
cy.request({
method: 'POST',
url: `/api/index_patterns/index_pattern`,
body: {
index_pattern: {
fieldAttrs: '{}',
title: indexPattern,
title: dataSource,
timeFieldName: '@timestamp',
fields: '{}',
},

View file

@ -94,6 +94,8 @@ import {
EMAIL_CONNECTOR_PASSWORD_INPUT,
EMAIL_CONNECTOR_SERVICE_SELECTOR,
PREVIEW_HISTOGRAM,
DATA_VIEW_COMBO_BOX,
DATA_VIEW_OPTION,
NEW_TERMS_TYPE,
NEW_TERMS_HISTORY_SIZE,
NEW_TERMS_HISTORY_TIME_TYPE,
@ -258,6 +260,10 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => {
export const fillDefineCustomRuleWithImportedQueryAndContinue = (
rule: CustomRule | OverrideRule
) => {
if (rule.dataSource.type === 'dataView') {
cy.get(DATA_VIEW_OPTION).click();
cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.dataSource.dataView}{enter}`);
}
cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click();
cy.get(TIMELINE(rule.timeline.id)).click();
cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery);
@ -282,9 +288,11 @@ export const fillDefineThresholdRule = (rule: ThresholdRule) => {
cy.get(TIMELINE(rule.timeline.id)).click();
cy.get(COMBO_BOX_CLEAR_BTN).first().click();
rule.index.forEach((index) => {
cy.get(COMBO_BOX_INPUT).first().type(`${index}{enter}`);
});
if (rule.dataSource.type === 'indexPatterns') {
rule.dataSource.index.forEach((index) => {
cy.get(COMBO_BOX_INPUT).first().type(`${index}{enter}`);
});
}
cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery);
cy.get(THRESHOLD_INPUT_AREA)
@ -494,7 +502,9 @@ export const getCustomQueryInvalidationText = () => cy.contains(CUSTOM_QUERY_REQ
* @param rule The rule to use to fill in everything
*/
export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatIndicatorRule) => {
fillIndexAndIndicatorIndexPattern(rule.index, rule.indicatorIndexPattern);
if (rule.dataSource.type === 'indexPatterns') {
fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern);
}
fillIndicatorMatchRow({
indexField: rule.indicatorMappingField,
indicatorIndexField: rule.indicatorIndexField,