[8.7] [Security Solution][Alerts] Removes custom cypress schemas in favor of io-ts schemas (#151520) (#152878)

# Backport

This will backport the following commits from `main` to `8.7`:
- [[Security Solution][Alerts] Removes custom cypress schemas in favor
of io-ts schemas
(#151520)](https://github.com/elastic/kibana/pull/151520)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Marshall
Main","email":"55718608+marshallmain@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-03-08T06:36:20Z","message":"[Security
Solution][Alerts] Removes custom cypress schemas in favor of io-ts
schemas (#151520)\n\n## Summary\r\n\r\nThis PR removes the rule schemas
that are specific to Cypress tests, and\r\nreplaces them with the HTTP
API schemas from the Detection Engine API.\r\nAs a result, the rule
schemas used in Cypress tests will now\r\nautomatically update when we
add new fields to rules. In addition, we\r\ncan try to start removing
some of mock rule data in Cypress tests and\r\nshare mocks across
integration/e2e/unit tests.\r\n\r\nFinally, this PR removes the specific
API call functions like\r\n`createCustomRule` and
`createCustomRuleEnabled` in favor of a generic\r\n`createRule` function
that can create any type of rule, without\r\nrestrictions on which
fields can be specified (e.g.\r\n`createMachineLearningRule` hard coded
`from: 'now-50000h',` in the\r\nfunction
body).\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b20feb24fa264a5bed66ef614936a6abcfbfd5ba","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:
SecuritySolution","Team:Detection
Alerts","v8.7.0","v8.8.0"],"number":151520,"url":"https://github.com/elastic/kibana/pull/151520","mergeCommit":{"message":"[Security
Solution][Alerts] Removes custom cypress schemas in favor of io-ts
schemas (#151520)\n\n## Summary\r\n\r\nThis PR removes the rule schemas
that are specific to Cypress tests, and\r\nreplaces them with the HTTP
API schemas from the Detection Engine API.\r\nAs a result, the rule
schemas used in Cypress tests will now\r\nautomatically update when we
add new fields to rules. In addition, we\r\ncan try to start removing
some of mock rule data in Cypress tests and\r\nshare mocks across
integration/e2e/unit tests.\r\n\r\nFinally, this PR removes the specific
API call functions like\r\n`createCustomRule` and
`createCustomRuleEnabled` in favor of a generic\r\n`createRule` function
that can create any type of rule, without\r\nrestrictions on which
fields can be specified (e.g.\r\n`createMachineLearningRule` hard coded
`from: 'now-50000h',` in the\r\nfunction
body).\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b20feb24fa264a5bed66ef614936a6abcfbfd5ba"}},"sourceBranch":"main","suggestedTargetBranches":["8.7"],"targetPullRequestStates":[{"branch":"8.7","label":"v8.7.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/151520","number":151520,"mergeCommit":{"message":"[Security
Solution][Alerts] Removes custom cypress schemas in favor of io-ts
schemas (#151520)\n\n## Summary\r\n\r\nThis PR removes the rule schemas
that are specific to Cypress tests, and\r\nreplaces them with the HTTP
API schemas from the Detection Engine API.\r\nAs a result, the rule
schemas used in Cypress tests will now\r\nautomatically update when we
add new fields to rules. In addition, we\r\ncan try to start removing
some of mock rule data in Cypress tests and\r\nshare mocks across
integration/e2e/unit tests.\r\n\r\nFinally, this PR removes the specific
API call functions like\r\n`createCustomRule` and
`createCustomRuleEnabled` in favor of a generic\r\n`createRule` function
that can create any type of rule, without\r\nrestrictions on which
fields can be specified (e.g.\r\n`createMachineLearningRule` hard coded
`from: 'now-50000h',` in the\r\nfunction
body).\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b20feb24fa264a5bed66ef614936a6abcfbfd5ba"}}]}]
BACKPORT-->
This commit is contained in:
Marshall Main 2023-03-08 00:59:14 -08:00 committed by GitHub
parent 58099ea904
commit a472ed48f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 1074 additions and 1485 deletions

View file

@ -9,7 +9,7 @@ import { JSON_TEXT } from '../../screens/alerts_details';
import { expandFirstAlert, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts';
import { openJsonView } from '../../tasks/alerts_details';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { esArchiverCCSLoad } from '../../tasks/es_archiver';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -23,7 +23,7 @@ describe('Alert details with unmapped fields', () => {
login();
cleanKibana();
esArchiverCCSLoad('unmapped_fields');
createCustomRuleEnabled(getUnmappedCCSRule());
createRule(getUnmappedCCSRule());
visitWithoutDateRange(ALERTS_URL);
waitForAlertsPanelToBeLoaded();
expandFirstAlert();

View file

@ -15,7 +15,7 @@ import {
goToRuleDetails,
waitForRulesTableToBeLoaded,
} from '../../tasks/alerts_detection_rules';
import { createEventCorrelationRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -31,9 +31,9 @@ describe('Detection rules', function () {
it('EQL rule on remote indices generates alerts', function () {
esArchiverCCSLoad('linux_process');
this.rule = getCCSEqlRule();
const rule = getCCSEqlRule();
login();
createEventCorrelationRule(this.rule);
createRule(rule);
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
waitForRulesTableToBeLoaded();
filterByCustomRules();
@ -46,9 +46,9 @@ describe('Detection rules', function () {
.invoke('text')
.then((text) => {
cy.log('ALERT_DATA_GRID', text);
expect(text).contains(this.rule.name);
expect(text).contains(this.rule.severity.toLowerCase());
expect(text).contains(this.rule.riskScore);
expect(text).contains(rule.name);
expect(text).contains(rule.severity);
expect(text).contains(rule.risk_score);
});
});
});

View file

@ -9,7 +9,7 @@ import { getNewRule } from '../../objects/rule';
import { ROLES } from '../../../common/test';
import { expandFirstAlertActions } from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visit, waitForPageWithoutDateRange } from '../../tasks/login';
@ -28,7 +28,7 @@ describe('Alerts timeline', () => {
// First we login as a privileged user to create alerts.
cleanKibana();
login();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
});

View file

@ -18,7 +18,7 @@ import {
waitForInstallRiskScoreModule,
} from '../../tasks/api_calls/risk_scores';
import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
import { clickEnableRiskScore } from '../../tasks/risk_scores';
@ -36,7 +36,7 @@ describe('Enable risk scores', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
});
beforeEach(() => {

View file

@ -30,7 +30,7 @@ import {
HOSTS_TABLE_ALERT_CELL,
} from '../../screens/entity_analytics';
import { openRiskTableFilterAndSelectTheLowOption } from '../../tasks/host_risk';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { getNewRule } from '../../objects/rule';
import { QUERY_TAB_BUTTON } from '../../screens/timeline';
@ -143,7 +143,7 @@ describe('Entity Analytics Dashboard', () => {
describe('With alerts data', () => {
before(() => {
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
});
beforeEach(() => {
@ -203,7 +203,7 @@ describe('Entity Analytics Dashboard', () => {
describe('With alerts data', () => {
before(() => {
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
});
beforeEach(() => {

View file

@ -16,7 +16,7 @@ import {
} from '../../screens/entity_analytics';
import { deleteRiskScore, installLegacyRiskScoreModule } from '../../tasks/api_calls/risk_scores';
import { findSavedObjects } from '../../tasks/api_calls/risk_scores/saved_objects';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
import {
@ -39,7 +39,7 @@ describe('Upgrade risk scores', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
});
beforeEach(() => {
@ -88,7 +88,7 @@ versions.forEach((version) =>
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
});
beforeEach(() => {

View file

@ -11,7 +11,7 @@ import { openTimelineFieldsBrowser, populateTimeline } from '../../tasks/timelin
import { HOSTS_URL, ALERTS_URL } from '../../urls/navigation';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { getNewRule } from '../../objects/rule';
import { refreshPage } from '../../tasks/security_header';
@ -27,7 +27,7 @@ describe('Create DataView runtime field', () => {
it('adds field to alert table', () => {
const fieldName = 'field.name.alert.page';
visit(ALERTS_URL);
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
refreshPage();
waitForAlertsToPopulate();
openEventsViewerFieldsBrowser();

View file

@ -6,12 +6,11 @@
*/
import { expandFirstAlert, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
import { getNewRule } from '../../objects/rule';
import type { CustomRule } from '../../objects/rule';
import { ALERTS_URL } from '../../urls/navigation';
import {
@ -24,12 +23,11 @@ import { OPEN_ALERT_DETAILS_PAGE } from '../../screens/alerts_details';
describe('Alert Details Page Navigation', () => {
describe('navigating to alert details page', () => {
let rule: CustomRule;
const rule = getNewRule();
before(() => {
rule = getNewRule();
cleanKibana();
login();
createCustomRuleEnabled(rule, 'rule1');
createRule({ ...rule, rule_id: 'rule1' });
});
describe('context menu', () => {

View file

@ -20,7 +20,7 @@ import { expandFirstAlert } from '../../tasks/alerts';
import { verifyInsightCount } from '../../tasks/alerts_details';
import { setStartDate } from '../../tasks/date_picker';
import { closeTimeline } from '../../tasks/timeline';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -33,7 +33,7 @@ describe('Alert Flyout', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
});
beforeEach(() => {

View file

@ -15,7 +15,7 @@ import {
import { expandFirstAlert } from '../../tasks/alerts';
import { openJsonView, openTable } from '../../tasks/alerts_details';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
@ -31,7 +31,7 @@ describe('Alert details with unmapped fields', () => {
cleanKibana();
esArchiverLoad('unmapped_fields');
login();
createCustomRuleEnabled(getUnmappedRule());
createRule(getUnmappedRule());
visitWithoutDateRange(ALERTS_URL);
waitForAlertsToPopulate();
expandFirstAlert();
@ -65,7 +65,7 @@ describe('Alert details with unmapped fields', () => {
};
openTable();
cy.get(ALERT_FLYOUT).find(tablePageSelector(4)).click({ force: true });
cy.get(ALERT_FLYOUT).find(tablePageSelector(6)).click({ force: true });
cy.get(ALERT_FLYOUT)
.find(TABLE_ROWS)
.last()

View file

@ -12,7 +12,7 @@ import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createCustomRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts';
const loadPageAsPlatformEngineerUser = (url: string) => {
@ -73,7 +73,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
createCustomRule(getNewRule());
createRule({ ...getNewRule(), rule_id: 'rule_testing' });
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@ -123,7 +123,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
createCustomRule(getNewRule());
createRule({ ...getNewRule(), rule_id: 'rule_testing' });
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@ -173,7 +173,7 @@ describe('Detections > Need Admin Callouts indicating an admin is needed to migr
context('On Rule Details page', () => {
beforeEach(() => {
createCustomRule(getNewRule());
createRule({ ...getNewRule(), rule_id: 'rule_testing' });
loadPageAsPlatformEngineerUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();

View file

@ -9,7 +9,7 @@ import { getBuildingBlockRule } from '../../objects/rule';
import { OVERVIEW_ALERTS_HISTOGRAM } from '../../screens/overview';
import { OVERVIEW } from '../../screens/security_header';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
@ -26,7 +26,7 @@ describe('Alerts generated by building block rules', () => {
login();
});
beforeEach(() => {
createCustomRuleEnabled(getBuildingBlockRule());
createRule(getBuildingBlockRule());
});
after(() => {
esArchiverUnload('auditbeat_big');

View file

@ -29,7 +29,7 @@ import {
openFirstAlert,
selectCountTable,
} from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
@ -48,7 +48,7 @@ describe('Changing alert status', () => {
});
context('Opening alerts', () => {
beforeEach(() => {
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
selectNumberOfAlerts(3);
@ -114,13 +114,13 @@ describe('Changing alert status', () => {
context('Marking alerts as acknowledged', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
selectCountTable();
clearGroupByTopInput();
});
it('Mark one alert as acknowledged when more than one open alerts are selected', () => {
it.skip('Mark one alert as acknowledged when more than one open alerts are selected', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
@ -154,7 +154,7 @@ describe('Changing alert status', () => {
context('Closing alerts', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule(), '1', 100);
createRule({ ...getNewRule(), rule_id: '1', max_signals: 100 });
visit(ALERTS_URL);
waitForAlertsToPopulate();
selectCountTable();
@ -223,7 +223,7 @@ describe('Changing alert status', () => {
});
});
it('Closes one alert when more than one opened alerts are selected', () => {
it.skip('Closes one alert when more than one opened alerts are selected', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {
@ -306,13 +306,13 @@ describe('Changing alert status', () => {
});
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
selectCountTable();
clearGroupByTopInput();
});
it('Mark one alert as acknowledged when more than one open alerts are selected', () => {
it.skip('Mark one alert as acknowledged when more than one open alerts are selected', () => {
cy.get(ALERTS_COUNT)
.invoke('text')
.then((alertNumberString) => {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { getNewThreatIndicatorRule } from '../../objects/rule';
import { getNewThreatIndicatorRule, indicatorRuleMatchingDoc } from '../../objects/rule';
import { cleanKibana } from '../../tasks/common';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -22,7 +22,7 @@ import {
import { TIMELINE_FIELD } from '../../screens/rule_details';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { expandFirstAlert, setEnrichmentDates, viewThreatIntelTab } from '../../tasks/alerts';
import { createCustomIndicatorRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_details';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
@ -34,7 +34,7 @@ describe('CTI Enrichment', () => {
esArchiverLoad('threat_indicator');
esArchiverLoad('suspicious_source_event');
login();
createCustomIndicatorRule(getNewThreatIndicatorRule());
createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true });
});
after(() => {
@ -49,11 +49,12 @@ describe('CTI Enrichment', () => {
it('Displays enrichment matched.* fields on the timeline', () => {
const expectedFields = {
'threat.enrichments.matched.atomic': getNewThreatIndicatorRule().atomic,
'threat.enrichments.matched.type': getNewThreatIndicatorRule().matchedType,
'threat.enrichments.matched.field': getNewThreatIndicatorRule().indicatorMappingField,
'threat.enrichments.matched.id': getNewThreatIndicatorRule().matchedId,
'threat.enrichments.matched.index': getNewThreatIndicatorRule().matchedIndex,
'threat.enrichments.matched.atomic': indicatorRuleMatchingDoc.atomic,
'threat.enrichments.matched.type': indicatorRuleMatchingDoc.matchedType,
'threat.enrichments.matched.field':
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
'threat.enrichments.matched.id': indicatorRuleMatchingDoc.matchedId,
'threat.enrichments.matched.index': indicatorRuleMatchingDoc.matchedIndex,
};
const fields = Object.keys(expectedFields) as Array<keyof typeof expectedFields>;

View file

@ -13,7 +13,7 @@ import {
OPTION_LIST_VALUES,
OPTION_SELECTABLE,
} from '../../screens/common/filter_group';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
import { ALERTS_URL } from '../../urls/navigation';
@ -60,7 +60,7 @@ describe.skip('Detections : Page Filters', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule(), 'custom_rule_filters');
createRule({ ...getNewRule(), rule_id: 'custom_rule_filters' });
});
beforeEach(() => {

View file

@ -17,7 +17,7 @@ import {
import { ENRICHED_DATA_ROW } from '../../screens/alerts_details';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import {
@ -45,7 +45,7 @@ describe('Enrichment', () => {
beforeEach(() => {
esArchiverLoad('risk_hosts');
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
visit(ALERTS_URL);
waitForAlertsToPopulate();
});

View file

@ -18,7 +18,7 @@ import {
investigateFirstAlertInTimeline,
scrollAlertTableColumnIntoView,
} from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visit } from '../../tasks/login';
@ -31,7 +31,7 @@ describe('Alerts timeline', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
});
beforeEach(() => {
visit(ALERTS_URL);

View file

@ -12,7 +12,7 @@ import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createCustomRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
import { getCallOut, waitForCallOutToBeShown, dismissCallOut } from '../../tasks/common/callouts';
const loadPageAsReadOnlyUser = (url: string) => {
@ -71,7 +71,7 @@ describe('Detections > Callouts', () => {
context('On Rule Details page', () => {
beforeEach(() => {
createCustomRule(getNewRule());
createRule(getNewRule());
loadPageAsReadOnlyUser(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();
@ -121,7 +121,7 @@ describe('Detections > Callouts', () => {
context('On Rule Details page', () => {
beforeEach(() => {
createCustomRule(getNewRule());
createRule(getNewRule());
loadPageAsPlatformEngineer(DETECTIONS_RULE_MANAGEMENT_URL);
waitForPageTitleToBeShown();
goToRuleDetails();

View file

@ -8,7 +8,7 @@
import { ANALYZER_NODE } from '../../screens/alerts';
import { openAnalyzerForFirstAlertInTimeline } from '../../tasks/alerts';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { getNewRule } from '../../objects/rule';
import { cleanKibana } from '../../tasks/common';
import { setStartDate } from '../../tasks/date_picker';
@ -21,7 +21,7 @@ describe('Analyze events view for alerts', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
});
beforeEach(() => {
visit(ALERTS_URL);

View file

@ -12,7 +12,7 @@ import {
USER_TABLE_ROW_TOTAL_ALERTS,
} from '../../screens/detection_response';
import { QUERY_TAB_BUTTON } from '../../screens/timeline';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { login, visit } from '../../tasks/login';
import { closeTimeline } from '../../tasks/timeline';
@ -25,7 +25,7 @@ describe.skip('Detection response view', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
visit(DETECTIONS_RESPONSE_URL);
});

View file

@ -14,7 +14,7 @@ import {
} from '../../screens/alerts_detection_rules';
import { VALUE_LISTS_MODAL_ACTIVATOR } from '../../screens/lists';
import { waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { dismissCallOut, getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -25,7 +25,7 @@ const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
describe('All rules - read only', () => {
before(() => {
cleanKibana();
createCustomRule(getNewRule(), '1');
createRule({ ...getNewRule(), rule_id: '1' });
login(ROLES.reader);
});

View file

@ -76,14 +76,7 @@ import { hasIndexPatterns, getDetails } from '../../tasks/rule_details';
import { login, visitWithoutDateRange } from '../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
import {
createCustomRule,
createMachineLearningRule,
createCustomIndicatorRule,
createEventCorrelationRule,
createThresholdRule,
createNewTermsRule,
} from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines';
import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common';
@ -95,7 +88,6 @@ import {
getMachineLearningRule,
getNewTermsRule,
} from '../../objects/rule';
import { getIndicatorMatchTimelineTemplate } from '../../objects/timeline';
import { esArchiverResetKibana } from '../../tasks/es_archiver';
import { getAvailablePrebuiltRulesCount } from '../../tasks/api_calls/prebuilt_rules';
@ -109,7 +101,6 @@ const prePopulatedTags = ['test-default-tag-1', 'test-default-tag-2'];
const expectedNumberOfCustomRulesToBeEdited = 6;
const expectedNumberOfMachineLearningRulesToBeEdited = 1;
const timelineTemplate = getIndicatorMatchTimelineTemplate();
/**
* total number of custom rules that are not Machine learning
*/
@ -117,12 +108,11 @@ const expectedNumberOfNotMLRules =
expectedNumberOfCustomRulesToBeEdited - expectedNumberOfMachineLearningRulesToBeEdited;
const numberOfRulesPerPage = 5;
const indexDataSource = { index: prePopulatedIndexPatterns, type: 'indexPatterns' } as const;
const defaultRuleData = {
dataSource: indexDataSource,
index: prePopulatedIndexPatterns,
tags: prePopulatedTags,
timeline: timelineTemplate,
timeline_title: 'Generic Threat Match Timeline',
timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e',
};
describe('Detection rules, bulk edit', () => {
@ -135,19 +125,17 @@ describe('Detection rules, bulk edit', () => {
resetRulesTableState();
deleteAlertsAndRules();
esArchiverResetKibana();
createCustomRule(
{
...getNewRule(),
name: RULE_NAME,
...defaultRuleData,
},
'1'
);
createEventCorrelationRule({ ...getEqlRule(), ...defaultRuleData }, '2');
createMachineLearningRule({ ...getMachineLearningRule(), ...defaultRuleData });
createCustomIndicatorRule({ ...getNewThreatIndicatorRule(), ...defaultRuleData }, '4');
createThresholdRule({ ...getNewThresholdRule(), ...defaultRuleData }, '5');
createNewTermsRule({ ...getNewTermsRule(), ...defaultRuleData }, '6');
createRule({
...getNewRule(),
name: RULE_NAME,
...defaultRuleData,
rule_id: '1',
});
createRule({ ...getEqlRule(), ...defaultRuleData, rule_id: '2' });
createRule({ ...getMachineLearningRule(), tags: ['test-default-tag-1', 'test-default-tag-2'] });
createRule({ ...getNewThreatIndicatorRule(), ...defaultRuleData, rule_id: '4' });
createRule({ ...getNewThresholdRule(), ...defaultRuleData, rule_id: '5' });
createRule({ ...getNewTermsRule(), ...defaultRuleData, rule_id: '6' });
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);

View file

@ -41,15 +41,7 @@ import { esArchiverResetKibana } from '../../tasks/es_archiver';
import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
import {
createMachineLearningRule,
createCustomIndicatorRule,
createEventCorrelationRule,
createThresholdRule,
createNewTermsRule,
createSavedQueryRule,
createCustomRuleEnabled,
} from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { createSlackConnector } from '../../tasks/api_calls/connectors';
import {
@ -93,23 +85,21 @@ describe('Detection rules, bulk edit of rule actions', () => {
},
];
createCustomRuleEnabled(
{
...getNewRule(),
name: ruleNameToAssert,
},
'1',
500,
actions
);
createRule({
...getNewRule(),
name: ruleNameToAssert,
rule_id: '1',
max_signals: 500,
actions,
});
});
createEventCorrelationRule(getEqlRule(), '2');
createMachineLearningRule(getMachineLearningRule(), '3');
createCustomIndicatorRule(getNewThreatIndicatorRule(), '4');
createThresholdRule(getNewThresholdRule(), '5');
createNewTermsRule(getNewTermsRule(), '6');
createSavedQueryRule({ ...getNewRule(), savedId: 'mocked' }, '7');
createRule({ ...getEqlRule(), rule_id: '2' });
createRule({ ...getMachineLearningRule(), rule_id: '3' });
createRule({ ...getNewThreatIndicatorRule(), rule_id: '4' });
createRule({ ...getNewThresholdRule(), rule_id: '5' });
createRule({ ...getNewTermsRule(), rule_id: '6' });
createRule({ ...getNewRule(), saved_id: 'mocked', rule_id: '7' });
createSlackConnector();
});

View file

@ -32,14 +32,7 @@ import { hasIndexPatterns, getDetails, assertDetailsNotExist } from '../../tasks
import { login, visitWithoutDateRange } from '../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
import {
createCustomRule,
createCustomIndicatorRule,
createEventCorrelationRule,
createThresholdRule,
createNewTermsRule,
createSavedQueryRule,
} from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../tasks/common';
import {
@ -58,12 +51,6 @@ const expectedIndexPatterns = ['index-1-*', 'index-2-*'];
const expectedNumberOfCustomRulesToBeEdited = 6;
const dataViewDataSource = { dataView: DATA_VIEW_ID, type: 'dataView' } as const;
const dataViewRuleData = {
dataSource: dataViewDataSource,
};
describe('Bulk editing index patterns of rules with a data view only', () => {
before(() => {
cleanKibana();
@ -75,12 +62,33 @@ describe('Bulk editing index patterns of rules with a data view only', () => {
postDataView(DATA_VIEW_ID);
createCustomRule({ ...getNewRule(), ...dataViewRuleData }, '1');
createEventCorrelationRule({ ...getEqlRule(), ...dataViewRuleData }, '2');
createCustomIndicatorRule({ ...getNewThreatIndicatorRule(), ...dataViewRuleData }, '3');
createThresholdRule({ ...getNewThresholdRule(), ...dataViewRuleData }, '4');
createNewTermsRule({ ...getNewTermsRule(), ...dataViewRuleData }, '5');
createSavedQueryRule({ ...getNewRule(), ...dataViewRuleData, savedId: 'mocked' }, '6');
createRule({ ...getNewRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' });
createRule({ ...getEqlRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '2' });
createRule({
...getNewThreatIndicatorRule(),
index: undefined,
data_view_id: DATA_VIEW_ID,
rule_id: '3',
});
createRule({
...getNewThresholdRule(),
index: undefined,
data_view_id: DATA_VIEW_ID,
rule_id: '4',
});
createRule({
...getNewTermsRule(),
index: undefined,
data_view_id: DATA_VIEW_ID,
rule_id: '5',
});
createRule({
...getNewRule(),
index: undefined,
data_view_id: DATA_VIEW_ID,
saved_id: 'mocked',
rule_id: '6',
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
@ -197,17 +205,12 @@ describe('Bulk editing index patterns of rules with index patterns and rules wit
postDataView(DATA_VIEW_ID);
createCustomRule({ ...getNewRule(), ...dataViewRuleData }, '1');
createCustomRule(
{
...getNewRule(),
dataSource: {
type: 'indexPatterns',
index: ['test-index-1-*'],
},
},
'2'
);
createRule({ ...getNewRule(), index: undefined, data_view_id: DATA_VIEW_ID, rule_id: '1' });
createRule({
...getNewRule(),
index: ['test-index-1-*'],
rule_id: '2',
});
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);

View file

@ -6,13 +6,7 @@
*/
import { ruleFields } from '../../data/detection_engine';
import {
getNewRule,
getExistingRule,
getIndexPatterns,
getEditedRule,
getNewOverrideRule,
} from '../../objects/rule';
import { getNewRule, getExistingRule, getEditedRule, getNewOverrideRule } from '../../objects/rule';
import { getTimeline } from '../../objects/timeline';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
@ -77,7 +71,7 @@ import {
goToRuleDetails,
selectNumberOfRules,
} from '../../tasks/alerts_detection_rules';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common';
import { addEmailConnectorAndRuleAction } from '../../tasks/common/rule_actions';
@ -240,9 +234,9 @@ describe('Custom query rules', () => {
context('Deletion', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule(), 'rule1');
createCustomRuleEnabled(getNewOverrideRule(), 'rule2');
createCustomRuleEnabled(getExistingRule(), 'rule3');
createRule({ ...getNewRule(), rule_id: 'rule1', enabled: true, max_signals: 500 });
createRule({ ...getNewOverrideRule(), rule_id: 'rule2', enabled: true, max_signals: 500 });
createRule({ ...getExistingRule(), rule_id: 'rule3', enabled: true });
visit(DETECTIONS_RULE_MANAGEMENT_URL);
});
@ -347,17 +341,12 @@ describe('Custom query rules', () => {
context('Edition', () => {
const rule = getEditedRule();
const expectedEditedtags = rule.tags?.join('');
const expectedEditedIndexPatterns =
rule.dataSource.type === 'indexPatterns' &&
rule.dataSource.index &&
rule.dataSource.index.length
? rule.dataSource.index
: getIndexPatterns();
const expectedEditedIndexPatterns = rule.index;
before(() => {
deleteAlertsAndRules();
deleteConnectors();
createCustomRuleEnabled(getExistingRule(), 'rule1');
createRule({ ...getExistingRule(), rule_id: 'rule1', enabled: true });
});
beforeEach(() => {
visit(DETECTIONS_RULE_MANAGEMENT_URL);
@ -373,7 +362,7 @@ describe('Custom query rules', () => {
cy.wait('@fetchRuleDetails').then(({ response }) => {
cy.wrap(response?.statusCode).should('eql', 200);
cy.wrap(response?.body.max_signals).should('eql', getExistingRule().maxSignals);
cy.wrap(response?.body.max_signals).should('eql', getExistingRule().max_signals);
cy.wrap(response?.body.enabled).should('eql', false);
});
});
@ -384,13 +373,9 @@ describe('Custom query rules', () => {
editFirstRule();
// expect define step to populate
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(''));
}
cy.get(CUSTOM_QUERY_INPUT).should('have.value', existingRule.query);
cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index?.join(''));
goToAboutStepTab();
@ -398,8 +383,8 @@ describe('Custom query rules', () => {
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);
cy.get(SEVERITY_DROPDOWN).should('have.text', 'High');
cy.get(DEFAULT_RISK_SCORE_INPUT).invoke('val').should('eql', `${existingRule.risk_score}`);
goToScheduleStepTab();
@ -433,14 +418,14 @@ 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', existingRule.maxSignals);
cy.wrap(response?.body.max_signals).should('eql', existingRule.max_signals);
});
cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getEditedRule().description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', getEditedRule().severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', getEditedRule().riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'Medium');
getDetails(RISK_SCORE_DETAILS).should('have.text', `${getEditedRule().risk_score}`);
getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags);
});
cy.get(INVESTIGATION_NOTES_TOGGLE).click({ force: true });
@ -450,7 +435,7 @@ describe('Custom query rules', () => {
'have.text',
expectedEditedIndexPatterns?.join('')
);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().customQuery);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', getEditedRule().query);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
});

View file

@ -5,10 +5,8 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getDataViewRule } from '../../objects/rule';
import type { CompleteTimeline } from '../../objects/timeline';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
import {
@ -53,7 +51,6 @@ import {
} 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,
@ -78,11 +75,11 @@ describe('Custom query rules', () => {
describe('Custom detection rules creation with data views', () => {
const rule = getDataViewRule();
const expectedUrls = rule.referenceUrls?.join('');
const expectedFalsePositives = rule.falsePositivesExamples?.join('');
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');
const expectedTags = rule.tags?.join('');
const mitreAttack = rule.mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const mitreAttack = rule.threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
const expectedNumberOfRules = 1;
beforeEach(() => {
@ -90,44 +87,34 @@ describe('Custom query rules', () => {
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. */
const timeline = rule.timeline as CompleteTimeline;
esArchiverResetKibana();
createTimeline(timeline).then((response) => {
cy.wrap({
...rule,
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
if (rule.dataSource.type === 'dataView') {
postDataView(rule.dataSource.dataView);
if (rule.data_view_id != null) {
postDataView(rule.data_view_id);
}
});
it('Creates and enables a new rule', function () {
visit(RULE_CREATION);
fillDefineCustomRuleAndContinue(this.rule);
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
cy.get(RULES_MANAGEMENT_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', rule.name);
cy.get(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'High');
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(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', this.rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'High');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -140,21 +127,19 @@ describe('Custom query rules', () => {
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(DATA_VIEW_DETAILS).should('have.text', rule.data_view_id);
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query);
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}`
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(
rule.from ?? 'now-6m',
rule.interval ?? '5m'
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();
@ -163,19 +148,17 @@ describe('Custom query rules', () => {
cy.get(NUMBER_OF_ALERTS)
.invoke('text')
.should('match', /^[1-9].+$/);
cy.get(ALERT_GRID_CELL).contains(this.rule.name);
cy.get(ALERT_GRID_CELL).contains(rule.name);
});
it('Creates and edits a new rule with a data view', function () {
visit(RULE_CREATION);
fillDefineCustomRuleAndContinue(this.rule);
cy.get(RULE_NAME_INPUT).clear({ force: true }).type(this.rule.name, { force: true });
cy.get(RULE_DESCRIPTION_INPUT)
.clear({ force: true })
.type(this.rule.description, { force: true });
fillDefineCustomRuleAndContinue(rule);
cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true });
cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true });
cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true });
fillScheduleRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(rule);
createRuleWithoutEnabling();
goToRuleDetails();

View file

@ -24,7 +24,6 @@ import {
} from '../../screens/rule_details';
import { goToRuleDetails, editFirstRule } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { createSavedQuery, deleteSavedQueries } from '../../tasks/api_calls/saved_queries';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
@ -39,10 +38,9 @@ import {
import { saveEditedRule } from '../../tasks/edit_rule';
import { login, visit } from '../../tasks/login';
import { getDetails } from '../../tasks/rule_details';
import { createSavedQueryRule, createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { RULE_CREATION, SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
import type { CompleteTimeline } from '../../objects/timeline';
const savedQueryName = 'custom saved query';
const savedQueryQuery = 'process.name: test';
@ -57,19 +55,10 @@ describe('Custom saved_query rules', () => {
beforeEach(() => {
deleteAlertsAndRules();
deleteSavedQueries();
const timeline = getNewRule().timeline as CompleteTimeline;
createTimeline(timeline).then((response) => {
cy.wrap({
...getNewRule(),
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
});
it('Creates saved query rule', function () {
const rule = getNewRule();
createSavedQuery(savedQueryName, savedQueryQuery, savedQueryFilterKey);
visit(RULE_CREATION);
@ -88,8 +77,8 @@ describe('Custom saved_query rules', () => {
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist');
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
cy.intercept('POST', '/api/detection_engine/rules').as('savedQueryRule');
createAndEnableRule();
@ -100,7 +89,7 @@ describe('Custom saved_query rules', () => {
goToRuleDetails();
cy.get(RULE_NAME_HEADER).should('contain', `${this.rule.name}`);
cy.get(RULE_NAME_HEADER).should('contain', `${rule.name}`);
cy.get(DEFINE_RULE_PANEL_PROGRESS).should('not.exist');
@ -112,7 +101,12 @@ describe('Custom saved_query rules', () => {
context('Non existent saved query', () => {
const FAILED_TO_LOAD_ERROR = 'Failed to load the saved query';
beforeEach(() => {
createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' });
createRule({
...getNewRule(),
type: 'saved_query',
saved_id: 'non-existent',
query: undefined,
});
cy.visit(SECURITY_DETECTIONS_RULES_URL);
});
it('Shows error toast on details page when saved query can not be loaded', function () {
@ -131,7 +125,7 @@ describe('Custom saved_query rules', () => {
context('Editing', () => {
it('Allows to update query rule as saved_query rule type', () => {
createSavedQuery(savedQueryName, savedQueryQuery);
createCustomRule(getNewRule());
createRule(getNewRule());
cy.visit(SECURITY_DETECTIONS_RULES_URL);
@ -158,7 +152,12 @@ describe('Custom saved_query rules', () => {
const expectedCustomTestQuery = 'random test query';
createSavedQuery(savedQueryName, savedQueryQuery).then((response) => {
cy.log(JSON.stringify(response.body, null, 2));
createSavedQueryRule({ ...getNewRule(), savedId: response.body.id });
createRule({
...getNewRule(),
type: 'saved_query',
saved_id: response.body.id,
query: undefined,
});
});
cy.visit(SECURITY_DETECTIONS_RULES_URL);
@ -185,7 +184,12 @@ describe('Custom saved_query rules', () => {
it('Allows to update saved_query rule with non-existent query by adding custom query', () => {
const expectedCustomTestQuery = 'random test query';
createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' });
createRule({
...getNewRule(),
type: 'saved_query',
saved_id: 'non-existent',
query: undefined,
});
cy.visit(SECURITY_DETECTIONS_RULES_URL);
@ -208,7 +212,12 @@ describe('Custom saved_query rules', () => {
it('Allows to update saved_query rule with non-existent query by selecting another saved query', () => {
createSavedQuery(savedQueryName, savedQueryQuery);
createSavedQueryRule({ ...getNewRule(), savedId: 'non-existent' });
createRule({
...getNewRule(),
type: 'saved_query',
saved_id: 'non-existent',
query: undefined,
});
cy.visit(SECURITY_DETECTIONS_RULES_URL);

View file

@ -5,8 +5,7 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getEqlRule, getEqlSequenceRule, getIndexPatterns } from '../../objects/rule';
import { ALERT_DATA_GRID, NUMBER_OF_ALERTS } from '../../screens/alerts';
@ -48,7 +47,6 @@ import {
goToRuleDetails,
goToTheRuleDetailsOf,
} from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
createAndEnableRule,
@ -63,7 +61,6 @@ import { login, visit } from '../../tasks/login';
import { RULE_CREATION } from '../../urls/navigation';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
import type { CompleteTimeline } from '../../objects/timeline';
describe('EQL rules', () => {
before(() => {
@ -72,51 +69,39 @@ describe('EQL rules', () => {
deleteAlertsAndRules();
});
describe('Detection rules, EQL', () => {
const expectedUrls = getEqlRule().referenceUrls?.join('');
const expectedFalsePositives = getEqlRule().falsePositivesExamples?.join('');
const expectedTags = getEqlRule().tags?.join('');
const mitreAttack = getEqlRule().mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const rule = getEqlRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');
const expectedTags = rule.tags?.join('');
const mitreAttack = rule.threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
const expectedNumberOfRules = 1;
const expectedNumberOfAlerts = '2 alerts';
beforeEach(() => {
const timeline = getEqlRule().timeline as CompleteTimeline;
createTimeline(timeline).then((response) => {
cy.wrap({
...getEqlRule(),
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
});
it('Creates and enables a new EQL rule', function () {
visit(RULE_CREATION);
selectEqlRuleType();
fillDefineEqlRuleAndContinue(this.rule);
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillDefineEqlRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
expectNumberOfRules(RULES_MANAGEMENT_TABLE, 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', rule.name);
cy.get(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'High');
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(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', this.rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'High');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -130,19 +115,17 @@ describe('EQL rules', () => {
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(CUSTOM_QUERY_DETAILS).should('have.text', rule.query);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
});
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${this.rule.runsEvery.interval}${this.rule.runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${this.rule.lookBack.interval}${this.rule.lookBack.type}`
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(
rule.from ?? 'now-6m',
rule.interval ?? '5m'
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();
@ -152,9 +135,9 @@ describe('EQL rules', () => {
cy.get(ALERT_DATA_GRID)
.invoke('text')
.then((text) => {
expect(text).contains(this.rule.name);
expect(text).contains(this.rule.severity.toLowerCase());
expect(text).contains(this.rule.riskScore);
expect(text).contains(rule.name);
expect(text).contains(rule.severity);
expect(text).contains(rule.risk_score);
});
});
});
@ -162,21 +145,11 @@ describe('EQL rules', () => {
describe('Detection rules, sequence EQL', () => {
const expectedNumberOfSequenceAlerts = '2 alerts';
const rule = getEqlSequenceRule();
before(() => {
esArchiverLoad('auditbeat_big');
});
beforeEach(() => {
const timeline = getEqlSequenceRule().timeline as CompleteTimeline;
createTimeline(timeline).then((response) => {
cy.wrap({
...getEqlSequenceRule(),
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
});
after(() => {
esArchiverUnload('auditbeat_big');
});
@ -184,11 +157,11 @@ describe('EQL rules', () => {
it('Creates and enables a new EQL rule with a sequence', function () {
visit(RULE_CREATION);
selectEqlRuleType();
fillDefineEqlRuleAndContinue(this.rule);
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillDefineEqlRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
goToTheRuleDetailsOf(this.rule.name);
goToTheRuleDetailsOf(rule.name);
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
@ -197,8 +170,8 @@ describe('EQL rules', () => {
.invoke('text')
.then((text) => {
cy.log('ALERT_DATA_GRID', text);
expect(text).contains(this.rule.name);
expect(text).contains(this.rule.severity.toLowerCase());
expect(text).contains(rule.name);
expect(text).contains(rule.severity);
});
});
});

View file

@ -23,7 +23,7 @@ import {
} from '../../tasks/alerts_detection_rules';
import { createExceptionList, deleteExceptionList } from '../../tasks/api_calls/exceptions';
import { getExceptionList } from '../../objects/exception';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -45,7 +45,7 @@ describe('Export rules', () => {
// Rules get exported via _bulk_action endpoint
cy.intercept('POST', '/api/detection_engine/rules/_bulk_action').as('bulk_action');
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
createCustomRule(getNewRule()).as('ruleResponse');
createRule(getNewRule()).as('ruleResponse');
});
it('Exports a custom rule', function () {
@ -104,21 +104,19 @@ describe('Export rules', () => {
deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type);
// create rule with exceptions
createExceptionList(exceptionList, exceptionList.list_id).then((response) =>
createCustomRule(
{
...getNewRule(),
name: 'rule with exceptions',
exceptionLists: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
},
'2'
)
createRule({
...getNewRule(),
name: 'rule with exceptions',
exceptions_list: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
rule_id: '2',
})
);
});

View file

@ -5,12 +5,12 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import {
getIndexPatterns,
getNewThreatIndicatorRule,
getThreatIndexPatterns,
indicatorRuleMatchingDoc,
} from '../../objects/rule';
import {
@ -65,7 +65,7 @@ import {
checkDuplicatedRule,
expectNumberOfRules,
} from '../../tasks/alerts_detection_rules';
import { createCustomIndicatorRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { loadPrepackagedTimelineTemplates } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
@ -106,15 +106,16 @@ import { login, visit, visitWithoutDateRange } from '../../tasks/login';
import { goBackToRulesTable, getDetails } from '../../tasks/rule_details';
import { DETECTIONS_RULE_MANAGEMENT_URL, RULE_CREATION } from '../../urls/navigation';
const DEFAULT_THREAT_MATCH_QUERY = '@timestamp >= "now-30d/d"';
describe('indicator match', () => {
describe('Detection rules, Indicator Match', () => {
const expectedUrls = getNewThreatIndicatorRule().referenceUrls?.join('');
const expectedFalsePositives = getNewThreatIndicatorRule().falsePositivesExamples?.join('');
const expectedUrls = getNewThreatIndicatorRule().references?.join('');
const expectedFalsePositives = getNewThreatIndicatorRule().false_positives?.join('');
const expectedTags = getNewThreatIndicatorRule().tags?.join('');
const mitreAttack = getNewThreatIndicatorRule().mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const mitreAttack = getNewThreatIndicatorRule().threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
const expectedNumberOfRules = 1;
const expectedNumberOfAlerts = '1 alert';
@ -212,8 +213,8 @@ describe('indicator match', () => {
const rule = getNewThreatIndicatorRule();
visitWithoutDateRange(RULE_CREATION);
selectIndicatorMatchType();
if (rule.dataSource.type === 'indexPatterns') {
fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern);
if (rule.index) {
fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index);
}
});
@ -238,8 +239,8 @@ describe('indicator match', () => {
it('Does NOT show invalidation text when there is a valid "index field" and a valid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getDefineContinueButton().click();
getIndicatorInvalidationText().should('not.exist');
@ -248,7 +249,7 @@ describe('indicator match', () => {
it('Shows invalidation text when there is an invalid "index field" and a valid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: 'non-existent-value',
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
validColumns: 'indicatorField',
});
getDefineContinueButton().click();
@ -257,7 +258,7 @@ describe('indicator match', () => {
it('Shows invalidation text when there is a valid "index field" and an invalid "indicator index field"', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
@ -267,21 +268,21 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows valid rows of "index fields" and valid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: 'agent.name',
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
validColumns: 'indicatorField',
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should('have.text', 'agent.name');
getIndicatorMappingComboField().should(
'have.text',
getNewThreatIndicatorRule().indicatorIndexField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].value
);
getIndicatorIndexComboField(2).should('not.exist');
getIndicatorMappingComboField(2).should('not.exist');
@ -289,14 +290,14 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows have valid "index fields" and invalid "indicator index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: 'non-existent-value',
validColumns: 'indexField',
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: 'second-non-existent-value',
validColumns: 'indexField',
});
@ -309,14 +310,14 @@ describe('indicator match', () => {
it('Deletes the first row when you have two rows. Both rows have valid "indicator index fields" and invalid "index fields". The second row should become the first row', () => {
fillIndicatorMatchRow({
indexField: 'non-existent-value',
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
validColumns: 'indicatorField',
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: 'second-non-existent-value',
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
validColumns: 'indicatorField',
});
getIndicatorDeleteButton().click();
@ -327,8 +328,8 @@ describe('indicator match', () => {
it('Deletes the first row of data but not the UI elements and the text defaults back to the placeholder of Search', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should('text', 'Search');
@ -339,8 +340,8 @@ describe('indicator match', () => {
it('Deletes the second row when you have three rows. The first row is valid data, the second row is invalid data, and the third row is valid data. Third row should shift up correctly', () => {
fillIndicatorMatchRow({
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getIndicatorAndButton().click();
fillIndicatorMatchRow({
@ -352,25 +353,25 @@ describe('indicator match', () => {
getIndicatorAndButton().click();
fillIndicatorMatchRow({
rowNumber: 3,
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getIndicatorDeleteButton(2).click();
getIndicatorIndexComboField(1).should(
'text',
getNewThreatIndicatorRule().indicatorMappingField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
);
getIndicatorMappingComboField(1).should(
'text',
getNewThreatIndicatorRule().indicatorIndexField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].value
);
getIndicatorIndexComboField(2).should(
'text',
getNewThreatIndicatorRule().indicatorMappingField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
);
getIndicatorMappingComboField(2).should(
'text',
getNewThreatIndicatorRule().indicatorIndexField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].value
);
getIndicatorIndexComboField(3).should('not.exist');
getIndicatorMappingComboField(3).should('not.exist');
@ -385,17 +386,17 @@ describe('indicator match', () => {
getIndicatorOrButton().click();
fillIndicatorMatchRow({
rowNumber: 2,
indexField: getNewThreatIndicatorRule().indicatorMappingField,
indicatorIndexField: getNewThreatIndicatorRule().indicatorIndexField,
indexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].field,
indicatorIndexField: getNewThreatIndicatorRule().threat_mapping[0].entries[0].value,
});
getIndicatorDeleteButton().click();
getIndicatorIndexComboField().should(
'text',
getNewThreatIndicatorRule().indicatorMappingField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
);
getIndicatorMappingComboField().should(
'text',
getNewThreatIndicatorRule().indicatorIndexField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].value
);
getIndicatorIndexComboField(2).should('not.exist');
getIndicatorMappingComboField(2).should('not.exist');
@ -436,8 +437,8 @@ describe('indicator match', () => {
expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules);
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(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'Critical');
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
@ -445,9 +446,9 @@ describe('indicator match', () => {
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', rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore);
getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threatIndicatorPath);
getDetails(SEVERITY_DETAILS).should('have.text', 'Critical');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(INDICATOR_PREFIX_OVERRIDE).should('have.text', rule.threat_indicator_path);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -461,32 +462,27 @@ describe('indicator match', () => {
cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN);
cy.get(DEFINITION_DETAILS).within(() => {
if (rule.dataSource.type === 'indexPatterns') {
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.dataSource.index?.join(''));
if (rule.index) {
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', rule.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',
rule.indicatorIndexPattern.join('')
);
getDetails(INDICATOR_INDEX_PATTERNS).should('have.text', rule.threat_index.join(''));
getDetails(INDICATOR_MAPPING).should(
'have.text',
`${rule.indicatorMappingField} MATCHES ${rule.indicatorIndexField}`
`${rule.threat_mapping[0].entries[0].field} MATCHES ${rule.threat_mapping[0].entries[0].value}`
);
getDetails(INDICATOR_INDEX_QUERY).should('have.text', '*:*');
});
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${rule.runsEvery?.interval}${rule.runsEvery?.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${rule.lookBack?.interval}${rule.lookBack?.type}`
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(
rule.from ?? 'now-6m',
rule.interval ?? '5m'
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();
@ -495,14 +491,14 @@ describe('indicator match', () => {
cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts);
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);
cy.get(ALERT_RISK_SCORE).first().should('have.text', rule.risk_score);
});
it('Investigate alert in timeline', () => {
const accessibilityText = `Press enter for options, or press space to begin dragging.`;
loadPrepackagedTimelineTemplates();
createCustomIndicatorRule(getNewThreatIndicatorRule());
createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true });
visit(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();
@ -512,18 +508,20 @@ describe('indicator match', () => {
cy.get(PROVIDER_BADGE).should(
'have.text',
`threat.enrichments.matched.atomic: "${
getNewThreatIndicatorRule().atomic
indicatorRuleMatchingDoc.atomic
}"threat.enrichments.matched.type: "indicator_match_rule"threat.enrichments.matched.field: "${
getNewThreatIndicatorRule().indicatorMappingField
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
}"`
);
cy.get(INDICATOR_MATCH_ROW_RENDER).should(
'have.text',
`threat.enrichments.matched.field${
getNewThreatIndicatorRule().indicatorMappingField
}${accessibilityText}matched${getNewThreatIndicatorRule().indicatorMappingField}${
getNewThreatIndicatorRule().atomic
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
}${accessibilityText}matched${
getNewThreatIndicatorRule().threat_mapping[0].entries[0].field
}${
indicatorRuleMatchingDoc.atomic
}${accessibilityText}threat.enrichments.matched.typeindicator_match_rule${accessibilityText}provided` +
` byfeed.nameAbuseCH malware${accessibilityText}`
);
@ -533,7 +531,7 @@ describe('indicator match', () => {
describe('Duplicates the indicator rule', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomIndicatorRule(getNewThreatIndicatorRule());
createRule({ ...getNewThreatIndicatorRule(), rule_id: 'rule_testing', enabled: true });
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
});

View file

@ -7,7 +7,7 @@
import { getNewRule } from '../../objects/rule';
import { RULES_MONITORING_TAB, RULE_NAME } from '../../screens/alerts_detection_rules';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import { login, visitWithoutDateRange } from '../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
@ -19,7 +19,7 @@ describe('Rules talbes links', () => {
});
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(getNewRule(), 'rule1');
createRule({ ...getNewRule(), rule_id: 'rule1' });
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
});

View file

@ -5,7 +5,9 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import { isArray } from 'lodash';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getMachineLearningRule } from '../../objects/rule';
import {
@ -53,10 +55,10 @@ import { login, visitWithoutDateRange } from '../../tasks/login';
import { RULE_CREATION } from '../../urls/navigation';
describe('Detection rules, machine learning', () => {
const expectedUrls = getMachineLearningRule().referenceUrls.join('');
const expectedFalsePositives = getMachineLearningRule().falsePositivesExamples.join('');
const expectedTags = getMachineLearningRule().tags.join('');
const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().mitre);
const expectedUrls = (getMachineLearningRule().references ?? []).join('');
const expectedFalsePositives = (getMachineLearningRule().false_positives ?? []).join('');
const expectedTags = (getMachineLearningRule().tags ?? []).join('');
const expectedMitre = formatMitreAttackDescription(getMachineLearningRule().threat ?? []);
const expectedNumberOfRules = 1;
before(() => {
@ -66,28 +68,29 @@ describe('Detection rules, machine learning', () => {
});
it('Creates and enables a new ml rule', () => {
const mlRule = getMachineLearningRule();
selectMachineLearningRuleType();
fillDefineMachineLearningRuleAndContinue(getMachineLearningRule());
fillAboutRuleAndContinue(getMachineLearningRule());
fillScheduleRuleAndContinue(getMachineLearningRule());
fillDefineMachineLearningRuleAndContinue(mlRule);
fillAboutRuleAndContinue(mlRule);
fillScheduleRuleAndContinue(mlRule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
expectNumberOfRules(RULES_MANAGEMENT_TABLE, expectedNumberOfRules);
cy.get(RULE_NAME).should('have.text', getMachineLearningRule().name);
cy.get(RISK_SCORE).should('have.text', getMachineLearningRule().riskScore);
cy.get(SEVERITY).should('have.text', getMachineLearningRule().severity);
cy.get(RULE_NAME).should('have.text', mlRule.name);
cy.get(RISK_SCORE).should('have.text', mlRule.risk_score);
cy.get(SEVERITY).should('have.text', 'Critical');
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
cy.get(RULE_NAME_HEADER).should('contain', `${getMachineLearningRule().name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', getMachineLearningRule().description);
cy.get(RULE_NAME_HEADER).should('contain', `${mlRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', mlRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', getMachineLearningRule().severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', getMachineLearningRule().riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'Critical');
getDetails(RISK_SCORE_DETAILS).should('have.text', mlRule.risk_score);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -98,31 +101,26 @@ describe('Detection rules, machine learning', () => {
getDetails(TAGS_DETAILS).should('have.text', expectedTags);
});
cy.get(DEFINITION_DETAILS).within(() => {
getDetails(ANOMALY_SCORE_DETAILS).should(
'have.text',
getMachineLearningRule().anomalyScoreThreshold
);
getDetails(ANOMALY_SCORE_DETAILS).should('have.text', mlRule.anomaly_threshold);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
const machineLearningJobsArray = isArray(mlRule.machine_learning_job_id)
? mlRule.machine_learning_job_id
: [mlRule.machine_learning_job_id];
// With the #1912 ML rule improvement changes we enable jobs on rule creation.
// Though, in cypress jobs enabling does not work reliably and job can be started or stopped.
// Thus, we disable next check till we fix the issue with enabling jobs in cypress.
// Relevant ticket: https://github.com/elastic/security-team/issues/5389
// cy.get(MACHINE_LEARNING_JOB_STATUS).should('have.text', 'StoppedStopped');
cy.get(MACHINE_LEARNING_JOB_ID).should(
'have.text',
getMachineLearningRule().machineLearningJobs.join('')
);
cy.get(MACHINE_LEARNING_JOB_ID).should('have.text', machineLearningJobsArray.join(''));
});
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${getMachineLearningRule().runsEvery.interval}${getMachineLearningRule().runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${getMachineLearningRule().lookBack.interval}${getMachineLearningRule().lookBack.type}`
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${mlRule.interval}`);
const humanizedDuration = getHumanizedDuration(
mlRule.from ?? 'now-6m',
mlRule.interval ?? '5m'
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
});
});

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { getNewTermsRule } from '../../objects/rule';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getIndexPatterns, getNewTermsRule } from '../../objects/rule';
import { ALERT_DATA_GRID } from '../../screens/alerts';
import {
@ -46,7 +45,6 @@ import {
import { getDetails } from '../../tasks/rule_details';
import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
createAndEnableRule,
@ -60,7 +58,6 @@ import {
import { login, visit } from '../../tasks/login';
import { RULE_CREATION } from '../../urls/navigation';
import type { CompleteTimeline } from '../../objects/timeline';
describe('New Terms rules', () => {
before(() => {
@ -68,51 +65,42 @@ describe('New Terms rules', () => {
login();
});
describe('Detection rules, New Terms', () => {
const expectedUrls = getNewTermsRule().referenceUrls?.join('');
const expectedFalsePositives = getNewTermsRule().falsePositivesExamples?.join('');
const expectedTags = getNewTermsRule().tags?.join('');
const mitreAttack = getNewTermsRule().mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const rule = getNewTermsRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');
const expectedTags = rule.tags?.join('');
const mitreAttack = rule.threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
const expectedNumberOfRules = 1;
beforeEach(() => {
const timeline = getNewTermsRule().timeline as CompleteTimeline;
deleteAlertsAndRules();
createTimeline(timeline).then((response) => {
cy.wrap({
...getNewTermsRule(),
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
});
it('Creates and enables a new terms rule', function () {
visit(RULE_CREATION);
selectNewTermsRuleType();
fillDefineNewTermsRuleAndContinue(this.rule);
fillAboutRuleAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillDefineNewTermsRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
expectNumberOfRules(RULES_MANAGEMENT_TABLE, 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', rule.name);
cy.get(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'High');
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(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', this.rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'High');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -125,22 +113,20 @@ describe('New Terms rules', () => {
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', 'auditbeat-*');
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'New Terms');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
getDetails(NEW_TERMS_FIELDS_DETAILS).should('have.text', 'host.name');
getDetails(NEW_TERMS_HISTORY_WINDOW_DETAILS).should('have.text', '51000h');
});
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${this.rule.runsEvery.interval}${this.rule.runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${this.rule.lookBack.interval}${this.rule.lookBack.type}`
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(
rule.from ?? 'now-6m',
rule.interval ?? '5m'
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();
@ -149,9 +135,9 @@ describe('New Terms rules', () => {
cy.get(ALERT_DATA_GRID)
.invoke('text')
.then((text) => {
expect(text).contains(this.rule.name);
expect(text).contains(this.rule.severity.toLowerCase());
expect(text).contains(this.rule.riskScore);
expect(text).contains(rule.name);
expect(text).contains(rule.severity);
expect(text).contains(rule.risk_score);
});
});
});

View file

@ -5,10 +5,8 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre, OverrideRule } from '../../objects/rule';
import { getNewOverrideRule, getSeveritiesOverride } from '../../objects/rule';
import type { CompleteTimeline } from '../../objects/timeline';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getIndexPatterns, getNewOverrideRule, getSeveritiesOverride } from '../../objects/rule';
import { NUMBER_OF_ALERTS, ALERT_GRID_CELL } from '../../screens/alerts';
@ -50,7 +48,6 @@ import {
} from '../../screens/rule_details';
import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana } from '../../tasks/common';
import {
createAndEnableRule,
@ -66,57 +63,46 @@ import { getDetails } from '../../tasks/rule_details';
import { RULE_CREATION } from '../../urls/navigation';
describe('Detection rules, override', () => {
const expectedUrls = getNewOverrideRule().referenceUrls?.join('');
const expectedFalsePositives = getNewOverrideRule().falsePositivesExamples?.join('');
const expectedTags = getNewOverrideRule().tags?.join('');
const mitreAttack = getNewOverrideRule().mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const rule = getNewOverrideRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');
const expectedTags = rule.tags?.join('');
const mitreAttack = rule.threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
before(() => {
cleanKibana();
login();
});
beforeEach(() => {
const timeline = getNewOverrideRule().timeline as CompleteTimeline;
createTimeline(timeline).then((response) => {
cy.wrap({
...getNewOverrideRule(),
timeline: {
...timeline,
id: response.body.data.persistTimeline.timeline.savedObjectId,
},
}).as('rule');
});
});
it('Creates and enables a new custom rule with override option', function () {
visitWithoutDateRange(RULE_CREATION);
fillDefineCustomRuleAndContinue(this.rule);
fillAboutRuleWithOverrideAndContinue(this.rule);
fillScheduleRuleAndContinue(this.rule);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleWithOverrideAndContinue(rule);
fillScheduleRuleAndContinue(rule);
createAndEnableRule();
cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)');
expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1);
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', rule.name);
cy.get(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'High');
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(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', this.rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', this.rule.riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'High');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(RISK_SCORE_OVERRIDE_DETAILS).should(
'have.text',
`${this.rule.riskOverride}kibana.alert.risk_score`
`${rule.risk_score_mapping?.[0].field}kibana.alert.risk_score`
);
getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', this.rule.nameOverride);
getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', rule.rule_name_override);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -125,16 +111,16 @@ describe('Detection rules, override', () => {
expect(removeExternalLinkText(mitre.text())).equal(expectedMitre);
});
getDetails(TAGS_DETAILS).should('have.text', expectedTags);
getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', this.rule.timestampOverride);
getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', rule.timestamp_override);
cy.contains(DETAILS_TITLE, 'Severity override')
.invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings
.then((severityOverrideIndex) => {
(this.rule as OverrideRule).severityOverride.forEach((severity, i) => {
rule.severity_mapping?.forEach((severity, i) => {
cy.get(DETAILS_DESCRIPTION)
.eq(severityOverrideIndex + i)
.should(
'have.text',
`${severity.sourceField}:${severity.sourceValue}${getSeveritiesOverride()[i]}`
`${severity.field}:${severity.value}${getSeveritiesOverride()[i]}`
);
});
});
@ -142,20 +128,15 @@ describe('Detection rules, override', () => {
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', 'auditbeat-*');
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', this.rule.customQuery);
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query);
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',
`${this.rule.runsEvery.interval}${this.rule.runsEvery.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${this.rule.lookBack.interval}${this.rule.lookBack.type}`
);
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(rule.from ?? 'now-6m', rule.interval ?? '5m');
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();

View file

@ -37,7 +37,7 @@ import {
expectFilterByEnabledRules,
} from '../../tasks/alerts_detection_rules';
import { RULES_MANAGEMENT_TABLE } from '../../screens/alerts_detection_rules';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import {
expectRowsPerPage,
expectTablePage,
@ -47,25 +47,27 @@ import {
sortByTableColumn,
} from '../../tasks/table_pagination';
function createRule(id: string, name: string, tags?: string[], enabled = false): void {
const rule = getNewRule();
rule.name = name;
rule.tags = tags;
rule.enabled = enabled;
createCustomRule(rule, id);
}
function createTestRules(): void {
createRule('1', 'test 1', ['tag-a']);
createRule('2', 'rule 1', ['tag-b']);
createRule('3', 'rule 2', ['tag-b']);
createRule('4', 'rule 3', ['tag-b', 'tag-c']);
createRule('5', 'rule 4', ['tag-b']);
createRule('6', 'rule 5', ['tag-b', 'tag-c']);
createRule('7', 'rule 6', ['tag-b']);
createRule('8', 'rule 7', ['tag-b'], true);
createRule({ ...getNewRule(), rule_id: '1', name: 'test 1', tags: ['tag-a'], enabled: false });
createRule({ ...getNewRule(), rule_id: '2', name: 'rule 1', tags: ['tag-b'], enabled: false });
createRule({ ...getNewRule(), rule_id: '3', name: 'rule 2', tags: ['tag-b'], enabled: false });
createRule({
...getNewRule(),
rule_id: '4',
name: 'rule 3',
tags: ['tag-b', 'tag-c'],
enabled: false,
});
createRule({ ...getNewRule(), rule_id: '5', name: 'rule 4', tags: ['tag-b'], enabled: false });
createRule({
...getNewRule(),
rule_id: '6',
name: 'rule 5',
tags: ['tag-b', 'tag-c'],
enabled: false,
});
createRule({ ...getNewRule(), rule_id: '7', name: 'rule 6', tags: ['tag-b'], enabled: false });
createRule({ ...getNewRule(), rule_id: '8', name: 'rule 7', tags: ['tag-b'], enabled: true });
}
function visitRulesTableWithState(urlTableState: Record<string, unknown>): void {

View file

@ -42,20 +42,18 @@ describe('Rule actions during detection rule creation', () => {
deleteDataView(indexConnector.index);
});
const rule = {
...getSimpleCustomQueryRule(),
actions: { throttle: 'rule', connectors: [indexConnector] },
};
const index = rule.actions.connectors[0].index;
const rule = getSimpleCustomQueryRule();
const actions = { throttle: 'rule', connectors: [indexConnector] };
const index = actions.connectors[0].index;
const initialNumberOfDocuments = 0;
const expectedJson = JSON.parse(rule.actions.connectors[0].document);
const expectedJson = JSON.parse(actions.connectors[0].document);
it('Indexes a new document after the index action is triggered ', function () {
visit(RULE_CREATION);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
fillRuleAction(rule);
fillRuleAction(actions);
createAndEnableRule();
goToRuleDetails();

View file

@ -26,7 +26,7 @@ import {
import { login, visit, visitWithoutDateRange } from '../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { getNewRule } from '../../objects/rule';
import { setRowsPerPageTo } from '../../tasks/table_pagination';
@ -38,7 +38,7 @@ describe('Alerts detection rules table auto-refresh', () => {
cleanKibana();
login();
for (let i = 1; i < 7; i += 1) {
createCustomRule({ ...getNewRule(), name: `Test rule ${i}` }, `${i}`);
createRule({ ...getNewRule(), name: `Test rule ${i}`, rule_id: `${i}` });
}
});

View file

@ -22,7 +22,7 @@ import {
import { login, visit } from '../../tasks/login';
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
import { createCustomRule } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import {
getExistingRule,
@ -37,10 +37,10 @@ describe('Alerts detection rules', () => {
before(() => {
cleanKibana();
login();
createCustomRule(getNewRule(), '1');
createCustomRule(getExistingRule(), '2');
createCustomRule(getNewOverrideRule(), '3');
createCustomRule(getNewThresholdRule(), '4');
createRule({ ...getNewRule(), rule_id: '1' });
createRule({ ...getExistingRule(), rule_id: '2' });
createRule({ ...getNewOverrideRule(), rule_id: '3' });
createRule({ ...getNewThresholdRule(), rule_id: '4' });
});
it('Sorts by enabled rules', () => {
@ -62,8 +62,8 @@ describe('Alerts detection rules', () => {
});
it('Pagination updates page number and results', () => {
createCustomRule({ ...getNewRule(), name: 'Test a rule' }, '5');
createCustomRule({ ...getNewRule(), name: 'Not same as first rule' }, '6');
createRule({ ...getNewRule(), name: 'Test a rule', rule_id: '5' });
createRule({ ...getNewRule(), name: 'Not same as first rule', rule_id: '6' });
visit(DETECTIONS_RULE_MANAGEMENT_URL);
waitForRulesTableToBeLoaded();

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import { formatMitreAttackDescription } from '../../helpers/rules';
import type { Mitre } from '../../objects/rule';
import { getNewThresholdRule } from '../../objects/rule';
import { formatMitreAttackDescription, getHumanizedDuration } from '../../helpers/rules';
import { getIndexPatterns, getNewThresholdRule } from '../../objects/rule';
import { ALERT_GRID_CELL, NUMBER_OF_ALERTS } from '../../screens/alerts';
@ -46,7 +45,6 @@ import {
import { getDetails } from '../../tasks/rule_details';
import { expectNumberOfRules, goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createTimeline } from '../../tasks/api_calls/timelines';
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
import {
createAndEnableRule,
@ -60,15 +58,14 @@ import {
import { login, visitWithoutDateRange } from '../../tasks/login';
import { RULE_CREATION } from '../../urls/navigation';
import type { CompleteTimeline } from '../../objects/timeline';
describe('Detection rules, threshold', () => {
let rule = getNewThresholdRule();
const expectedUrls = getNewThresholdRule().referenceUrls?.join('');
const expectedFalsePositives = getNewThresholdRule().falsePositivesExamples?.join('');
const expectedTags = getNewThresholdRule().tags?.join('');
const mitreAttack = getNewThresholdRule().mitre as Mitre[];
const expectedMitre = formatMitreAttackDescription(mitreAttack);
const rule = getNewThresholdRule();
const expectedUrls = rule.references?.join('');
const expectedFalsePositives = rule.false_positives?.join('');
const expectedTags = rule.tags?.join('');
const mitreAttack = rule.threat;
const expectedMitre = formatMitreAttackDescription(mitreAttack ?? []);
before(() => {
cleanKibana();
@ -76,12 +73,7 @@ describe('Detection rules, threshold', () => {
});
beforeEach(() => {
rule = getNewThresholdRule();
const timeline = rule.timeline as CompleteTimeline;
deleteAlertsAndRules();
createTimeline(timeline).then((response) => {
timeline.id = response.body.data.persistTimeline.timeline.savedObjectId;
});
visitWithoutDateRange(RULE_CREATION);
});
@ -97,8 +89,8 @@ describe('Detection rules, threshold', () => {
expectNumberOfRules(RULES_MANAGEMENT_TABLE, 1);
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(RISK_SCORE).should('have.text', rule.risk_score);
cy.get(SEVERITY).should('have.text', 'High');
cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true');
goToRuleDetails();
@ -106,8 +98,8 @@ describe('Detection rules, threshold', () => {
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', rule.severity);
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.riskScore);
getDetails(SEVERITY_DETAILS).should('have.text', 'High');
getDetails(RISK_SCORE_DETAILS).should('have.text', rule.risk_score);
getDetails(REFERENCE_URLS_DETAILS).should((details) => {
expect(removeExternalLinkText(details.text())).equal(expectedUrls);
});
@ -120,24 +112,19 @@ describe('Detection rules, threshold', () => {
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', 'auditbeat-*');
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.customQuery);
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', getIndexPatterns().join(''));
getDetails(CUSTOM_QUERY_DETAILS).should('have.text', rule.query);
getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold');
getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None');
getDetails(THRESHOLD_DETAILS).should(
'have.text',
`Results aggregated by ${rule.thresholdField} >= ${rule.threshold}`
`Results aggregated by ${rule.threshold.field} >= ${rule.threshold.value}`
);
});
cy.get(SCHEDULE_DETAILS).within(() => {
getDetails(RUNS_EVERY_DETAILS).should(
'have.text',
`${rule.runsEvery?.interval}${rule.runsEvery?.type}`
);
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should(
'have.text',
`${rule.lookBack?.interval}${rule.lookBack?.type}`
);
getDetails(RUNS_EVERY_DETAILS).should('have.text', `${rule.interval}`);
const humanizedDuration = getHumanizedDuration(rule.from ?? 'now-6m', rule.interval ?? '5m');
getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should('have.text', `${humanizedDuration}`);
});
waitForTheRuleToBeExecuted();

View file

@ -9,7 +9,7 @@ import { getNewRule } from '../../../objects/rule';
import { RULE_STATUS } from '../../../screens/create_new_rule';
import { createCustomRule } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
esArchiverLoad,
@ -72,10 +72,11 @@ describe('Exceptions flyout', () => {
esArchiverLoad('exceptions');
login();
createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) =>
createCustomRule({
createRule({
...getNewRule(),
dataSource: { index: ['auditbeat-*', 'exceptions-*'], type: 'indexPatterns' },
exceptionLists: [
index: ['auditbeat-*', 'exceptions-*'],
enabled: false,
exceptions_list: [
{
id: response.body.id,
list_id: getExceptionList().list_id,

View file

@ -9,7 +9,7 @@ import { ROLES } from '../../../../common/test';
import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
import { createCustomRule } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../urls/navigation';
@ -48,9 +48,9 @@ describe('Exceptions Table', () => {
// Create exception list associated with a rule
createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) =>
createCustomRule({
createRule({
...getNewRule(),
exceptionLists: [
exceptions_list: [
{
id: response.body.id,
list_id: getExceptionList2().list_id,

View file

@ -11,7 +11,7 @@ import {
} from '../../../tasks/es_archiver';
import { getNewRule } from '../../../objects/rule';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { createCustomRule, deleteCustomRule } from '../../../tasks/api_calls/rules';
import { createRule, deleteCustomRule } from '../../../tasks/api_calls/rules';
import { editException, editExceptionFlyoutItemName } from '../../../tasks/exceptions';
import { EXCEPTIONS_URL } from '../../../urls/navigation';
@ -28,7 +28,7 @@ describe('Add/edit exception from exception management page', () => {
esArchiverLoad('exceptions');
login();
visitWithoutDateRange(EXCEPTIONS_URL);
createCustomRule(getNewRule());
createRule(getNewRule());
});
after(() => {

View file

@ -9,7 +9,7 @@ import { ROLES } from '../../../../common/test';
import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
import { createCustomRule } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../urls/navigation';
@ -48,9 +48,9 @@ describe('Exceptions Table', () => {
// Create exception list associated with a rule
createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) =>
createCustomRule({
createRule({
...getNewRule(),
exceptionLists: [
exceptions_list: [
{
id: response.body.id,
list_id: getExceptionList2().list_id,

View file

@ -7,7 +7,7 @@
import { getNewRule } from '../../../objects/rule';
import { createCustomRule } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
esArchiverLoad,
@ -59,22 +59,20 @@ describe('Add endpoint exception from rule details', () => {
deleteAlertsAndRules();
// create rule with exception
createEndpointExceptionList().then((response) => {
createCustomRule(
{
...getNewRule(),
customQuery: 'event.code:*',
dataSource: { index: ['auditbeat*'], type: 'indexPatterns' },
exceptionLists: [
{
id: response.body.id,
list_id: response.body.list_id,
type: response.body.type,
namespace_type: response.body.namespace_type,
},
],
},
'2'
);
createRule({
...getNewRule(),
query: 'event.code:*',
index: ['auditbeat*'],
exceptions_list: [
{
id: response.body.id,
list_id: response.body.list_id,
type: response.body.type,
namespace_type: response.body.namespace_type,
},
],
rule_id: '2',
});
});
});

View file

@ -9,7 +9,7 @@ import { getException, getExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../../screens/alerts';
import { createCustomRule, createCustomRuleEnabled } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
goToClosedAlertsOnRuleDetailsPage,
@ -86,22 +86,20 @@ describe('Add/edit exception from rule details', () => {
deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type);
// create rule with exceptions
createExceptionList(exceptionList, exceptionList.list_id).then((response) => {
createCustomRule(
{
...getNewRule(),
customQuery: 'agent.name:*',
dataSource: { index: ['exceptions*'], type: 'indexPatterns' },
exceptionLists: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
},
'2'
);
createRule({
...getNewRule(),
query: 'agent.name:*',
index: ['exceptions*'],
exceptions_list: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
rule_id: '2',
});
createExceptionListItem(exceptionList.list_id, {
list_id: exceptionList.list_id,
item_id: 'simple_list_item',
@ -251,19 +249,13 @@ describe('Add/edit exception from rule details', () => {
describe('rule without existing exceptions', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(
{
...getNewRule(),
customQuery: 'agent.name:*',
dataSource: { index: ['exceptions*'], type: 'indexPatterns' },
runsEvery: {
interval: '10',
timeType: 'Seconds',
type: 's',
},
},
'rule_testing'
);
createRule({
...getNewRule(),
query: 'agent.name:*',
index: ['exceptions*'],
interval: '10s',
rule_id: 'rule_testing',
});
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
goToExceptionsTab();

View file

@ -8,7 +8,7 @@
import { LOADING_INDICATOR } from '../../../screens/security_header';
import { getNewRule } from '../../../objects/rule';
import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../../screens/alerts';
import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
import {
addExceptionFromFirstAlert,
@ -69,19 +69,13 @@ describe('Add exception using data views from rule details', () => {
beforeEach(() => {
deleteAlertsAndRules();
createCustomRuleEnabled(
{
...getNewRule(),
customQuery: 'agent.name:*',
dataSource: { dataView: 'exceptions-*', type: 'dataView' },
runsEvery: {
interval: '10',
timeType: 'Seconds',
type: 's',
},
},
'rule_testing'
);
createRule({
...getNewRule(),
query: 'agent.name:*',
data_view_id: 'exceptions-*',
interval: '10s',
rule_id: 'rule_testing',
});
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
goToRuleDetails();
waitForAlertsToPopulate();

View file

@ -8,7 +8,7 @@
import { getExceptionList } from '../../../objects/exception';
import { getNewRule } from '../../../objects/rule';
import { ROLES } from '../../../../common/test';
import { createCustomRule } from '../../../tasks/api_calls/rules';
import { createRule } from '../../../tasks/api_calls/rules';
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
import { login, visitWithoutDateRange } from '../../../tasks/login';
import { goToExceptionsTab, goToAlertsTab } from '../../../tasks/rule_details';
@ -35,22 +35,20 @@ describe('Exceptions viewer read only', () => {
esArchiverResetKibana();
// create rule with exceptions
createExceptionList(exceptionList, exceptionList.list_id).then((response) => {
createCustomRule(
{
...getNewRule(),
customQuery: 'agent.name:*',
dataSource: { index: ['exceptions*'], type: 'indexPatterns' },
exceptionLists: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
},
'2'
);
createRule({
...getNewRule(),
query: 'agent.name:*',
index: ['exceptions*'],
exceptions_list: [
{
id: response.body.id,
list_id: exceptionList.list_id,
type: exceptionList.type,
namespace_type: exceptionList.namespace_type,
},
],
rule_id: '2',
});
});
login(ROLES.reader);

View file

@ -20,7 +20,7 @@ import {
startTour,
} from '../../tasks/guided_onboarding';
import { cleanKibana } from '../../tasks/common';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { getNewRule } from '../../objects/rule';
import { ALERTS_URL, DASHBOARDS_URL } from '../../urls/navigation';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
@ -32,7 +32,7 @@ describe('Guided onboarding tour', () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled({ ...getNewRule(), customQuery: 'user.name:*' });
createRule({ ...getNewRule(), query: 'user.name:*' });
});
beforeEach(() => {
startAlertsCasesTour();

View file

@ -8,7 +8,7 @@
import { getNewRule } from '../../objects/rule';
import { SELECTED_ALERTS } from '../../screens/alerts';
import { SERVER_SIDE_EVENT_COUNT } from '../../screens/timeline';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import {
bulkInvestigateSelectedEventsInTimeline,
@ -57,7 +57,7 @@ describe('Bulk Investigate in Timeline', () => {
context('Alerts', () => {
before(() => {
createCustomRuleEnabled(getNewRule());
createRule(getNewRule());
});
beforeEach(() => {

View file

@ -6,7 +6,7 @@
*/
import { ALERT_FLYOUT } from '../../screens/alerts_details';
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
import { createRule } from '../../tasks/api_calls/rules';
import { cleanKibana } from '../../tasks/common';
import { waitForAlertsToPopulate } from '../../tasks/create_new_rule';
import { login, visitWithoutDateRange } from '../../tasks/login';
@ -28,7 +28,7 @@ describe('user details flyout', () => {
it('shows user detail flyout from alert table', () => {
visitWithoutDateRange(ALERTS_URL);
createCustomRuleEnabled({ ...getNewRule(), customQuery: 'user.name:*' });
createRule({ ...getNewRule(), query: 'user.name:*' });
refreshPage();
waitForAlertsToPopulate();

View file

@ -5,18 +5,30 @@
* 2.0.
*/
import type { Mitre } from '../objects/rule';
import dateMath from '@kbn/datemath';
import moment from 'moment';
export const formatMitreAttackDescription = (mitre: Mitre[]) => {
import type { ThreatArray } from '../../common/detection_engine/rule_schema';
export const formatMitreAttackDescription = (mitre: ThreatArray) => {
return mitre
.map(
(threat) =>
threat.tactic +
threat.techniques
.map((technique) => {
return technique.name + technique.subtechniques.join('');
})
.join('')
`${threat.tactic.name} (${threat.tactic.id})${
threat.technique
? threat.technique
.map((technique) => {
return `${technique.name} (${technique.id})${
technique.subtechnique
? technique.subtechnique
.map((subtechnique) => `${subtechnique.name} (${subtechnique.id})`)
.join('')
: ''
}`;
})
.join('')
: ''
}`
)
.join('');
};
@ -35,3 +47,33 @@ export const elementsOverlap = ($element1: JQuery<HTMLElement>, $element2: JQuer
return true;
}
};
export const getHumanizedDuration = (from: string, interval: string): string => {
const fromValue = dateMath.parse(from) ?? moment();
const intervalValue = dateMath.parse(`now-${interval}`) ?? moment();
const fromDuration = moment.duration(intervalValue.diff(fromValue));
// Basing calculations off floored seconds count as moment durations weren't precise
const intervalDuration = Math.floor(fromDuration.asSeconds());
// For consistency of display value
if (intervalDuration === 0) {
return `0s`;
}
if (intervalDuration % 3600 === 0) {
return `${intervalDuration / 3600}h`;
} else if (intervalDuration % 60 === 0) {
return `${intervalDuration / 60}m`;
} else {
return `${intervalDuration}s`;
}
};
export const convertHistoryStartToSize = (relativeTime: string) => {
if (relativeTime.startsWith('now-')) {
return relativeTime.substring(4);
} else {
return relativeTime;
}
};

View file

@ -5,120 +5,30 @@
* 2.0.
*/
import type { RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types';
import type {
RuleActionThrottle,
SeverityMappingItem,
Threat,
} from '@kbn/securitysolution-io-ts-alerting-types';
import { getMockThreatData } from '../../public/detections/mitre/mitre_tactics_techniques';
import type { CompleteTimeline } from './timeline';
import { getTimeline, getIndicatorMatchTimelineTemplate } from './timeline';
import type { RuleResponse } from '../../common/detection_engine/rule_schema';
import type {
EqlRuleCreateProps,
MachineLearningRuleCreateProps,
NewTermsRuleCreateProps,
QueryRuleCreateProps,
RuleResponse,
ThreatMatchRuleCreateProps,
ThresholdRuleCreateProps,
} from '../../common/detection_engine/rule_schema';
import type { Connectors } from './connector';
const ccsRemoteName: string = Cypress.env('CCS_REMOTE_NAME');
interface MitreAttackTechnique {
name: string;
subtechniques: string[];
}
export interface Mitre {
tactic: string;
techniques: MitreAttackTechnique[];
}
interface SeverityOverride {
sourceField: string;
sourceValue: string;
}
interface Interval {
interval: string;
timeType: string;
type: string;
}
export interface Actions {
throttle: RuleActionThrottle;
connectors: Connectors[];
}
export type RuleDataSource =
| { type: 'indexPatterns'; index: string[] }
| { type: 'dataView'; dataView: string };
export interface CustomRule {
customQuery?: string;
name: string;
description: string;
dataSource: RuleDataSource;
severity?: string;
riskScore?: string;
tags?: string[];
timelineTemplate?: string;
referenceUrls?: string[];
falsePositivesExamples?: string[];
mitre?: Mitre[];
note?: string;
runsEvery?: Interval;
interval?: string;
lookBack?: Interval;
timeline?: CompleteTimeline;
maxSignals?: number;
buildingBlockType?: string;
exceptionLists?: Array<{ id: string; list_id: string; type: string; namespace_type: string }>;
actions?: Actions;
enabled?: boolean;
}
export interface ThresholdRule extends CustomRule {
thresholdField: string;
threshold: string;
}
export interface SavedQueryRule extends CustomRule {
savedId: string;
}
export interface OverrideRule extends CustomRule {
severityOverride: SeverityOverride[];
riskOverride: string;
nameOverride: string;
timestampOverride: string;
}
export interface ThreatIndicatorRule extends CustomRule {
indicatorIndexPattern: string[];
indicatorMappingField: string;
indicatorIndexField: string;
threatIndicatorPath: string;
type?: string;
atomic?: string;
matchedType?: string;
matchedId?: string;
matchedIndex?: string;
}
export interface NewTermsRule extends CustomRule {
newTermsFields: string[];
historyWindowSize: Interval;
}
export interface MachineLearningRule {
machineLearningJobs: string[];
anomalyScoreThreshold: number;
name: string;
description: string;
severity: string;
riskScore: string;
tags: string[];
timelineTemplate?: string;
referenceUrls: string[];
falsePositivesExamples: string[];
mitre: Mitre[];
note: string;
runsEvery: Interval;
lookBack: Interval;
interval?: string;
}
export const getIndexPatterns = (): string[] => [
'apm-*-transaction*',
'auditbeat-*',
@ -133,368 +43,408 @@ export const getIndexPatterns = (): string[] => [
export const getThreatIndexPatterns = (): string[] => ['logs-ti_*'];
const getMitre1 = (): Mitre => ({
tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`,
techniques: [
const getMitre1 = (): Threat => ({
framework: 'MITRE ATT&CK',
tactic: {
name: getMockThreatData().tactic.name,
id: getMockThreatData().tactic.id,
reference: getMockThreatData().tactic.reference,
},
technique: [
{
name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
subtechniques: [
`${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`,
id: getMockThreatData().technique.id,
reference: getMockThreatData().technique.reference,
name: getMockThreatData().technique.name,
subtechnique: [
{
id: getMockThreatData().subtechnique.id,
name: getMockThreatData().subtechnique.name,
reference: getMockThreatData().subtechnique.reference,
},
],
},
{
name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
subtechniques: [],
name: getMockThreatData().technique.name,
id: getMockThreatData().technique.id,
reference: getMockThreatData().technique.reference,
subtechnique: [],
},
],
});
const getMitre2 = (): Mitre => ({
tactic: `${getMockThreatData().tactic.name} (${getMockThreatData().tactic.id})`,
techniques: [
const getMitre2 = (): Threat => ({
framework: 'MITRE ATT&CK',
tactic: {
name: getMockThreatData().tactic.name,
id: getMockThreatData().tactic.id,
reference: getMockThreatData().tactic.reference,
},
technique: [
{
name: `${getMockThreatData().technique.name} (${getMockThreatData().technique.id})`,
subtechniques: [
`${getMockThreatData().subtechnique.name} (${getMockThreatData().subtechnique.id})`,
id: getMockThreatData().technique.id,
reference: getMockThreatData().technique.reference,
name: getMockThreatData().technique.name,
subtechnique: [
{
id: getMockThreatData().subtechnique.id,
name: getMockThreatData().subtechnique.name,
reference: getMockThreatData().subtechnique.reference,
},
],
},
],
});
const getSeverityOverride1 = (): SeverityOverride => ({
sourceField: 'host.name',
sourceValue: 'host',
const getSeverityOverride1 = (): SeverityMappingItem => ({
field: 'host.name',
value: 'host',
operator: 'equals',
severity: 'low',
});
const getSeverityOverride2 = (): SeverityOverride => ({
sourceField: '@timestamp',
sourceValue: '10/02/2020',
const getSeverityOverride2 = (): SeverityMappingItem => ({
field: '@timestamp',
value: '10/02/2020',
operator: 'equals',
severity: 'medium',
});
const getSeverityOverride3 = (): SeverityOverride => ({
sourceField: 'host.geo.name',
sourceValue: 'atack',
const getSeverityOverride3 = (): SeverityMappingItem => ({
field: 'host.geo.name',
value: 'atack',
operator: 'equals',
severity: 'high',
});
const getSeverityOverride4 = (): SeverityOverride => ({
sourceField: 'agent.type',
sourceValue: 'auditbeat',
const getSeverityOverride4 = (): SeverityMappingItem => ({
field: 'agent.type',
value: 'auditbeat',
operator: 'equals',
severity: 'critical',
});
const getRunsEvery = (): Interval => ({
interval: '100',
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' },
export const getDataViewRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
data_view_id: 'auditbeat-2022',
name: 'New Data View Rule',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getNewRule = (): CustomRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getNewRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'New Rule Test',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getSimpleCustomQueryRule = (): CustomRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getSimpleCustomQueryRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'New Rule Test',
description: 'The new rule description.',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
interval: '100m',
from: 'now-50000h',
severity: 'low',
risk_score: 21,
});
export const getBuildingBlockRule = (): CustomRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getBuildingBlockRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'Building Block Rule Test',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
buildingBlockType: 'default',
interval: '100m',
from: 'now-50000h',
max_signals: 100,
building_block_type: 'default',
});
export const getUnmappedRule = (): CustomRule => ({
customQuery: '*:*',
dataSource: { index: ['unmapped*'], type: 'indexPatterns' },
export const getUnmappedRule = (): QueryRuleCreateProps => ({
type: 'query',
query: '*:*',
index: ['unmapped*'],
name: 'Rule with unmapped fields',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getUnmappedCCSRule = (): CustomRule => ({
customQuery: '*:*',
dataSource: { index: [`${ccsRemoteName}:unmapped*`], type: 'indexPatterns' },
export const getUnmappedCCSRule = (): QueryRuleCreateProps => ({
type: 'query',
query: '*:*',
index: [`${ccsRemoteName}:unmapped*`],
name: 'Rule with unmapped fields',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getExistingRule = (): CustomRule => ({
customQuery: 'host.name: *',
export const getExistingRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
name: 'Rule 1',
description: 'Description for Rule 1',
dataSource: { index: ['auditbeat-*'], type: 'indexPatterns' },
severity: 'High',
riskScore: '19',
index: ['auditbeat-*'],
severity: 'high',
risk_score: 19,
tags: ['rule1'],
referenceUrls: [],
falsePositivesExamples: [],
mitre: [],
references: [],
false_positives: [],
threat: [],
note: 'This is my note',
runsEvery: getRunsEvery(),
interval: '100m',
lookBack: getLookBack(),
timeline: getTimeline(),
from: 'now-50000h',
// Please do not change, or if you do, needs
// to be any number other than default value
maxSignals: 500,
max_signals: 500,
});
export const getNewOverrideRule = (): OverrideRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getNewOverrideRule = (): QueryRuleCreateProps => ({
type: 'query',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'Override Rule',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
severityOverride: [
severity_mapping: [
getSeverityOverride1(),
getSeverityOverride2(),
getSeverityOverride3(),
getSeverityOverride4(),
],
riskOverride: 'destination.port',
nameOverride: 'agent.type',
timestampOverride: '@timestamp',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
risk_score_mapping: [
{ field: 'destination.port', value: '', operator: 'equals', risk_score: undefined },
],
rule_name_override: 'agent.type',
timestamp_override: '@timestamp',
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getNewThresholdRule = (): ThresholdRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getNewThresholdRule = (): ThresholdRuleCreateProps => ({
type: 'threshold',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'Threshold Rule',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
thresholdField: 'host.name',
threshold: '1',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
threshold: {
field: 'host.name',
value: 1,
},
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getNewTermsRule = (): NewTermsRule => ({
customQuery: 'host.name: *',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
export const getNewTermsRule = (): NewTermsRuleCreateProps => ({
type: 'new_terms',
query: 'host.name: *',
index: getIndexPatterns(),
name: 'New Terms Rule',
description: 'The new rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
newTermsFields: ['host.name'],
historyWindowSize: {
// historyWindowSize needs to be larger than the rule's lookback value
interval: '51000',
timeType: 'Hours',
type: 'h',
},
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
new_terms_fields: ['host.name'],
history_window_start: 'now-51000h',
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getMachineLearningRule = (): MachineLearningRule => ({
machineLearningJobs: [
export const getMachineLearningRule = (): MachineLearningRuleCreateProps => ({
type: 'machine_learning',
machine_learning_job_id: [
'Unusual Linux Network Activity',
'Anomalous Process for a Linux Population',
],
anomalyScoreThreshold: 20,
anomaly_threshold: 20,
name: 'New ML Rule Test',
description: 'The new ML rule description.',
severity: 'Critical',
riskScore: '70',
severity: 'critical',
risk_score: 70,
tags: ['ML'],
referenceUrls: ['https://elastic.co/'],
falsePositivesExamples: ['False1'],
mitre: [getMitre1()],
references: ['https://elastic.co/'],
false_positives: ['False1'],
threat: [getMitre1()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
interval: '100m',
from: 'now-50000h',
});
export const getEqlRule = (): CustomRule => ({
customQuery: 'any where process.name == "zsh"',
export const getEqlRule = (): EqlRuleCreateProps => ({
type: 'eql',
language: 'eql',
query: 'any where process.name == "zsh"',
name: 'New EQL Rule',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
index: getIndexPatterns(),
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getCCSEqlRule = (): CustomRule => ({
customQuery: 'any where process.name == "run-parts"',
export const getCCSEqlRule = (): EqlRuleCreateProps => ({
type: 'eql',
language: 'eql',
query: 'any where process.name == "run-parts"',
name: 'New EQL Rule',
dataSource: { index: [`${ccsRemoteName}:run-parts`], type: 'indexPatterns' },
index: [`${ccsRemoteName}:run-parts`],
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getEqlSequenceRule = (): CustomRule => ({
customQuery:
export const getEqlSequenceRule = (): EqlRuleCreateProps => ({
type: 'eql',
language: 'eql',
query:
'sequence with maxspan=30s\
[any where agent.name == "test.local"]\
[any where host.name == "test.local"]',
name: 'New EQL Sequence Rule',
dataSource: { index: getIndexPatterns(), type: 'indexPatterns' },
index: getIndexPatterns(),
description: 'New EQL rule description.',
severity: 'High',
riskScore: '17',
severity: 'high',
risk_score: 17,
tags: ['test', 'newRule'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
timeline: getTimeline(),
maxSignals: 100,
interval: '100m',
from: 'now-50000h',
max_signals: 100,
});
export const getNewThreatIndicatorRule = (): ThreatIndicatorRule => ({
export const getNewThreatIndicatorRule = (): ThreatMatchRuleCreateProps => ({
type: 'threat_match',
name: 'Threat Indicator Rule Test',
description: 'The threat indicator rule description.',
dataSource: { index: ['suspicious-*'], type: 'indexPatterns' },
severity: 'Critical',
riskScore: '20',
query: '*:*',
threat_query: '*:*',
index: ['suspicious-*'],
severity: 'critical',
risk_score: 20,
tags: ['test', 'threat'],
referenceUrls: ['http://example.com/', 'https://example.com/'],
falsePositivesExamples: ['False1', 'False2'],
mitre: [getMitre1(), getMitre2()],
references: ['http://example.com/', 'https://example.com/'],
false_positives: ['False1', 'False2'],
threat: [getMitre1(), getMitre2()],
note: '# test markdown',
runsEvery: getRunsEvery(),
lookBack: getLookBack(),
indicatorIndexPattern: ['filebeat-*'],
indicatorMappingField: 'myhash.mysha256',
indicatorIndexField: 'threat.indicator.file.hash.sha256',
type: 'file',
interval: '100m',
from: 'now-50000h',
threat_index: ['filebeat-*'],
threat_mapping: [
{
entries: [
{
field: 'myhash.mysha256',
value: 'threat.indicator.file.hash.sha256',
type: 'mapping',
},
],
},
],
max_signals: 100,
threat_indicator_path: 'threat.indicator',
timeline_title: 'Generic Threat Match Timeline',
timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e',
});
export const indicatorRuleMatchingDoc = {
atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3',
timeline: getIndicatorMatchTimelineTemplate(),
maxSignals: 100,
threatIndicatorPath: 'threat.indicator',
matchedType: 'indicator_match_rule',
matchedId: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f',
matchedIndex: 'logs-ti_abusech.malware',
});
};
export const duplicatedRuleName = `${getNewThreatIndicatorRule().name} [Duplicate]`;
export const getSeveritiesOverride = (): string[] => ['Low', 'Medium', 'High', 'Critical'];
export const getEditedRule = (): CustomRule => ({
export const getEditedRule = (): QueryRuleCreateProps => ({
...getExistingRule(),
severity: 'Medium',
severity: 'medium',
description: 'Edited Rule description',
tags: [...(getExistingRule().tags || []), 'edited'],
});
@ -505,13 +455,30 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response<RuleResponse
updated_at: updatedAt,
updated_by: updatedBy,
created_at: createdAt,
created_by: createdBy,
description,
name,
risk_score: riskScore,
severity,
note,
tags,
timeline_id: timelineId,
timeline_title: timelineTitle,
interval,
enabled,
author,
false_positives: falsePositives,
from,
rule_id: ruleId,
max_signals: maxSignals,
risk_score_mapping: riskScoreMapping,
severity_mapping: severityMapping,
threat,
to,
references,
version,
exceptions_list: exceptionsList,
immutable,
related_integrations: relatedIntegrations,
setup,
} = ruleResponse.body;
let query: string | undefined;
@ -527,39 +494,38 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response<RuleResponse
updated_at: updatedAt,
updated_by: updatedBy,
created_at: createdAt,
created_by: 'elastic',
created_by: createdBy,
name,
tags,
interval: '100m',
enabled: false,
interval,
enabled,
description,
risk_score: riskScore,
severity,
note,
output_index: '',
author: [],
false_positives: [],
from: 'now-50000h',
rule_id: 'rule_testing',
max_signals: 100,
risk_score_mapping: [],
severity_mapping: [],
threat: [],
to: 'now',
references: [],
version: 1,
exceptions_list: [],
immutable: false,
related_integrations: [],
author,
false_positives: falsePositives,
from,
rule_id: ruleId,
max_signals: maxSignals,
risk_score_mapping: riskScoreMapping,
severity_mapping: severityMapping,
threat,
to,
references,
version,
exceptions_list: exceptionsList,
immutable,
related_integrations: relatedIntegrations,
required_fields: [],
setup: '',
setup,
type: 'query',
language: 'kuery',
index: getIndexPatterns(),
query,
throttle: 'no_actions',
actions: [],
timeline_id: timelineId,
timeline_title: timelineTitle,
};
// NOTE: Order of the properties in this object matters for the tests to work.

View file

@ -22,6 +22,12 @@ export const ADD_FALSE_POSITIVE_BTN =
export const ADD_REFERENCE_URL_BTN =
'[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] .euiButtonEmpty__text';
export const ALERT_SUPPRESSION_FIELDS =
'[data-test-subj="alertSuppressionInput"] [data-test-subj="comboBoxInput"]';
export const ALERT_SUPPRESSION_DURATION_OPTIONS =
'[data-test-subj="alertSuppressionDuration"] [data-test-subj="groupByDurationOptions"]';
export const ANOMALY_THRESHOLD_INPUT = '[data-test-subj="anomalyThresholdSlider"] .euiFieldNumber';
export const ADVANCED_SETTINGS_BTN = '[data-test-subj="advancedSettings"] .euiAccordion__button';

View file

@ -112,6 +112,10 @@ export const TIMELINE_TEMPLATE_DETAILS = 'Timeline template';
export const TIMESTAMP_OVERRIDE_DETAILS = 'Timestamp override';
export const SUPPRESS_BY_DETAILS = 'Suppress alerts by';
export const SUPPRESS_FOR_DETAILS = 'Suppress alerts for';
export const TIMELINE_FIELD = (field: string) => {
return `[data-test-subj="formatted-field-${field}"]`;
};

View file

@ -5,323 +5,19 @@
* 2.0.
*/
import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types';
import type {
CustomRule,
ThreatIndicatorRule,
MachineLearningRule,
ThresholdRule,
NewTermsRule,
SavedQueryRule,
} from '../../objects/rule';
export const createMachineLearningRule = (rule: MachineLearningRule, ruleId = 'ml_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.interval,
name: rule.name,
severity: rule.severity.toLocaleLowerCase(),
type: 'machine_learning',
from: 'now-50000h',
enabled: false,
machine_learning_job_id: rule.machineLearningJobs,
anomaly_threshold: rule.anomalyScoreThreshold,
tags: rule.tags,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
export const createCustomRule = (
rule: CustomRule,
ruleId = 'rule_testing'
): Cypress.Chainable<Cypress.Response<unknown>> => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const timeline = rule.timeline != null ? rule.timeline : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
import { DETECTION_ENGINE_RULES_URL } from '../../../common/constants';
import type { RuleCreateProps } from '../../../common/detection_engine/rule_schema';
export const createRule = (rule: RuleCreateProps) => {
return cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
name: rule.name,
severity,
type: 'query',
from: 'now-50000h',
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
query: rule.customQuery,
language: 'kuery',
enabled: rule.enabled ?? false,
exceptions_list: rule.exceptionLists ?? [],
tags: rule.tags,
...(timeline?.id ?? timeline?.templateTimelineId
? {
timeline_id: timeline.id ?? timeline.templateTimelineId,
timeline_title: timeline.title,
}
: {}),
actions: rule.actions,
},
url: DETECTION_ENGINE_RULES_URL,
body: rule,
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
};
export const createEventCorrelationRule = (rule: CustomRule, ruleId = 'rule_testing') => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLowerCase() : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`,
name: rule.name,
severity,
type: 'eql',
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
query: rule.customQuery,
language: 'eql',
enabled: true,
tags: rule.tags,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
});
};
export const createThresholdRule = (rule: ThresholdRule, ruleId = 'rule_testing') => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`,
name: rule.name,
severity,
type: 'threshold',
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
query: rule.customQuery,
threshold: {
field: [rule.thresholdField],
value: parseInt(rule.threshold, 10),
cardinality: [],
},
enabled: true,
tags: rule.tags,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
});
};
export const createNewTermsRule = (rule: NewTermsRule, ruleId = 'rule_testing') => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
from: `now-${rule.lookBack?.interval}${rule.lookBack?.type}`,
name: rule.name,
severity,
type: 'new_terms',
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
query: rule.customQuery,
new_terms_fields: rule.newTermsFields,
history_window_start: `now-${rule.historyWindowSize.interval}${rule.historyWindowSize.type}`,
enabled: true,
tags: rule.tags,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
});
};
export const createSavedQueryRule = (
rule: SavedQueryRule,
ruleId = 'saved_query_rule_testing'
): Cypress.Chainable<Cypress.Response<unknown>> => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const timeline = rule.timeline != null ? rule.timeline : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
return cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
name: rule.name,
severity,
type: 'saved_query',
from: 'now-50000h',
index: rule.dataSource.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
saved_id: rule.savedId,
language: 'kuery',
enabled: false,
exceptions_list: rule.exceptionLists ?? [],
tags: rule.tags,
...(timeline?.id ?? timeline?.templateTimelineId
? {
timeline_id: timeline.id ?? timeline.templateTimelineId,
timeline_title: timeline.title,
}
: {}),
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
};
export const createCustomIndicatorRule = (rule: ThreatIndicatorRule, ruleId = 'rule_testing') => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const timeline = rule.timeline != null ? rule.timeline : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
name: rule.name,
severity,
type: 'threat_match',
timeline_id: timeline?.templateTimelineId,
timeline_title: 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.type === 'indexPatterns' ? rule.dataSource.index : undefined,
data_view_id: rule.dataSource.type === 'dataView' ? rule.dataSource.dataView : undefined,
query: rule.customQuery || '*:*',
language: 'kuery',
enabled: true,
tags: rule.tags,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
};
export const createCustomRuleEnabled = (
rule: CustomRule,
ruleId = '1',
maxSignals = 500,
actions?: RuleActionArray
) => {
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
const interval = rule.runsEvery ? `${rule.runsEvery.interval}${rule.runsEvery.type}` : '100m';
if (rule.dataSource.type === 'indexPatterns') {
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
name: rule.name,
severity,
type: 'query',
from: 'now-50000h',
index: rule.dataSource.index,
query: rule.customQuery,
language: 'kuery',
enabled: true,
exceptions_list: rule.exceptionLists ?? [],
tags: ['rule1'],
max_signals: maxSignals,
building_block_type: rule.buildingBlockType,
actions,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
} else if (rule.dataSource.type === 'dataView') {
cy.request({
method: 'POST',
url: 'api/detection_engine/rules',
body: {
rule_id: ruleId,
risk_score: riskScore,
description: rule.description,
interval,
name: rule.name,
severity,
type: 'query',
from: 'now-50000h',
index: [],
data_view_id: rule.dataSource.dataView,
query: rule.customQuery,
language: 'kuery',
enabled: true,
exceptions_list: rule.exceptionLists ?? [],
tags: ['rule1'],
max_signals: maxSignals,
building_block_type: rule.buildingBlockType,
actions,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
}
};
export const deleteCustomRule = (ruleId = '1') => {
cy.request({
method: 'DELETE',

View file

@ -5,22 +5,20 @@
* 2.0.
*/
import { isArray, parseInt } from 'lodash';
import type {
RuleIntervalFrom,
Threat,
ThreatSubtechnique,
ThreatTechnique,
} from '@kbn/securitysolution-io-ts-alerting-types';
import type {
CustomRule,
MachineLearningRule,
OverrideRule,
ThreatIndicatorRule,
ThresholdRule,
NewTermsRule,
Mitre,
} from '../objects/rule';
import { getMachineLearningRule } from '../objects/rule';
import type { Actions } from '../objects/rule';
// For some reason importing these functions from ../../public/detections/pages/detection_engine/rules/helpers
// causes a "Webpack Compilation Error" in this file specifically, even though it imports fine in the test files
// in ../e2e/*, so we have a copy of the implementations in the cypress helpers.
import { convertHistoryStartToSize, getHumanizedDuration } from '../helpers/rules';
import {
ABOUT_CONTINUE_BTN,
ABOUT_EDIT_TAB,
@ -120,6 +118,15 @@ import { refreshPage } from './security_header';
import { EUI_FILTER_SELECT_ITEM, COMBO_BOX_INPUT } from '../screens/common/controls';
import { ruleFields } from '../data/detection_engine';
import { BACK_TO_RULES_TABLE } from '../screens/rule_details';
import type {
EqlRuleCreateProps,
MachineLearningRuleCreateProps,
NewTermsRuleCreateProps,
QueryRuleCreateProps,
RuleCreateProps,
ThreatMatchRuleCreateProps,
ThresholdRuleCreateProps,
} from '../../common/detection_engine/rule_schema';
export const createAndEnableRule = () => {
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
@ -135,32 +142,30 @@ export const createRuleWithoutEnabling = () => {
cy.get(BACK_TO_RULES_TABLE).should('not.exist');
};
export const fillAboutRule = (
rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule
) => {
export const fillAboutRule = (rule: RuleCreateProps) => {
cy.get(RULE_NAME_INPUT).clear({ force: true }).type(rule.name, { force: true });
cy.get(RULE_DESCRIPTION_INPUT).clear({ force: true }).type(rule.description, { force: true });
if (rule.severity) {
fillSeverity(rule.severity);
}
if (rule.riskScore) {
fillRiskScore(rule.riskScore);
if (rule.risk_score) {
fillRiskScore(rule.risk_score);
}
if (rule.tags) {
fillRuleTags(rule.tags);
}
cy.get(ADVANCED_SETTINGS_BTN).click({ force: true });
if (rule.referenceUrls) {
fillReferenceUrls(rule.referenceUrls);
if (rule.references) {
fillReferenceUrls(rule.references);
}
if (rule.falsePositivesExamples) {
fillFalsePositiveExamples(rule.falsePositivesExamples);
if (rule.false_positives) {
fillFalsePositiveExamples(rule.false_positives);
}
if (rule.mitre) {
fillMitre(rule.mitre);
if (rule.threat) {
fillMitre(rule.threat);
}
if (rule.note) {
fillNote(rule.note);
@ -176,28 +181,31 @@ export const fillNote = (note: string = ruleFields.investigationGuide) => {
return note;
};
export const fillMitre = (mitreAttacks: Mitre[]) => {
export const fillMitre = (mitreAttacks: Threat[]) => {
let techniqueIndex = 0;
let subtechniqueInputIndex = 0;
mitreAttacks.forEach((mitre, tacticIndex) => {
cy.get(MITRE_ATTACK_TACTIC_DROPDOWN).eq(tacticIndex).click({ force: true });
cy.contains(MITRE_TACTIC, mitre.tactic).click();
cy.contains(MITRE_TACTIC, `${mitre.tactic.name} (${mitre.tactic.id})`).click();
mitre.techniques.forEach((technique) => {
cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).eq(tacticIndex).click({ force: true });
cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).eq(techniqueIndex).click({ force: true });
cy.contains(MITRE_TACTIC, technique.name).click();
technique.subtechniques.forEach((subtechnique) => {
cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).eq(techniqueIndex).click({ force: true });
cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN)
.eq(subtechniqueInputIndex)
.click({ force: true });
cy.contains(MITRE_TACTIC, subtechnique).click();
subtechniqueInputIndex++;
if (mitre.technique) {
mitre.technique.forEach((technique) => {
cy.get(MITRE_ATTACK_ADD_TECHNIQUE_BUTTON).eq(tacticIndex).click({ force: true });
cy.get(MITRE_ATTACK_TECHNIQUE_DROPDOWN).eq(techniqueIndex).click({ force: true });
cy.contains(MITRE_TACTIC, `${technique.name} (${technique.id})`).click();
if (technique.subtechnique) {
technique.subtechnique.forEach((subtechnique) => {
cy.get(MITRE_ATTACK_ADD_SUBTECHNIQUE_BUTTON).eq(techniqueIndex).click({ force: true });
cy.get(MITRE_ATTACK_SUBTECHNIQUE_DROPDOWN)
.eq(subtechniqueInputIndex)
.click({ force: true });
cy.contains(MITRE_TACTIC, `${subtechnique.name} (${subtechnique.id})`).click();
subtechniqueInputIndex++;
});
techniqueIndex++;
}
});
techniqueIndex++;
});
}
cy.get(MITRE_ATTACK_ADD_TACTIC_BUTTON).click({ force: true });
});
@ -260,7 +268,7 @@ export const fillSeverity = (severity: string = ruleFields.ruleSeverity) => {
return severity;
};
export const fillRiskScore = (riskScore: string = ruleFields.riskScore.toString()) => {
export const fillRiskScore = (riskScore: number = ruleFields.riskScore) => {
cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${riskScore}`, { force: true });
return riskScore;
};
@ -280,37 +288,40 @@ export const fillReferenceUrls = (referenceUrls: string[] = ruleFields.reference
return referenceUrls;
};
export const fillAboutRuleAndContinue = (
rule: CustomRule | MachineLearningRule | ThresholdRule | ThreatIndicatorRule
) => {
export const fillAboutRuleAndContinue = (rule: RuleCreateProps) => {
fillAboutRule(rule);
getAboutContinueButton().should('exist').click({ force: true });
};
export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => {
export const fillAboutRuleWithOverrideAndContinue = (rule: RuleCreateProps) => {
cy.get(RULE_NAME_INPUT).type(rule.name, { force: true });
cy.get(RULE_DESCRIPTION_INPUT).type(rule.description, { force: true });
cy.get(SEVERITY_MAPPING_OVERRIDE_OPTION).click();
rule.severityOverride.forEach((severity, i) => {
cy.get(SEVERITY_OVERRIDE_ROW)
.eq(i)
.within(() => {
cy.get(COMBO_BOX_INPUT).eq(0).type(`${severity.sourceField}{enter}`);
cy.get(COMBO_BOX_INPUT).eq(1).type(`${severity.sourceValue}{enter}`);
});
});
if (rule.severity_mapping) {
rule.severity_mapping.forEach((severity, i) => {
cy.get(SEVERITY_OVERRIDE_ROW)
.eq(i)
.within(() => {
cy.get(COMBO_BOX_INPUT).eq(0).type(`${severity.field}{enter}`);
cy.get(COMBO_BOX_INPUT).eq(1).type(`${severity.value}{enter}`);
});
});
}
if (rule.severity) {
fillSeverity(rule.severity);
}
cy.get(RISK_MAPPING_OVERRIDE_OPTION).click();
cy.get(RISK_OVERRIDE).within(() => {
cy.get(COMBO_BOX_INPUT).type(`${rule.riskOverride}{enter}`);
});
if (rule.risk_score_mapping) {
const field = rule.risk_score_mapping[0].field;
cy.get(RISK_OVERRIDE).within(() => {
cy.get(COMBO_BOX_INPUT).type(`${field}{enter}`);
});
}
cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.riskScore}`, { force: true });
cy.get(DEFAULT_RISK_SCORE_INPUT).type(`{selectall}${rule.risk_score}`, { force: true });
if (rule.tags) {
fillRuleTags(rule.tags);
@ -318,45 +329,30 @@ export const fillAboutRuleWithOverrideAndContinue = (rule: OverrideRule) => {
cy.get(ADVANCED_SETTINGS_BTN).click({ force: true });
if (rule.referenceUrls) {
fillReferenceUrls(rule.referenceUrls);
if (rule.references) {
fillReferenceUrls(rule.references);
}
if (rule.falsePositivesExamples) {
fillFalsePositiveExamples(rule.falsePositivesExamples);
if (rule.false_positives) {
fillFalsePositiveExamples(rule.false_positives);
}
if (rule.mitre) {
fillMitre(rule.mitre);
if (rule.threat) {
fillMitre(rule.threat);
}
if (rule.note) {
fillNote(rule.note);
}
cy.get(RULE_NAME_OVERRIDE).within(() => {
cy.get(COMBO_BOX_INPUT).type(`${rule.nameOverride}{enter}`);
cy.get(COMBO_BOX_INPUT).type(`${rule.rule_name_override}{enter}`);
});
cy.get(RULE_TIMESTAMP_OVERRIDE).within(() => {
cy.get(COMBO_BOX_INPUT).type(`${rule.timestampOverride}{enter}`);
cy.get(COMBO_BOX_INPUT).type(`${rule.timestamp_override}{enter}`);
});
getAboutContinueButton().should('exist').click({ force: true });
};
const fillCustomQuery = (rule: CustomRule | OverrideRule) => {
if (rule.timeline?.id) {
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);
if (rule.dataSource.type === 'indexPatterns') {
removeAlertsIndex();
}
} else {
cy.get(CUSTOM_QUERY_INPUT)
.first()
.type(rule.customQuery || '');
}
};
// called after import rule from saved timeline
// if alerts index is created, it is included in the timeline
// to be consistent in multiple test runs, remove it if it's there
@ -372,24 +368,31 @@ export const continueWithNextSection = () => {
cy.get(CONTINUE_BUTTON).should('exist').click();
};
export const fillDefineCustomRuleAndContinue = (rule: CustomRule | OverrideRule) => {
if (rule.dataSource.type === 'dataView') {
export const fillDefineCustomRuleAndContinue = (rule: QueryRuleCreateProps) => {
if (rule.data_view_id !== undefined) {
cy.get(DATA_VIEW_OPTION).click();
cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.dataSource.dataView}{enter}`);
cy.get(DATA_VIEW_COMBO_BOX).type(`${rule.data_view_id}{enter}`);
}
fillCustomQuery(rule);
cy.get(CUSTOM_QUERY_INPUT)
.first()
.type(rule.query || '');
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
};
export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRule) => {
if (rule.runsEvery) {
cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(rule.runsEvery.interval);
cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType);
export const fillScheduleRuleAndContinue = (rule: RuleCreateProps) => {
if (rule.interval) {
const intervalNumber = rule.interval.slice(0, rule.interval.length - 1);
const intervalType = rule.interval.charAt(rule.interval.length - 1);
cy.get(RUNS_EVERY_INTERVAL).type('{selectall}').type(intervalNumber);
cy.get(RUNS_EVERY_TIME_TYPE).select(intervalType);
}
if (rule.lookBack) {
cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(rule.lookBack.interval);
cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType);
if (rule.from) {
const additionalLookback = getHumanizedDuration(rule.from, rule.interval ?? '5m');
const additionalLookbackNumber = additionalLookback.slice(0, additionalLookback.length - 1);
const additionalLookbackType = additionalLookback.charAt(additionalLookback.length - 1);
cy.get(LOOK_BACK_INTERVAL).type('{selectAll}').type(additionalLookbackNumber);
cy.get(LOOK_BACK_TIME_TYPE).select(additionalLookbackType);
}
cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true });
};
@ -401,55 +404,55 @@ export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) =
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);
rule.actions?.connectors.forEach((connector) => {
switch (connector.type) {
case 'index':
cy.get(INDEX_SELECTOR).click();
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
fillIndexConnectorForm(connector);
break;
case 'email':
cy.get(EMAIL_ACTION_BTN).click();
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
fillEmailConnectorForm(connector);
break;
}
});
}
export const fillRuleAction = (actions: Actions) => {
cy.get(ACTIONS_THROTTLE_INPUT).select(actions.throttle);
actions.connectors.forEach((connector) => {
switch (connector.type) {
case 'index':
cy.get(INDEX_SELECTOR).click();
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
fillIndexConnectorForm(connector);
break;
case 'email':
cy.get(EMAIL_ACTION_BTN).click();
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
fillEmailConnectorForm(connector);
break;
}
});
};
export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => {
export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProps) => {
const thresholdField = 0;
const threshold = 1;
const typeThresholdField = ($el: Cypress.ObjectLike) =>
cy.wrap($el).type(rule.thresholdField, { delay: 35 });
cy
.wrap($el)
.type(isArray(rule.threshold.field) ? rule.threshold.field[0] : rule.threshold.field, {
delay: 35,
});
fillCustomQuery(rule);
cy.get(CUSTOM_QUERY_INPUT)
.first()
.type(rule.query || '');
cy.get(THRESHOLD_INPUT_AREA)
.find(INPUT)
.then((inputs) => {
cy.wrap(inputs[thresholdField]).click();
cy.wrap(inputs[thresholdField]).pipe(typeThresholdField);
cy.get(EUI_FILTER_SELECT_ITEM).click({ force: true });
cy.wrap(inputs[threshold]).clear().type(rule.threshold);
cy.wrap(inputs[threshold]).clear().type(`${rule.threshold.value}`);
});
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
};
export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => {
if (rule.customQuery == null) {
throw new TypeError('The rule custom query should never be undefined or null ');
}
export const fillDefineEqlRuleAndContinue = (rule: EqlRuleCreateProps) => {
cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('exist');
cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('be.visible');
cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.customQuery);
cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.query);
cy.get(RULES_CREATION_FORM).find(EQL_QUERY_VALIDATION_SPINNER).should('not.exist');
cy.get(RULES_CREATION_PREVIEW_BUTTON).should('not.be.disabled').click({ force: true });
cy.get(RULES_CREATION_PREVIEW_REFRESH_BUTTON).should('not.be.disabled').click({ force: true });
@ -467,18 +470,21 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => {
cy.get(`${RULES_CREATION_FORM} ${EQL_QUERY_INPUT}`).should('not.exist');
};
export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRule) => {
fillCustomQuery(rule);
cy.get(NEW_TERMS_INPUT_AREA).find(INPUT).click().type(rule.newTermsFields[0], { delay: 35 });
export const fillDefineNewTermsRuleAndContinue = (rule: NewTermsRuleCreateProps) => {
cy.get(CUSTOM_QUERY_INPUT)
.first()
.type(rule.query || '');
cy.get(NEW_TERMS_INPUT_AREA).find(INPUT).click().type(rule.new_terms_fields[0], { delay: 35 });
cy.get(EUI_FILTER_SELECT_ITEM).click({ force: true });
cy.focused().type('{esc}'); // Close combobox dropdown so next inputs can be interacted with
const historySize = convertHistoryStartToSize(rule.history_window_start);
const historySizeNumber = historySize.slice(0, historySize.length - 1);
const historySizeType = historySize.charAt(historySize.length - 1);
cy.get(NEW_TERMS_INPUT_AREA)
.find(NEW_TERMS_HISTORY_SIZE)
.type('{selectAll}')
.type(rule.historyWindowSize.interval);
cy.get(NEW_TERMS_INPUT_AREA)
.find(NEW_TERMS_HISTORY_TIME_TYPE)
.select(rule.historyWindowSize.timeType);
.type(historySizeNumber);
cy.get(NEW_TERMS_INPUT_AREA).find(NEW_TERMS_HISTORY_TIME_TYPE).select(historySizeType);
cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
@ -603,21 +609,24 @@ export const getCustomQueryInvalidationText = () => cy.contains(CUSTOM_QUERY_REQ
* Fills in the define indicator match rules and then presses the continue button
* @param rule The rule to use to fill in everything
*/
export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatIndicatorRule) => {
if (rule.dataSource.type === 'indexPatterns') {
fillIndexAndIndicatorIndexPattern(rule.dataSource.index, rule.indicatorIndexPattern);
export const fillDefineIndicatorMatchRuleAndContinue = (rule: ThreatMatchRuleCreateProps) => {
if (rule.index) {
fillIndexAndIndicatorIndexPattern(rule.index, rule.threat_index);
}
fillIndicatorMatchRow({
indexField: rule.indicatorMappingField,
indicatorIndexField: rule.indicatorIndexField,
indexField: rule.threat_mapping[0].entries[0].field,
indicatorIndexField: rule.threat_mapping[0].entries[0].value,
});
getCustomIndicatorQueryInput().type('{selectall}{enter}*:*');
getDefineContinueButton().should('exist').click({ force: true });
cy.get(CUSTOM_QUERY_INPUT).should('not.exist');
};
export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRule) => {
const text = rule.machineLearningJobs
export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRuleCreateProps) => {
const jobsAsArray = isArray(rule.machine_learning_job_id)
? rule.machine_learning_job_id
: [rule.machine_learning_job_id];
const text = jobsAsArray
.map((machineLearningJob) => `${machineLearningJob}{downArrow}{enter}`)
.join('');
cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).click({ force: true });
@ -625,12 +634,9 @@ export const fillDefineMachineLearningRuleAndContinue = (rule: MachineLearningRu
cy.get(MACHINE_LEARNING_DROPDOWN_INPUT).type('{esc}');
cy.get(ANOMALY_THRESHOLD_INPUT).type(
`{selectall}${getMachineLearningRule().anomalyScoreThreshold}`,
{
force: true,
}
);
cy.get(ANOMALY_THRESHOLD_INPUT).type(`{selectall}${rule.anomaly_threshold}`, {
force: true,
});
getDefineContinueButton().should('exist').click({ force: true });
};

View file

@ -10,7 +10,7 @@ import { HOSTS_URL } from '../urls/navigation';
import { waitForPage } from './login';
import { openTimelineUsingToggle } from './security_main';
import { DEFAULT_ALERTS_INDEX } from '../../common/constants';
import { createCustomRuleEnabled } from './api_calls/rules';
import { createRule } from './api_calls/rules';
import { getNewRule } from '../objects/rule';
export const openSourcerer = (sourcererScope?: string) => {
@ -148,6 +148,6 @@ const refreshUntilAlertsIndexExists = async () => {
};
export const waitForAlertsIndexToExist = () => {
createCustomRuleEnabled(getNewRule(), '1', 100);
createRule({ ...getNewRule(), rule_id: '1', max_signals: 100 });
refreshUntilAlertsIndexExists();
};

View file

@ -27,6 +27,7 @@
"path": "../tsconfig.json",
"force": true
},
"@kbn/rison"
"@kbn/rison",
"@kbn/datemath"
]
}

View file

@ -555,6 +555,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
onChange={(id: string) => {
groupByRadioSelection.setValue(id);
}}
data-test-subj="groupByDurationOptions"
/>
),
[license, groupByFields]
@ -867,7 +868,10 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
</>
)}
<RuleTypeEuiFormRow $isVisible={isQueryRule(ruleType)}>
<RuleTypeEuiFormRow
$isVisible={isQueryRule(ruleType)}
data-test-subj="alertSuppressionInput"
>
<UseField
path="groupByFields"
component={GroupByFields}
@ -879,7 +883,10 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
}}
/>
</RuleTypeEuiFormRow>
<RuleTypeEuiFormRow $isVisible={isQueryRule(ruleType)}>
<RuleTypeEuiFormRow
$isVisible={isQueryRule(ruleType)}
data-test-subj="alertSuppressionDuration"
>
<UseMultiFields
fields={{
groupByRadioSelection: {