Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/elastic/kibana into optim…
Browse files Browse the repository at this point in the history
…izing_alerts
  • Loading branch information
igoristic committed Nov 30, 2020
2 parents 6f3368d + 4546352 commit 9c39cfb
Show file tree
Hide file tree
Showing 29 changed files with 7,898 additions and 46 deletions.
42 changes: 30 additions & 12 deletions x-pack/plugins/ml/server/lib/ml_client/ml_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,19 @@ export function getMlClient(
const jobIds =
jobType === 'anomaly-detector' ? getADJobIdsFromRequest(p) : getDFAJobIdsFromRequest(p);
if (jobIds.length) {
const filteredJobIds = await jobSavedObjectService.filterJobIdsForSpace(jobType, jobIds);
let missingIds = jobIds.filter((j) => filteredJobIds.indexOf(j) === -1);
if (allowWildcards === true && missingIds.join().match('\\*') !== null) {
// filter out wildcard ids from the error
missingIds = missingIds.filter((id) => id.match('\\*') === null);
}
if (missingIds.length) {
throw new MLJobNotFound(`No known job with id '${missingIds.join(',')}'`);
}
await checkIds(jobType, jobIds, allowWildcards);
}
}

async function checkIds(jobType: JobType, jobIds: string[], allowWildcards: boolean = false) {
const filteredJobIds = await jobSavedObjectService.filterJobIdsForSpace(jobType, jobIds);
let missingIds = jobIds.filter((j) => filteredJobIds.indexOf(j) === -1);
if (allowWildcards === true && missingIds.join().match('\\*') !== null) {
// filter out wildcard ids from the error
missingIds = missingIds.filter((id) => id.match('\\*') === null);
}
if (missingIds.length) {
throw new MLJobNotFound(`No known job with id '${missingIds.join(',')}'`);
}
}

Expand All @@ -59,8 +63,17 @@ export function getMlClient(
if (ids.length) {
// find all groups from unfiltered jobs
const responseGroupIds = [...new Set(allJobs.map((j) => j.groups ?? []).flat())];
// work out which ids requested are actually groups
const requestedGroupIds = ids.filter((id) => responseGroupIds.includes(id));

// work out which ids requested are actually groups and which are jobs
const requestedGroupIds: string[] = [];
const requestedJobIds: string[] = [];
ids.forEach((id) => {
if (responseGroupIds.includes(id)) {
requestedGroupIds.push(id);
} else {
requestedJobIds.push(id);
}
});

// find all groups from filtered jobs
const groupIdsFromFilteredJobs = [
Expand All @@ -77,10 +90,15 @@ export function getMlClient(
);

if (groupsIdsThatDidNotMatch.length) {
// is there are group ids which were requested but didn't
// if there are group ids which were requested but didn't
// exist in filtered jobs, list them in an error
throw new MLJobNotFound(`No known job with id '${groupsIdsThatDidNotMatch.join(',')}'`);
}

// check the remaining jobs ids
if (requestedJobIds.length) {
await checkIds('anomaly-detector', requestedJobIds, true);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { exception } from '../objects/exception';
import { newRule } from '../objects/rule';

import { RULE_STATUS } from '../screens/create_new_rule';
import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline';

import {
addExceptionFromFirstAlert,
goToClosedAlerts,
goToManageAlertsDetectionRules,
goToOpenedAlerts,
waitForAlertsIndexToBeCreated,
} from '../tasks/alerts';
import { createCustomRule, deleteCustomRule, removeSignalsIndex } from '../tasks/api_calls';
import { goToRuleDetails } from '../tasks/alerts_detection_rules';
import { waitForAlertsToPopulate } from '../tasks/create_new_rule';
import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver';
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import {
activatesRule,
addsException,
addsExceptionFromRuleSettings,
goToAlertsTab,
goToExceptionsTab,
removeException,
waitForTheRuleToBeExecuted,
} from '../tasks/rule_details';
import { refreshPage } from '../tasks/security_header';

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

const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = 1;

describe('Exceptions', () => {
beforeEach(() => {
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
waitForAlertsIndexToBeCreated();
createCustomRule(newRule);
goToManageAlertsDetectionRules();
goToRuleDetails();

cy.get(RULE_STATUS).should('have.text', '—');

esArchiverLoad('auditbeat_for_exceptions');
activatesRule();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfInitialAlertsText) => {
cy.wrap(parseInt(numberOfInitialAlertsText, 10)).should(
'eql',
NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS
);
});
});

afterEach(() => {
esArchiverUnload('auditbeat_for_exceptions');
esArchiverUnload('auditbeat_for_exceptions2');
removeSignalsIndex();
deleteCustomRule();
});
context('From rule', () => {
it('Creates an exception and deletes it', () => {
goToExceptionsTab();
addsExceptionFromRuleSettings(exception);
esArchiverLoad('auditbeat_for_exceptions2');
waitForTheRuleToBeExecuted();
goToAlertsTab();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfAlertsAfterCreatingExceptionText, 10)).should('eql', 0);
});

goToClosedAlerts();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfClosedAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfClosedAlertsAfterCreatingExceptionText, 10)).should(
'eql',
NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS
);
});

goToOpenedAlerts();
waitForTheRuleToBeExecuted();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfOpenedAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfOpenedAlertsAfterCreatingExceptionText, 10)).should('eql', 0);
});

goToExceptionsTab();
removeException();
refreshPage();
goToAlertsTab();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfAlertsAfterRemovingExceptionsText) => {
cy.wrap(parseInt(numberOfAlertsAfterRemovingExceptionsText, 10)).should(
'eql',
NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS
);
});
});
});

context('From alert', () => {
it('Creates an exception and deletes it', () => {
addExceptionFromFirstAlert();
addsException(exception);
esArchiverLoad('auditbeat_for_exceptions2');

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfAlertsAfterCreatingExceptionText, 10)).should('eql', 0);
});

goToClosedAlerts();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfClosedAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfClosedAlertsAfterCreatingExceptionText, 10)).should(
'eql',
NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS
);
});

goToOpenedAlerts();
waitForTheRuleToBeExecuted();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfOpenedAlertsAfterCreatingExceptionText) => {
cy.wrap(parseInt(numberOfOpenedAlertsAfterCreatingExceptionText, 10)).should('eql', 0);
});

goToExceptionsTab();
removeException();
goToAlertsTab();
waitForTheRuleToBeExecuted();
waitForAlertsToPopulate();
refreshPage();

cy.get(SERVER_SIDE_EVENT_COUNT)
.invoke('text')
.then((numberOfAlertsAfterRemovingExceptionsText) => {
cy.wrap(parseInt(numberOfAlertsAfterRemovingExceptionsText, 10)).should(
'eql',
NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS
);
});
});
});
});
17 changes: 17 additions & 0 deletions x-pack/plugins/security_solution/cypress/objects/exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export interface Exception {
field: string;
operator: string;
values: string[];
}

export const exception: Exception = {
field: 'host.name',
operator: 'is',
values: ['suricata-iowa'],
};
2 changes: 2 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const ADD_EXCEPTION_BTN = '[data-test-subj="addExceptionButton"]';

export const ALERTS = '[data-test-subj="event"]';

export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input';
Expand Down
24 changes: 24 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/exceptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const ADD_EXCEPTIONS_BTN = '[data-test-subj="exceptionsHeaderAddExceptionBtn"]';

export const CLOSE_ALERTS_CHECKBOX =
'[data-test-subj="bulk-close-alert-on-add-add-exception-checkbox"]';

export const CONFIRM_BTN = '[data-test-subj="add-exception-confirm-button"]';

export const FIELD_INPUT =
'[data-test-subj="fieldAutocompleteComboBox"] [data-test-subj="comboBoxInput"]';

export const FIELD_INPUT_RESULT = '.euiFilterSelectItem';

export const LOADING_SPINNER = '[data-test-subj="loading-spinner"]';

export const OPERATOR_INPUT = '[data-test-subj="operatorAutocompleteComboBox"]';

export const VALUES_INPUT =
'[data-test-subj="valuesAutocompleteMatch"] [data-test-subj="comboBoxInput"]';
14 changes: 13 additions & 1 deletion x-pack/plugins/security_solution/cypress/screens/rule_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@ export const ABOUT_DETAILS =

export const ADDITIONAL_LOOK_BACK_DETAILS = 'Additional look-back time';

export const ALERTS_TAB = '[data-test-subj="alertsTab"]';

export const ANOMALY_SCORE_DETAILS = 'Anomaly score';

export const CUSTOM_QUERY_DETAILS = 'Custom query';

export const DEFINITION_DETAILS =
'[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]';

export const DELETE_RULE = '[data-test-subj=rules-details-delete-rule]';

export const DETAILS_DESCRIPTION = '.euiDescriptionList__description';

export const DETAILS_TITLE = '.euiDescriptionList__title';

export const DELETE_RULE = '[data-test-subj=rules-details-delete-rule]';
export const EXCEPTIONS_TAB = '[data-test-subj="exceptionsTab"]';

export const FALSE_POSITIVES_DETAILS = 'False positive examples';

Expand All @@ -42,6 +46,8 @@ export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobS

export const MITRE_ATTACK_DETAILS = 'MITRE ATT&CK';

export const REFRESH_BUTTON = '[data-test-subj="refreshButton"]';

export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]';

export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]';
Expand All @@ -54,6 +60,12 @@ export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override';

export const REFERENCE_URLS_DETAILS = 'Reference URLs';

export const REMOVE_EXCEPTION_BTN = '[data-test-subj="exceptionsViewerDeleteBtn"]';

export const RULE_SWITCH = '[data-test-subj="ruleSwitch"]';

export const RULE_SWITCH_LOADER = '[data-test-subj="rule-switch-loader"]';

export const RULE_TYPE_DETAILS = 'Rule type';

export const RUNS_EVERY_DETAILS = 'Runs every';
Expand Down
Loading

0 comments on commit 9c39cfb

Please sign in to comment.