mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution][Exceptions]- Increase exceptions test coverage (#152757)
## Summary - Addresses https://github.com/elastic/security-team/issues/5947 - Adding tests to cover the yellow rows in [test sheet](https://docs.google.com/spreadsheets/d/1Eb_317s7nkQ4axVA270Ja99PRS-NWrYZAEc-1aVuyXg/edit#gid=0) - Organise the tests to correspond to the following [structure](https://docs.google.com/spreadsheets/d/14DdtghpxgfEmWoc7kot4XgEva_4GDEC_uTej65MUjV8/edit?pli=1#gid=0) - Removed the `x-pack/plugins/security_solution/cypress/e2e/exceptions/alerts_table_flow/add_exception.cy.ts` as it was duplicated from `x-pack/plugins/security_solution/cypress/e2e/exceptions/exceptions_management_flow/exceptions_table.cy.ts` - Skipped the `flyout_validation` tests until resolving this [ticket](https://github.com/elastic/kibana/issues/154994) - Regarding `Exception-List`, `Exception-List-Item` and `Rule with exceptions` migrations test cases are handled by most of our old `FTR` tests as most of them deal with `Exception List Schema` which doesn't include the new props, like the `expire_time` that was introduced in `8.7`, so adding new tests using the new schema can be treated as testing the new versions against the existing scenarios whereas the existing tests for the migrations (downgrade) tests [4a75a51
](4a75a51a3e
) - Tests under `x-pack/plugins/security_solution/cypress/upgrade_e2e` are just POCs can't be used # New tests folder structure based on workflow <img width="432" alt="image" src="https://user-images.githubusercontent.com/12671903/234849016-f6f227d1-fcaf-43cb-abe3-d3fc7f9cee00.png"> --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5e907edc39
commit
005108684b
41 changed files with 3355 additions and 579 deletions
|
@ -176,7 +176,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -563,7 +563,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -846,7 +846,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -1072,7 +1072,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -1327,7 +1327,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -1526,7 +1526,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="RightSideMenuItemsManageRulesButton"
|
||||
data-test-subj="RightSideMenuItemsLinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
|
|
@ -233,7 +233,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -300,7 +300,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -424,7 +424,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -591,7 +591,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -1054,7 +1054,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
@ -1149,7 +1149,7 @@ Object {
|
|||
>
|
||||
<button
|
||||
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-m-fill-primary"
|
||||
data-test-subj="ManageRulesButton"
|
||||
data-test-subj="LinkRulesButton"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
|
|
|
@ -77,7 +77,7 @@ const MenuItemsComponent: FC<MenuItemsProps> = ({
|
|||
{canUserEditList && (
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
data-test-subj={`${dataTestSubj || ''}ManageRulesButton`}
|
||||
data-test-subj={`${dataTestSubj || ''}LinkRulesButton`}
|
||||
fill
|
||||
onClick={() => {
|
||||
if (typeof onManageRules === 'function') onManageRules();
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('MenuItems', () => {
|
|||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.getByTestId('LinkedRulesMenuItems')).toHaveTextContent('Linked to 1 rules');
|
||||
expect(wrapper.getByTestId('ManageRulesButton')).toBeInTheDocument();
|
||||
expect(wrapper.getByTestId('LinkRulesButton')).toBeInTheDocument();
|
||||
expect(wrapper.getByTestId('MenuActionsButtonIcon')).toBeInTheDocument();
|
||||
});
|
||||
it('should not render linkedRules HeaderMenu component, instead should render a text', () => {
|
||||
|
@ -101,7 +101,7 @@ describe('MenuItems', () => {
|
|||
/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.queryByTestId('ManageRulesButton')).not.toBeInTheDocument();
|
||||
expect(wrapper.queryByTestId('LinkRulesButton')).not.toBeInTheDocument();
|
||||
});
|
||||
it('should call onManageRules', () => {
|
||||
const wrapper = render(
|
||||
|
@ -115,7 +115,7 @@ describe('MenuItems', () => {
|
|||
onManageRules={onManageRules}
|
||||
/>
|
||||
);
|
||||
fireEvent.click(wrapper.getByTestId('ManageRulesButton'));
|
||||
fireEvent.click(wrapper.getByTestId('LinkRulesButton'));
|
||||
expect(onManageRules).toHaveBeenCalled();
|
||||
});
|
||||
it('should call onExportModalOpen', () => {
|
||||
|
|
|
@ -117,3 +117,6 @@ export const _VERSION = 'WzI5NywxXQ==';
|
|||
export const VERSION = 1;
|
||||
export const IMMUTABLE = false;
|
||||
export const IMPORT_TIMEOUT = moment.duration(5, 'minutes');
|
||||
|
||||
/** Added in 8.7 */
|
||||
export const EXPIRE_TIME = '2023-04-24T19:00:00.000Z';
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
COMMENTS,
|
||||
DESCRIPTION,
|
||||
ENTRIES,
|
||||
EXPIRE_TIME,
|
||||
ITEM_ID,
|
||||
ITEM_TYPE,
|
||||
LIST_ID,
|
||||
|
@ -60,3 +61,18 @@ export const getCreateExceptionListItemMinimalSchemaMockWithoutId =
|
|||
os_types: OS_TYPES,
|
||||
type: ITEM_TYPE,
|
||||
});
|
||||
|
||||
/**
|
||||
* Useful for testing newer exception list item versions, as the previous
|
||||
* versions can be used to test migration cases
|
||||
*/
|
||||
export const getCreateExceptionListItemNewerVersionSchemaMock =
|
||||
(): CreateExceptionListItemSchema => ({
|
||||
description: DESCRIPTION,
|
||||
entries: ENTRIES,
|
||||
expire_time: EXPIRE_TIME,
|
||||
list_id: LIST_ID,
|
||||
name: NAME,
|
||||
os_types: OS_TYPES,
|
||||
type: ITEM_TYPE,
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
ImportExceptionsListSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { ENTRIES } from '../../constants.mock';
|
||||
import { ENTRIES, EXPIRE_TIME } from '../../constants.mock';
|
||||
|
||||
export const getImportExceptionsListSchemaMock = (
|
||||
listId = 'detection_list_id'
|
||||
|
@ -23,6 +23,11 @@ export const getImportExceptionsListSchemaMock = (
|
|||
type: 'detection',
|
||||
});
|
||||
|
||||
/**
|
||||
This mock retains the previous properties of the Exception List item, enabling us to
|
||||
conduct migration test cases. As it lacks the new "expire_time" property, and considering
|
||||
the absence of API versioning, we can utilize this mock to simulate the migration scenarios.
|
||||
*/
|
||||
export const getImportExceptionsListItemSchemaMock = (
|
||||
itemId = 'item_id_1',
|
||||
listId = 'detection_list_id'
|
||||
|
@ -35,6 +40,23 @@ export const getImportExceptionsListItemSchemaMock = (
|
|||
type: 'simple',
|
||||
});
|
||||
|
||||
/**
|
||||
Please ensure that this mock is updated with the new properties of the Exception List item,
|
||||
for example the inclusion of the "expire_time" property. This will allow us to test and evaluate
|
||||
the new scenarios effectively.
|
||||
*/
|
||||
export const getImportExceptionsListItemNewerVersionSchemaMock = (
|
||||
itemId = 'item_id_1',
|
||||
listId = 'detection_list_id'
|
||||
): ImportExceptionListItemSchema => ({
|
||||
description: 'some description',
|
||||
entries: ENTRIES,
|
||||
expire_time: EXPIRE_TIME,
|
||||
item_id: itemId,
|
||||
list_id: listId,
|
||||
name: 'Query with a rule id',
|
||||
type: 'simple',
|
||||
});
|
||||
export const getImportExceptionsListSchemaDecodedMock = (
|
||||
listId = 'detection_list_id'
|
||||
): ImportExceptionListSchemaDecoded => ({
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ROLES } from '../../../../common/test';
|
||||
import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login';
|
||||
|
||||
import { EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
import {
|
||||
deleteExceptionListWithRuleReferenceByListId,
|
||||
deleteExceptionListWithoutRuleReferenceByListId,
|
||||
exportExceptionList,
|
||||
searchForExceptionList,
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
clearSearchSelection,
|
||||
} from '../../../tasks/exceptions_table';
|
||||
import {
|
||||
EXCEPTIONS_TABLE_DELETE_BTN,
|
||||
EXCEPTIONS_TABLE_LIST_NAME,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../../screens/exceptions';
|
||||
import { createExceptionList } from '../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
import { TOASTER } from '../../../screens/alerts_detection_rules';
|
||||
|
||||
const getExceptionList1 = () => ({
|
||||
...getExceptionList(),
|
||||
name: 'Test a new list 1',
|
||||
list_id: 'exception_list_1',
|
||||
});
|
||||
const getExceptionList2 = () => ({
|
||||
...getExceptionList(),
|
||||
name: 'Test list 2',
|
||||
list_id: 'exception_list_2',
|
||||
});
|
||||
|
||||
describe('Exceptions Table', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
|
||||
// Create exception list associated with a rule
|
||||
createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) =>
|
||||
createRule(
|
||||
getNewRule({
|
||||
exceptions_list: [
|
||||
{
|
||||
id: response.body.id,
|
||||
list_id: getExceptionList2().list_id,
|
||||
type: getExceptionList2().type,
|
||||
namespace_type: getExceptionList2().namespace_type,
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Create exception list not used by any rules
|
||||
createExceptionList(getExceptionList1(), getExceptionList1().list_id).as(
|
||||
'exceptionListResponse'
|
||||
);
|
||||
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
});
|
||||
|
||||
it('Exports exception list', function () {
|
||||
cy.intercept(/(\/api\/exception_lists\/_export)/).as('export');
|
||||
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
exportExceptionList(getExceptionList1().list_id);
|
||||
|
||||
cy.wait('@export').then(({ response }) => {
|
||||
cy.wrap(response?.body).should(
|
||||
'eql',
|
||||
expectedExportedExceptionList(this.exceptionListResponse)
|
||||
);
|
||||
|
||||
cy.get(TOASTER).should(
|
||||
'have.text',
|
||||
`Exception list "${getExceptionList1().name}" exported successfully`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Filters exception lists on search', () => {
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
|
||||
|
||||
// Single word search
|
||||
searchForExceptionList('Endpoint');
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).should('have.text', 'Endpoint Security Exception List');
|
||||
|
||||
// Multi word search
|
||||
clearSearchSelection();
|
||||
searchForExceptionList('test');
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(1).should('have.text', 'Test list 2');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(0).should('have.text', 'Test a new list 1');
|
||||
|
||||
// Exact phrase search
|
||||
clearSearchSelection();
|
||||
searchForExceptionList(`"${getExceptionList1().name}"`);
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).should('have.text', getExceptionList1().name);
|
||||
|
||||
// Field search
|
||||
clearSearchSelection();
|
||||
searchForExceptionList('list_id:endpoint_list');
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).should('have.text', 'Endpoint Security Exception List');
|
||||
|
||||
clearSearchSelection();
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
|
||||
});
|
||||
|
||||
it('Deletes exception list without rule reference', () => {
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '3');
|
||||
|
||||
deleteExceptionListWithoutRuleReferenceByListId(getExceptionList1().list_id);
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
});
|
||||
|
||||
it('Deletes exception list with rule reference', () => {
|
||||
waitForPageWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
|
||||
deleteExceptionListWithRuleReferenceByListId(getExceptionList2().list_id);
|
||||
|
||||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Exceptions Table - read only', () => {
|
||||
before(() => {
|
||||
// First we login as a privileged user to create exception list
|
||||
esArchiverResetKibana();
|
||||
login(ROLES.platform_engineer);
|
||||
visitWithoutDateRange(EXCEPTIONS_URL, ROLES.platform_engineer);
|
||||
createExceptionList(getExceptionList(), getExceptionList().list_id);
|
||||
|
||||
// Then we login as read-only user to test.
|
||||
login(ROLES.reader);
|
||||
visitWithoutDateRange(EXCEPTIONS_URL, ROLES.reader);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
|
||||
cy.get(EXCEPTIONS_TABLE_SHOWING_LISTS).should('have.text', `Showing 1 list`);
|
||||
});
|
||||
|
||||
it('Delete icon is not shown', () => {
|
||||
cy.get(EXCEPTIONS_TABLE_DELETE_BTN).should('not.exist');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
goToClosedAlertsOnRuleDetailsPage,
|
||||
goToOpenedAlertsOnRuleDetailsPage,
|
||||
openAddEndpointExceptionFromFirstAlert,
|
||||
} from '../../../tasks/alerts';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { getEndpointRule } from '../../../objects/rule';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import {
|
||||
waitForAlertsToPopulate,
|
||||
waitForTheRuleToBeExecuted,
|
||||
} from '../../../tasks/create_new_rule';
|
||||
import {
|
||||
esArchiverLoad,
|
||||
esArchiverResetKibana,
|
||||
esArchiverUnload,
|
||||
} from '../../../tasks/es_archiver';
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
|
||||
import {
|
||||
addExceptionFlyoutItemName,
|
||||
selectCloseSingleAlerts,
|
||||
submitNewExceptionItem,
|
||||
} from '../../../tasks/exceptions';
|
||||
import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts';
|
||||
import {
|
||||
EXCEPTION_ITEM_CONTAINER,
|
||||
FIELD_INPUT_PARENT,
|
||||
NO_EXCEPTIONS_EXIST_PROMPT,
|
||||
} from '../../../screens/exceptions';
|
||||
import {
|
||||
removeException,
|
||||
goToAlertsTab,
|
||||
goToEndpointExceptionsTab,
|
||||
} from '../../../tasks/rule_details';
|
||||
|
||||
describe('Endpoint Exceptions workflows from Alert', () => {
|
||||
const expectedNumberOfAlerts = 1;
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
esArchiverLoad('endpoint');
|
||||
login();
|
||||
createRule(getEndpointRule());
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
waitForTheRuleToBeExecuted();
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
esArchiverUnload('endpoint');
|
||||
esArchiverUnload('endpoint_2');
|
||||
});
|
||||
|
||||
it('Should be able to create and close single Endpoint exception from overflow menu', () => {
|
||||
// The Endpoint will populated with predefined fields
|
||||
openAddEndpointExceptionFromFirstAlert();
|
||||
|
||||
// As the endpoint.alerts-* is used to trigger the alert the
|
||||
// file.Ext.code_signature will be populated as the first item
|
||||
cy.get(EXCEPTION_ITEM_CONTAINER)
|
||||
.eq(0)
|
||||
.find(FIELD_INPUT_PARENT)
|
||||
.eq(0)
|
||||
.should('have.text', 'file.Ext.code_signature');
|
||||
|
||||
selectCloseSingleAlerts();
|
||||
addExceptionFlyoutItemName('Sample Exception');
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Alerts table should now be empty from having added exception and closed
|
||||
// matching alert
|
||||
cy.get(EMPTY_ALERT_TABLE).should('exist');
|
||||
|
||||
// Closed alert should appear in table
|
||||
goToClosedAlertsOnRuleDetailsPage();
|
||||
cy.get(ALERTS_COUNT).should('exist');
|
||||
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alert`);
|
||||
|
||||
// Endpoint Exception will move to Endpoint List under Exception tab of rule
|
||||
goToEndpointExceptionsTab();
|
||||
|
||||
// Remove the exception and load an event that would have matched that exception
|
||||
// to show that said exception now starts to show up again
|
||||
removeException();
|
||||
// when removing exception and again, no more exist, empty screen shows again
|
||||
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
|
||||
|
||||
// load more docs
|
||||
esArchiverLoad('endpoint_2');
|
||||
|
||||
goToAlertsTab();
|
||||
goToOpenedAlertsOnRuleDetailsPage();
|
||||
waitForTheRuleToBeExecuted();
|
||||
waitForAlertsToPopulate();
|
||||
|
||||
cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alert`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LOADING_INDICATOR } from '../../../screens/security_header';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import {
|
||||
addExceptionFromFirstAlert,
|
||||
goToClosedAlertsOnRuleDetailsPage,
|
||||
goToOpenedAlertsOnRuleDetailsPage,
|
||||
} from '../../../tasks/alerts';
|
||||
import {
|
||||
addExceptionEntryFieldValue,
|
||||
addExceptionEntryFieldValueValue,
|
||||
addExceptionEntryOperatorValue,
|
||||
addExceptionFlyoutItemName,
|
||||
selectBulkCloseAlerts,
|
||||
submitNewExceptionItem,
|
||||
validateExceptionItemFirstAffectedRuleNameInRulePage,
|
||||
validateExceptionItemAffectsTheCorrectRulesInRulePage,
|
||||
} from '../../../tasks/exceptions';
|
||||
import {
|
||||
esArchiverLoad,
|
||||
esArchiverResetKibana,
|
||||
esArchiverUnload,
|
||||
} from '../../../tasks/es_archiver';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import {
|
||||
goToAlertsTab,
|
||||
goToExceptionsTab,
|
||||
removeException,
|
||||
waitForTheRuleToBeExecuted,
|
||||
} from '../../../tasks/rule_details';
|
||||
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
|
||||
import { postDataView, deleteAlertsAndRules } from '../../../tasks/common';
|
||||
import { NO_EXCEPTIONS_EXIST_PROMPT } from '../../../screens/exceptions';
|
||||
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
|
||||
|
||||
describe('Rule Exceptions workflows from Alert', () => {
|
||||
const EXPECTED_NUMBER_OF_ALERTS = '1 alert';
|
||||
const ITEM_NAME = 'Sample Exception List Item';
|
||||
const newRule = getNewRule();
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
esArchiverLoad('exceptions');
|
||||
login();
|
||||
postDataView('exceptions-*');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
esArchiverUnload('exceptions');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createRule({
|
||||
...newRule,
|
||||
query: 'agent.name:*',
|
||||
data_view_id: 'exceptions-*',
|
||||
interval: '10s',
|
||||
rule_id: 'rule_testing',
|
||||
});
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
esArchiverUnload('exceptions_2');
|
||||
});
|
||||
|
||||
it('Creates an exception item from alert actions overflow menu and close all matching alerts', () => {
|
||||
cy.get(LOADING_INDICATOR).should('not.exist');
|
||||
addExceptionFromFirstAlert();
|
||||
|
||||
addExceptionEntryFieldValue('agent.name', 0);
|
||||
addExceptionEntryOperatorValue('is', 0);
|
||||
addExceptionEntryFieldValueValue('foo', 0);
|
||||
|
||||
addExceptionFlyoutItemName(ITEM_NAME);
|
||||
selectBulkCloseAlerts();
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Alerts table should now be empty from having added exception and closed
|
||||
// matching alert
|
||||
cy.get(EMPTY_ALERT_TABLE).should('exist');
|
||||
|
||||
// Closed alert should appear in table
|
||||
goToClosedAlertsOnRuleDetailsPage();
|
||||
cy.get(ALERTS_COUNT).should('exist');
|
||||
cy.get(ALERTS_COUNT).should('have.text', `${EXPECTED_NUMBER_OF_ALERTS}`);
|
||||
|
||||
// Remove the exception and load an event that would have matched that exception
|
||||
// to show that said exception now starts to show up again
|
||||
goToExceptionsTab();
|
||||
|
||||
// Validate the exception is affecting the correct rule count and name
|
||||
validateExceptionItemAffectsTheCorrectRulesInRulePage(1);
|
||||
validateExceptionItemFirstAffectedRuleNameInRulePage(newRule.name);
|
||||
|
||||
// when removing exception and again, no more exist, empty screen shows again
|
||||
removeException();
|
||||
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
|
||||
|
||||
// load more docs
|
||||
esArchiverLoad('exceptions_2');
|
||||
|
||||
// now that there are no more exceptions, the docs should match and populate alerts
|
||||
goToAlertsTab();
|
||||
goToOpenedAlertsOnRuleDetailsPage();
|
||||
waitForTheRuleToBeExecuted();
|
||||
waitForAlertsToPopulate();
|
||||
|
||||
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExceptionListTypeEnum, NamespaceType } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { getException, getExceptionList } from '../../../objects/exception';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import {
|
||||
addExceptionFlyoutFromViewerHeader,
|
||||
goToEndpointExceptionsTab,
|
||||
goToExceptionsTab,
|
||||
openEditException,
|
||||
openExceptionFlyoutFromEmptyViewerPrompt,
|
||||
} from '../../../tasks/rule_details';
|
||||
import {
|
||||
addExceptionComment,
|
||||
addExceptionConditions,
|
||||
addExceptionFlyoutItemName,
|
||||
clickCopyCommentToClipboard,
|
||||
submitEditedExceptionItem,
|
||||
submitNewExceptionItem,
|
||||
clickOnShowComments,
|
||||
selectOs,
|
||||
} from '../../../tasks/exceptions';
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
|
||||
import {
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER,
|
||||
EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT,
|
||||
LOADING_SPINNER,
|
||||
} from '../../../screens/exceptions';
|
||||
import {
|
||||
createEndpointExceptionList,
|
||||
createExceptionList,
|
||||
createExceptionListItem,
|
||||
} from '../../../tasks/api_calls/exceptions';
|
||||
import { ROLES } from '../../../../common/test';
|
||||
|
||||
interface ResponseType {
|
||||
body: {
|
||||
id: string;
|
||||
list_id: string;
|
||||
type: ExceptionListTypeEnum;
|
||||
namespace_type: NamespaceType;
|
||||
};
|
||||
}
|
||||
describe('Add, copy comments in different exceptions type and validate sharing them between users', () => {
|
||||
describe('Rule exceptions', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
const exceptionList = getExceptionList();
|
||||
// create rule with exceptions
|
||||
createExceptionList(exceptionList, exceptionList.list_id).then((response) => {
|
||||
createRule({
|
||||
...getNewRule(),
|
||||
query: '*',
|
||||
index: ['*'],
|
||||
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',
|
||||
tags: [],
|
||||
type: 'simple',
|
||||
description: 'Test exception item 2',
|
||||
name: 'Sample Exception List Item 2',
|
||||
namespace_type: 'single',
|
||||
entries: [
|
||||
{
|
||||
field: 'unique_value.test',
|
||||
operator: 'included',
|
||||
type: 'match_any',
|
||||
value: ['foo'],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
goToExceptionsTab();
|
||||
});
|
||||
|
||||
it('Add comment on a new exception, add another comment has unicode from a different user and copy to clipboard', () => {
|
||||
// User 1
|
||||
// open add exception modal
|
||||
addExceptionFlyoutFromViewerHeader();
|
||||
|
||||
cy.get(LOADING_SPINNER).should('not.exist');
|
||||
|
||||
// add exception item conditions
|
||||
addExceptionConditions(getException());
|
||||
|
||||
// add exception item name
|
||||
addExceptionFlyoutItemName('My item name');
|
||||
|
||||
// add exception comment
|
||||
addExceptionComment('User 1 comment');
|
||||
|
||||
// submit
|
||||
submitNewExceptionItem();
|
||||
|
||||
// new exception item displays
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
|
||||
|
||||
// click on show comments
|
||||
clickOnShowComments();
|
||||
// copy the first comment to clipboard
|
||||
clickCopyCommentToClipboard();
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT).eq(0).should('have.text', 'User 1 comment');
|
||||
|
||||
// User 2
|
||||
// Login with different users to validate accessing comments of different users
|
||||
login(ROLES.soc_manager);
|
||||
|
||||
// Navigate to Rule page
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
|
||||
goToExceptionsTab();
|
||||
|
||||
// open edit exception modal
|
||||
openEditException();
|
||||
// add exception comment
|
||||
addExceptionComment('User 2 comment @ using unicode');
|
||||
// submit
|
||||
submitEditedExceptionItem();
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT).eq(0).should('have.text', 'User 1 comment');
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT)
|
||||
.eq(1)
|
||||
.should('have.text', 'User 2 comment @ using unicode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Endpoint exceptions', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
// create rule with exception
|
||||
createEndpointExceptionList().then((response) => {
|
||||
createRule({
|
||||
...getNewRule(),
|
||||
query: '*',
|
||||
index: ['*'],
|
||||
exceptions_list: [
|
||||
{
|
||||
id: (response as ResponseType).body.id,
|
||||
list_id: (response as ResponseType).body.list_id,
|
||||
type: (response as ResponseType).body.type,
|
||||
namespace_type: (response as ResponseType).body.namespace_type,
|
||||
},
|
||||
],
|
||||
rule_id: '2',
|
||||
});
|
||||
});
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
goToEndpointExceptionsTab();
|
||||
});
|
||||
|
||||
it('Add comment on a new exception, and add another comment has unicode character from a different user', () => {
|
||||
// User 1
|
||||
// The Endpoint will populated with predefined fields
|
||||
|
||||
// open add exception modal
|
||||
openExceptionFlyoutFromEmptyViewerPrompt();
|
||||
|
||||
// for endpoint exceptions, must specify OS
|
||||
selectOs('windows');
|
||||
|
||||
// add exception item conditions
|
||||
addExceptionConditions({
|
||||
field: 'event.code',
|
||||
operator: 'is',
|
||||
values: ['foo'],
|
||||
});
|
||||
// add exception comment
|
||||
addExceptionComment('User 1 comment');
|
||||
|
||||
// add exception item name
|
||||
addExceptionFlyoutItemName('Endpoint exception');
|
||||
|
||||
// submit
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Endpoint Exception will move to Endpoint List under Exception tab of rule
|
||||
goToEndpointExceptionsTab();
|
||||
|
||||
// click on show comments
|
||||
clickOnShowComments();
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT).eq(0).should('have.text', 'User 1 comment');
|
||||
|
||||
// User 2
|
||||
// Login with different users to validate accessing comments of different users
|
||||
login(ROLES.soc_manager);
|
||||
|
||||
// Navigate to Rule page
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
|
||||
// Endpoint Exception will move to Endpoint List under Exception tab of rule
|
||||
goToEndpointExceptionsTab();
|
||||
|
||||
// open edit exception modal
|
||||
openEditException();
|
||||
// add exception comment
|
||||
addExceptionComment('User 2 comment @ using unicode');
|
||||
// submit
|
||||
submitEditedExceptionItem();
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT).eq(0).should('have.text', 'User 1 comment');
|
||||
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT)
|
||||
.eq(1)
|
||||
.should('have.text', 'User 2 comment @ using unicode');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -62,20 +62,25 @@ import {
|
|||
} from '../../../tasks/api_calls/exceptions';
|
||||
import { getExceptionList } from '../../../objects/exception';
|
||||
|
||||
// Test Skipped until we fix the Flyout rerendering issue
|
||||
// https://github.com/elastic/kibana/issues/154994
|
||||
|
||||
// NOTE: You might look at these tests and feel they're overkill,
|
||||
// but the exceptions flyout has a lot of logic making it difficult
|
||||
// to test in enzyme and very small changes can inadvertently add
|
||||
// bugs. As the complexity within the builder grows, these should
|
||||
// ensure the most basic logic holds.
|
||||
describe('Exceptions flyout', () => {
|
||||
describe.skip('Exceptions flyout', { testIsolation: false }, () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
// this is a made-up index that has just the necessary
|
||||
// mappings to conduct tests, avoiding loading large
|
||||
// amounts of data like in auditbeat_exceptions
|
||||
esArchiverLoad('exceptions');
|
||||
esArchiverLoad('conflicts_1');
|
||||
esArchiverLoad('conflicts_2');
|
||||
// Comment the Conflicts here as they are skipped
|
||||
// esArchiverLoad('conflicts_1');
|
||||
// esArchiverLoad('conflicts_2');
|
||||
login();
|
||||
createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) =>
|
||||
createRule(
|
||||
getNewRule({
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import { esArchiverResetKibana, esArchiverUnload } from '../../../tasks/es_archiver';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import {
|
||||
openExceptionFlyoutFromEmptyViewerPrompt,
|
||||
goToExceptionsTab,
|
||||
} from '../../../tasks/rule_details';
|
||||
import {
|
||||
addExceptionFlyoutItemName,
|
||||
addTwoAndedConditions,
|
||||
addTwoORedConditions,
|
||||
submitNewExceptionItem,
|
||||
} from '../../../tasks/exceptions';
|
||||
import {
|
||||
EXCEPTION_CARD_ITEM_NAME,
|
||||
EXCEPTION_CARD_ITEM_CONDITIONS,
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER,
|
||||
} from '../../../screens/exceptions';
|
||||
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
|
||||
|
||||
describe(
|
||||
'Add multiple conditions and validate the generated exceptions',
|
||||
{ testIsolation: false },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
// At least create Rule with exceptions_list to be able to view created exceptions
|
||||
createRule({
|
||||
...getNewRule(),
|
||||
query: 'agent.name:*',
|
||||
index: ['exceptions*'],
|
||||
exceptions_list: [],
|
||||
rule_id: '2',
|
||||
});
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
goToExceptionsTab();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
esArchiverUnload('exceptions');
|
||||
});
|
||||
const exceptionName = 'My item name';
|
||||
|
||||
it('Use multipe AND conditions and validate it generates one exception', () => {
|
||||
// open add exception modal
|
||||
openExceptionFlyoutFromEmptyViewerPrompt();
|
||||
|
||||
// add exception item name
|
||||
addExceptionFlyoutItemName(exceptionName);
|
||||
|
||||
// add Two ANDed condition
|
||||
addTwoAndedConditions('agent.name', 'foo', '@timestamp', '123');
|
||||
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Only one Exception should generated
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
|
||||
|
||||
// validate the And operator is displayed correctly
|
||||
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', exceptionName);
|
||||
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should(
|
||||
'have.text',
|
||||
' agent.nameIS fooAND @timestampIS 123'
|
||||
);
|
||||
});
|
||||
|
||||
it('Use multipe OR conditions and validate it generates multiple exceptions', () => {
|
||||
// open add exception modal
|
||||
openExceptionFlyoutFromEmptyViewerPrompt();
|
||||
|
||||
// add exception item name
|
||||
addExceptionFlyoutItemName(exceptionName);
|
||||
|
||||
// exception item 1
|
||||
// add Two ORed condition
|
||||
addTwoORedConditions('agent.name', 'foo', '@timestamp', '123');
|
||||
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Two Exceptions should be generated
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 2);
|
||||
|
||||
// validate the details of the first exception
|
||||
cy.get(EXCEPTION_CARD_ITEM_NAME).eq(0).should('have.text', exceptionName);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
addExceptionEntryFieldMatchIncludedValue,
|
||||
addExceptionEntryFieldValue,
|
||||
addExceptionEntryOperatorValue,
|
||||
addExceptionFlyoutItemName,
|
||||
submitNewExceptionItem,
|
||||
} from '../../../tasks/exceptions';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import {
|
||||
goToExceptionsTab,
|
||||
openExceptionFlyoutFromEmptyViewerPrompt,
|
||||
} from '../../../tasks/rule_details';
|
||||
import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW } from '../../../screens/lists';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { cleanKibana } from '../../../tasks/common';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../../urls/navigation';
|
||||
import {
|
||||
createListsIndex,
|
||||
waitForListsIndex,
|
||||
waitForValueListsModalToBeLoaded,
|
||||
selectValueListType,
|
||||
selectValueListsFile,
|
||||
uploadValueList,
|
||||
openValueListsModal,
|
||||
deleteValueListsFile,
|
||||
closeValueListsModal,
|
||||
} from '../../../tasks/lists';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { esArchiverLoad, esArchiverUnload } from '../../../tasks/es_archiver';
|
||||
import {
|
||||
CLOSE_ALERTS_CHECKBOX,
|
||||
EXCEPTIONS_TABLE_MODAL,
|
||||
EXCEPTION_CARD_ITEM_CONDITIONS,
|
||||
EXCEPTION_CARD_ITEM_NAME,
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER,
|
||||
NO_EXCEPTIONS_EXIST_PROMPT,
|
||||
} from '../../../screens/exceptions';
|
||||
|
||||
const goToRulesAndOpenValueListModal = () => {
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
waitForListsIndex();
|
||||
waitForValueListsModalToBeLoaded();
|
||||
openValueListsModal();
|
||||
};
|
||||
|
||||
describe('Use Value list in exception entry', () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
login();
|
||||
esArchiverLoad('exceptions');
|
||||
createRule({
|
||||
...getNewRule(),
|
||||
query: 'user.name:*',
|
||||
index: ['exceptions*'],
|
||||
exceptions_list: [],
|
||||
rule_id: '2',
|
||||
});
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
});
|
||||
beforeEach(() => {
|
||||
createListsIndex();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
esArchiverUnload('exceptions');
|
||||
});
|
||||
|
||||
it('Should use value list in exception entry, and validate deleting value list prompt', () => {
|
||||
const ITEM_NAME = 'Exception item with value list';
|
||||
const ITEM_FIELD = 'agent.name';
|
||||
|
||||
goToRulesAndOpenValueListModal();
|
||||
|
||||
// Add new value list of type keyword
|
||||
const listName = 'value_list.txt';
|
||||
selectValueListType('keyword');
|
||||
selectValueListsFile(listName);
|
||||
uploadValueList();
|
||||
|
||||
cy.get(VALUE_LISTS_TABLE)
|
||||
.find(VALUE_LISTS_ROW)
|
||||
.should(($row) => {
|
||||
expect($row.text()).to.contain(listName);
|
||||
expect($row.text()).to.contain('Keywords');
|
||||
});
|
||||
closeValueListsModal();
|
||||
goToRuleDetails();
|
||||
goToExceptionsTab();
|
||||
|
||||
// open add exception modal
|
||||
openExceptionFlyoutFromEmptyViewerPrompt();
|
||||
|
||||
// add exception item name
|
||||
addExceptionFlyoutItemName(ITEM_NAME);
|
||||
|
||||
addExceptionEntryFieldValue(ITEM_FIELD, 0);
|
||||
addExceptionEntryOperatorValue('is in list', 0);
|
||||
|
||||
addExceptionEntryFieldMatchIncludedValue('value_list.txt', 0);
|
||||
|
||||
// The Close all alerts that match attributes in this exception option is disabled
|
||||
cy.get(CLOSE_ALERTS_CHECKBOX).should('exist');
|
||||
cy.get(CLOSE_ALERTS_CHECKBOX).should('have.attr', 'disabled');
|
||||
|
||||
// Create exception
|
||||
submitNewExceptionItem();
|
||||
|
||||
// displays existing exception items
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
|
||||
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('not.exist');
|
||||
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', ITEM_NAME);
|
||||
cy.get(EXCEPTION_CARD_ITEM_CONDITIONS).should(
|
||||
'have.text',
|
||||
` ${ITEM_FIELD}included in value_list.txt`
|
||||
);
|
||||
|
||||
// Go back to value list to delete the existing one
|
||||
goToRulesAndOpenValueListModal();
|
||||
|
||||
deleteValueListsFile(listName);
|
||||
|
||||
// Toast should be shown because of exception reference
|
||||
cy.get(EXCEPTIONS_TABLE_MODAL).should('exist');
|
||||
});
|
||||
});
|
|
@ -107,6 +107,9 @@ describe('Add endpoint exception from rule details', () => {
|
|||
// open add exception modal
|
||||
openExceptionFlyoutFromEmptyViewerPrompt();
|
||||
|
||||
// submit button is disabled if no paramerters were added
|
||||
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
|
||||
|
||||
// for endpoint exceptions, must specify OS
|
||||
selectOs('windows');
|
||||
|
||||
|
|
|
@ -5,26 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LOADING_INDICATOR } from '../../../screens/security_header';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { ALERTS_COUNT, EMPTY_ALERT_TABLE } from '../../../screens/alerts';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import {
|
||||
addExceptionFromFirstAlert,
|
||||
goToClosedAlertsOnRuleDetailsPage,
|
||||
goToOpenedAlertsOnRuleDetailsPage,
|
||||
} from '../../../tasks/alerts';
|
||||
import {
|
||||
addExceptionEntryFieldValue,
|
||||
addExceptionEntryFieldValueValue,
|
||||
addExceptionEntryOperatorValue,
|
||||
addExceptionFlyoutItemName,
|
||||
editException,
|
||||
editExceptionFlyoutItemName,
|
||||
selectBulkCloseAlerts,
|
||||
submitEditedExceptionItem,
|
||||
submitNewExceptionItem,
|
||||
} from '../../../tasks/exceptions';
|
||||
import {
|
||||
esArchiverLoad,
|
||||
|
@ -89,49 +81,7 @@ describe('Add exception using data views from rule details', () => {
|
|||
esArchiverUnload('exceptions_2');
|
||||
});
|
||||
|
||||
it('Creates an exception item from alert actions overflow menu', () => {
|
||||
cy.get(LOADING_INDICATOR).should('not.exist');
|
||||
addExceptionFromFirstAlert();
|
||||
|
||||
addExceptionEntryFieldValue('agent.name', 0);
|
||||
addExceptionEntryOperatorValue('is', 0);
|
||||
addExceptionEntryFieldValueValue('foo', 0);
|
||||
|
||||
addExceptionFlyoutItemName(ITEM_NAME);
|
||||
selectBulkCloseAlerts();
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Alerts table should now be empty from having added exception and closed
|
||||
// matching alert
|
||||
cy.get(EMPTY_ALERT_TABLE).should('exist');
|
||||
|
||||
// Closed alert should appear in table
|
||||
goToClosedAlertsOnRuleDetailsPage();
|
||||
cy.get(ALERTS_COUNT).should('exist');
|
||||
cy.get(ALERTS_COUNT).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`);
|
||||
|
||||
// Remove the exception and load an event that would have matched that exception
|
||||
// to show that said exception now starts to show up again
|
||||
goToExceptionsTab();
|
||||
|
||||
// when removing exception and again, no more exist, empty screen shows again
|
||||
removeException();
|
||||
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
|
||||
|
||||
// load more docs
|
||||
esArchiverLoad('exceptions_2');
|
||||
|
||||
// now that there are no more exceptions, the docs should match and populate alerts
|
||||
goToAlertsTab();
|
||||
goToOpenedAlertsOnRuleDetailsPage();
|
||||
waitForTheRuleToBeExecuted();
|
||||
waitForAlertsToPopulate();
|
||||
|
||||
cy.get(ALERTS_COUNT).should('exist');
|
||||
cy.get(ALERTS_COUNT).should('have.text', '2 alerts');
|
||||
});
|
||||
|
||||
it('Creates an exception item', () => {
|
||||
it('Creates an exception item and close all matching alerts', () => {
|
||||
goToExceptionsTab();
|
||||
// when no exceptions exist, empty component shows with action to add exception
|
||||
cy.get(NO_EXCEPTIONS_EXIST_PROMPT).should('exist');
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
esArchiverLoad,
|
||||
esArchiverUnload,
|
||||
esArchiverResetKibana,
|
||||
} from '../../../tasks/es_archiver';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { createRule, deleteCustomRule } from '../../../tasks/api_calls/rules';
|
||||
import { editException, editExceptionFlyoutItemName } from '../../../tasks/exceptions';
|
||||
import { EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
|
||||
import {
|
||||
CONFIRM_BTN,
|
||||
MANAGE_EXCEPTION_CREATE_BUTTON_MENU,
|
||||
MANAGE_EXCEPTION_CREATE_BUTTON_EXCEPTION,
|
||||
RULE_ACTION_LINK_RULE_SWITCH,
|
||||
} from '../../../screens/exceptions';
|
||||
|
||||
describe('Add/edit exception from exception management page', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
esArchiverLoad('exceptions');
|
||||
createRule(getNewRule());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
esArchiverUnload('exceptions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
deleteCustomRule();
|
||||
});
|
||||
|
||||
describe('create exception item', () => {
|
||||
it('create exception item', () => {
|
||||
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
|
||||
cy.get(MANAGE_EXCEPTION_CREATE_BUTTON_MENU).click();
|
||||
cy.get(MANAGE_EXCEPTION_CREATE_BUTTON_EXCEPTION).click();
|
||||
|
||||
// edit exception item name
|
||||
editExceptionFlyoutItemName('Name');
|
||||
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
|
||||
|
||||
// should select some rules
|
||||
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
|
||||
|
||||
// select rule
|
||||
cy.get(RULE_ACTION_LINK_RULE_SWITCH).find('button').click();
|
||||
|
||||
// should be available to submit
|
||||
cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,22 +5,27 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getExceptionList } from '../../../objects/exception';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
import { getExceptionList } from '../../../../objects/exception';
|
||||
import { getNewRule } from '../../../../objects/rule';
|
||||
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { exceptionsListDetailsUrl } from '../../../urls/navigation';
|
||||
import { login, visitWithoutDateRange } from '../../../../tasks/login';
|
||||
import { createRule } from '../../../../tasks/api_calls/rules';
|
||||
import { EXCEPTIONS_URL, exceptionsListDetailsUrl } from '../../../../urls/navigation';
|
||||
import {
|
||||
createSharedExceptionList,
|
||||
editExceptionLisDetails,
|
||||
linkSharedListToRulesFromListDetails,
|
||||
saveLinkedRules,
|
||||
validateSharedListLinkedRules,
|
||||
waitForExceptionListDetailToBeLoaded,
|
||||
} from '../../../tasks/exceptions_table';
|
||||
import { createExceptionList } from '../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
} from '../../../../tasks/exceptions_table';
|
||||
import { createExceptionList } from '../../../../tasks/api_calls/exceptions';
|
||||
import {
|
||||
EXCEPTIONS_LIST_MANAGEMENT_NAME,
|
||||
EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION,
|
||||
} from '../../../screens/exceptions';
|
||||
EXCEPTION_LIST_DETAILS_LINK_RULES_BTN,
|
||||
} from '../../../../screens/exceptions';
|
||||
|
||||
const LIST_NAME = 'My exception list';
|
||||
const UPDATED_LIST_NAME = 'Updated exception list';
|
||||
|
@ -34,7 +39,9 @@ const getExceptionList1 = () => ({
|
|||
list_id: 'exception_list_test',
|
||||
});
|
||||
|
||||
describe('Exception list management page', () => {
|
||||
const EXCEPTION_LIST_NAME = 'Newly created list';
|
||||
|
||||
describe('Exception list detail page', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
|
@ -54,15 +61,17 @@ describe('Exception list management page', () => {
|
|||
})
|
||||
)
|
||||
);
|
||||
createRule(getNewRule({ name: 'Rule to link to shared list' }));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id));
|
||||
waitForExceptionListDetailToBeLoaded();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
});
|
||||
|
||||
it('Edits list details', () => {
|
||||
it('Should edit list details', () => {
|
||||
visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id));
|
||||
waitForExceptionListDetailToBeLoaded();
|
||||
// Check list details are loaded
|
||||
cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', LIST_NAME);
|
||||
cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', LIST_DESCRIPTION);
|
||||
|
@ -92,4 +101,28 @@ describe('Exception list management page', () => {
|
|||
visitWithoutDateRange(exceptionsListDetailsUrl(getExceptionList1().list_id));
|
||||
cy.get(EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION).should('have.text', 'Add a description');
|
||||
});
|
||||
|
||||
it('Should create a new list and link it to two rules', () => {
|
||||
createSharedExceptionList(
|
||||
{ name: 'Newly created list', description: 'This is my list.' },
|
||||
true
|
||||
);
|
||||
|
||||
// After creation - directed to list detail page
|
||||
cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', EXCEPTION_LIST_NAME);
|
||||
|
||||
// Open Link rules flyout
|
||||
cy.get(EXCEPTION_LIST_DETAILS_LINK_RULES_BTN).click();
|
||||
|
||||
// Link the first two Rules
|
||||
linkSharedListToRulesFromListDetails(2);
|
||||
|
||||
// Save the 2 linked Rules
|
||||
saveLinkedRules();
|
||||
|
||||
const linkedRulesNames = ['Rule to link to shared list', 'New Rule Test'];
|
||||
|
||||
// Validate the number of linked rules as well as the Rules' names
|
||||
validateSharedListLinkedRules(2, linkedRulesNames);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
esArchiverLoad,
|
||||
esArchiverUnload,
|
||||
esArchiverResetKibana,
|
||||
} from '../../../tasks/es_archiver';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import {
|
||||
addExceptionFlyoutItemName,
|
||||
editException,
|
||||
editExceptionFlyoutItemName,
|
||||
linkFirstRuleOnExceptionFlyout,
|
||||
linkFirstSharedListOnExceptionFlyout,
|
||||
editFirstExceptionItemInListDetailPage,
|
||||
submitEditedExceptionItem,
|
||||
submitNewExceptionItem,
|
||||
deleteFirstExceptionItemInListDetailPage,
|
||||
} from '../../../tasks/exceptions';
|
||||
import { DETECTIONS_RULE_MANAGEMENT_URL, EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
|
||||
import {
|
||||
CONFIRM_BTN,
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER,
|
||||
EXCEPTION_CARD_ITEM_NAME,
|
||||
EXCEPTIONS_LIST_MANAGEMENT_NAME,
|
||||
EXECPTION_ITEM_CARD_HEADER_TITLE,
|
||||
EMPTY_EXCEPTIONS_VIEWER,
|
||||
} from '../../../screens/exceptions';
|
||||
import { goToRuleDetails } from '../../../tasks/alerts_detection_rules';
|
||||
import { goToExceptionsTab } from '../../../tasks/rule_details';
|
||||
import {
|
||||
addExceptionListFromSharedExceptionListHeaderMenu,
|
||||
createSharedExceptionList,
|
||||
findSharedExceptionListItemsByName,
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
} from '../../../tasks/exceptions_table';
|
||||
|
||||
describe('Add, edit and delete exception', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
esArchiverLoad('exceptions');
|
||||
|
||||
createRule(getNewRule());
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
});
|
||||
after(() => {
|
||||
esArchiverUnload('exceptions');
|
||||
});
|
||||
|
||||
const exceptionName = 'My item name';
|
||||
const exceptionNameEdited = 'My item name edited';
|
||||
const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.name';
|
||||
const EXCEPTION_LIST_NAME = 'Newly created list';
|
||||
|
||||
describe('Add, Edit and delete Exception item', () => {
|
||||
it('should create exception item from Shared Exception List page and linked to a Rule', () => {
|
||||
// Click on "Create shared exception list" button on the header
|
||||
// Click on "Create exception item"
|
||||
addExceptionListFromSharedExceptionListHeaderMenu();
|
||||
|
||||
// Add exception item name
|
||||
addExceptionFlyoutItemName(exceptionName);
|
||||
|
||||
// Add Condition
|
||||
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
|
||||
|
||||
// Confirm button should disabled until a rule(s) is selected
|
||||
cy.get(CONFIRM_BTN).should('have.attr', 'disabled');
|
||||
|
||||
// select rule
|
||||
linkFirstRuleOnExceptionFlyout();
|
||||
|
||||
// should be able to submit
|
||||
cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled');
|
||||
|
||||
submitNewExceptionItem();
|
||||
|
||||
// Navigate to Rule page
|
||||
visitWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
|
||||
goToRuleDetails();
|
||||
|
||||
goToExceptionsTab();
|
||||
|
||||
// Only one Exception should generated
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', 1);
|
||||
|
||||
// validate the And operator is displayed correctly
|
||||
cy.get(EXCEPTION_CARD_ITEM_NAME).should('have.text', exceptionName);
|
||||
});
|
||||
|
||||
it('should create exception item from Shared Exception List page, linked to a Shared List and validate Edit/delete in list detail page', function () {
|
||||
createSharedExceptionList(
|
||||
{ name: 'Newly created list', description: 'This is my list.' },
|
||||
true
|
||||
);
|
||||
|
||||
// After creation - directed to list detail page
|
||||
cy.get(EXCEPTIONS_LIST_MANAGEMENT_NAME).should('have.text', EXCEPTION_LIST_NAME);
|
||||
|
||||
// Go back to Shared Exception List
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
|
||||
// Click on "Create shared exception list" button on the header
|
||||
// Click on "Create exception item"
|
||||
addExceptionListFromSharedExceptionListHeaderMenu();
|
||||
|
||||
// Add exception item name
|
||||
addExceptionFlyoutItemName(exceptionName);
|
||||
|
||||
// Add Condition
|
||||
editException(FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD, 0, 0);
|
||||
|
||||
// select shared list radio option and select the first one
|
||||
linkFirstSharedListOnExceptionFlyout();
|
||||
|
||||
submitNewExceptionItem();
|
||||
|
||||
// New exception is added to the new List
|
||||
findSharedExceptionListItemsByName(`${EXCEPTION_LIST_NAME}`, [exceptionName]);
|
||||
|
||||
// Click on the first exception overflow menu items
|
||||
// Open the edit modal
|
||||
editFirstExceptionItemInListDetailPage();
|
||||
// edit exception item name
|
||||
editExceptionFlyoutItemName(exceptionNameEdited);
|
||||
|
||||
// submit
|
||||
submitEditedExceptionItem();
|
||||
|
||||
// check the new name after edit
|
||||
cy.get(EXECPTION_ITEM_CARD_HEADER_TITLE).should('have.text', exceptionNameEdited);
|
||||
|
||||
// Click on the first exception overflow menu items
|
||||
// delete the exception
|
||||
deleteFirstExceptionItemInListDetailPage();
|
||||
|
||||
cy.get(EMPTY_EXCEPTIONS_VIEWER).should('exist');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createRule } from '../../../../tasks/api_calls/rules';
|
||||
import { getExceptionList } from '../../../../objects/exception';
|
||||
import { assertNumberOfExceptionItemsExists } from '../../../../tasks/exceptions';
|
||||
import {
|
||||
assertExceptionListsExists,
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId,
|
||||
findSharedExceptionListItemsByName,
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
} from '../../../../tasks/exceptions_table';
|
||||
import { login, visitWithoutDateRange } from '../../../../tasks/login';
|
||||
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
|
||||
import {
|
||||
createExceptionList,
|
||||
createExceptionListItem,
|
||||
} from '../../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
import { getNewRule } from '../../../../objects/rule';
|
||||
|
||||
const expiredDate = new Date(Date.now() - 1000000).toISOString();
|
||||
const futureDate = new Date(Date.now() + 1000000).toISOString();
|
||||
const EXCEPTION_LIST_NAME = 'My test list';
|
||||
const EXCEPTION_LIST_TO_DUPLICATE_NAME = 'A test list 2';
|
||||
const EXCEPTION_LIST_ITEM_NAME = 'Sample Exception List Item 1';
|
||||
const EXCEPTION_LIST_ITEM_NAME_2 = 'Sample Exception List Item 2';
|
||||
|
||||
const getExceptionList1 = () => ({
|
||||
...getExceptionList(),
|
||||
name: EXCEPTION_LIST_NAME,
|
||||
list_id: 'exception_list_1',
|
||||
});
|
||||
const getExceptionList2 = () => ({
|
||||
...getExceptionList(),
|
||||
name: EXCEPTION_LIST_TO_DUPLICATE_NAME,
|
||||
list_id: 'exception_list_2',
|
||||
});
|
||||
|
||||
describe('Duplicate List', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
|
||||
createRule(getNewRule({ name: 'Another rule' }));
|
||||
|
||||
// Create exception list associated with a rule
|
||||
createExceptionList(getExceptionList2(), getExceptionList2().list_id).then((response) =>
|
||||
createRule(
|
||||
getNewRule({
|
||||
exceptions_list: [
|
||||
{
|
||||
id: response.body.id,
|
||||
list_id: getExceptionList2().list_id,
|
||||
type: getExceptionList2().type,
|
||||
namespace_type: getExceptionList2().namespace_type,
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Create exception list not used by any rules
|
||||
createExceptionList(getExceptionList1(), getExceptionList1().list_id).as(
|
||||
'exceptionListResponse'
|
||||
);
|
||||
// Create exception list associated with a rule
|
||||
createExceptionList(getExceptionList2(), getExceptionList2().list_id);
|
||||
|
||||
createExceptionListItem(getExceptionList2().list_id, {
|
||||
list_id: getExceptionList2().list_id,
|
||||
item_id: 'simple_list_item_1',
|
||||
tags: [],
|
||||
type: 'simple',
|
||||
description: 'Test exception item',
|
||||
name: EXCEPTION_LIST_ITEM_NAME,
|
||||
namespace_type: 'single',
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'match_any',
|
||||
value: ['some host', 'another host'],
|
||||
},
|
||||
],
|
||||
expire_time: expiredDate,
|
||||
});
|
||||
createExceptionListItem(getExceptionList2().list_id, {
|
||||
list_id: getExceptionList2().list_id,
|
||||
item_id: 'simple_list_item_2',
|
||||
tags: [],
|
||||
type: 'simple',
|
||||
description: 'Test exception item',
|
||||
name: EXCEPTION_LIST_ITEM_NAME_2,
|
||||
namespace_type: 'single',
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'match_any',
|
||||
value: ['some host', 'another host'],
|
||||
},
|
||||
],
|
||||
expire_time: futureDate,
|
||||
});
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
});
|
||||
|
||||
it('Duplicate exception list with expired items', function () {
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId(getExceptionList2().list_id, true);
|
||||
|
||||
// After duplication - check for new list
|
||||
assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]);
|
||||
|
||||
findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [
|
||||
EXCEPTION_LIST_ITEM_NAME,
|
||||
EXCEPTION_LIST_ITEM_NAME_2,
|
||||
]);
|
||||
|
||||
assertNumberOfExceptionItemsExists(2);
|
||||
});
|
||||
|
||||
it('Duplicate exception list without expired items', function () {
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId(getExceptionList2().list_id, false);
|
||||
|
||||
// After duplication - check for new list
|
||||
assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]);
|
||||
|
||||
findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [
|
||||
EXCEPTION_LIST_ITEM_NAME_2,
|
||||
]);
|
||||
|
||||
assertNumberOfExceptionItemsExists(1);
|
||||
});
|
||||
});
|
|
@ -4,39 +4,39 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getExceptionList } from '../../../objects/exception';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
|
||||
import { EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
import { getExceptionList } from '../../../../objects/exception';
|
||||
import { getNewRule } from '../../../../objects/rule';
|
||||
import {
|
||||
searchForExceptionList,
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
clearSearchSelection,
|
||||
} from '../../../tasks/exceptions_table';
|
||||
import {
|
||||
EXCEPTIONS_TABLE_LIST_NAME,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../../screens/exceptions';
|
||||
import { createExceptionList } from '../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
EXCEPTIONS_TABLE_LIST_NAME,
|
||||
} from '../../../../screens/exceptions';
|
||||
import { createExceptionList } from '../../../../tasks/api_calls/exceptions';
|
||||
import { createRule } from '../../../../tasks/api_calls/rules';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
import {
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
searchForExceptionList,
|
||||
clearSearchSelection,
|
||||
} from '../../../../tasks/exceptions_table';
|
||||
import { login, visitWithoutDateRange } from '../../../../tasks/login';
|
||||
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
|
||||
|
||||
const EXCEPTION_LIST_NAME = 'My test list';
|
||||
const EXCEPTION_LIST_NAME_TWO = 'A test list 2';
|
||||
|
||||
const getExceptionList1 = () => ({
|
||||
...getExceptionList(),
|
||||
name: 'Test a new list 1',
|
||||
name: EXCEPTION_LIST_NAME,
|
||||
list_id: 'exception_list_1',
|
||||
});
|
||||
|
||||
const getExceptionList2 = () => ({
|
||||
...getExceptionList(),
|
||||
name: 'Test list 2',
|
||||
name: EXCEPTION_LIST_NAME_TWO,
|
||||
list_id: 'exception_list_2',
|
||||
});
|
||||
|
||||
describe('Exceptions Table', () => {
|
||||
before(() => {
|
||||
describe('Filter Lists', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
|
||||
|
@ -59,9 +59,6 @@ describe('Exceptions Table', () => {
|
|||
createExceptionList(getExceptionList1(), getExceptionList1().list_id).as(
|
||||
'exceptionListResponse'
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
});
|
||||
|
@ -88,8 +85,8 @@ describe('Exceptions Table', () => {
|
|||
// Using cy.contains because we do not care about the exact text,
|
||||
// just checking number of lists shown
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(1).should('have.text', 'Test list 2');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(0).should('have.text', 'Test a new list 1');
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(1).should('have.text', EXCEPTION_LIST_NAME_TWO);
|
||||
cy.get(EXCEPTIONS_TABLE_LIST_NAME).eq(0).should('have.text', EXCEPTION_LIST_NAME);
|
||||
|
||||
// Exact phrase search
|
||||
clearSearchSelection();
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
IMPORT_SHARED_EXCEPTION_LISTS_CLOSE_BTN,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../../../screens/exceptions';
|
||||
import {
|
||||
waitForExceptionsTableToBeLoaded,
|
||||
importExceptionLists,
|
||||
importExceptionListWithSelectingOverwriteExistingOption,
|
||||
importExceptionListWithSelectingCreateNewOption,
|
||||
validateImportExceptionListWentSuccessfully,
|
||||
validateImportExceptionListFailedBecauseExistingListFound,
|
||||
} from '../../../../tasks/exceptions_table';
|
||||
import { login, visitWithoutDateRange } from '../../../../tasks/login';
|
||||
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
|
||||
describe('Import Lists', () => {
|
||||
const LIST_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_exception_list.ndjson';
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
});
|
||||
beforeEach(() => {
|
||||
login();
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
cy.intercept(/(\/api\/exception_lists\/_import)/).as('import');
|
||||
});
|
||||
it('Should import exception list successfully if the list does not exist', () => {
|
||||
importExceptionLists(LIST_TO_IMPORT_FILENAME);
|
||||
|
||||
validateImportExceptionListWentSuccessfully();
|
||||
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_CLOSE_BTN).click();
|
||||
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
});
|
||||
|
||||
it('Should not import exception list if it exists', () => {
|
||||
importExceptionLists(LIST_TO_IMPORT_FILENAME);
|
||||
|
||||
validateImportExceptionListFailedBecauseExistingListFound();
|
||||
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
});
|
||||
|
||||
it('Should import exception list if it exists but the user selected overwrite checkbox', () => {
|
||||
importExceptionLists(LIST_TO_IMPORT_FILENAME);
|
||||
|
||||
validateImportExceptionListFailedBecauseExistingListFound();
|
||||
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
|
||||
importExceptionListWithSelectingOverwriteExistingOption();
|
||||
|
||||
validateImportExceptionListWentSuccessfully();
|
||||
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
});
|
||||
|
||||
it('Should import exception list if it exists but the user selected create new checkbox', () => {
|
||||
importExceptionLists(LIST_TO_IMPORT_FILENAME);
|
||||
|
||||
validateImportExceptionListFailedBecauseExistingListFound();
|
||||
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '1');
|
||||
|
||||
importExceptionListWithSelectingCreateNewOption();
|
||||
|
||||
validateImportExceptionListWentSuccessfully();
|
||||
// Validate table items count
|
||||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
});
|
||||
});
|
|
@ -4,18 +4,14 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { getExceptionList, expectedExportedExceptionList } from '../../../../objects/exception';
|
||||
import { getNewRule } from '../../../../objects/rule';
|
||||
|
||||
import { getExceptionList, expectedExportedExceptionList } from '../../../objects/exception';
|
||||
import { getNewRule } from '../../../objects/rule';
|
||||
import { createRule } from '../../../../tasks/api_calls/rules';
|
||||
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../../tasks/login';
|
||||
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../../tasks/login';
|
||||
|
||||
import { EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
|
||||
import {
|
||||
assertExceptionListsExists,
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId,
|
||||
findSharedExceptionListItemsByName,
|
||||
deleteExceptionListWithoutRuleReferenceByListId,
|
||||
deleteExceptionListWithRuleReferenceByListId,
|
||||
exportExceptionList,
|
||||
|
@ -23,21 +19,18 @@ import {
|
|||
createSharedExceptionList,
|
||||
linkRulesToExceptionList,
|
||||
assertNumberLinkedRules,
|
||||
} from '../../../tasks/exceptions_table';
|
||||
} from '../../../../tasks/exceptions_table';
|
||||
import {
|
||||
EXCEPTIONS_LIST_MANAGEMENT_NAME,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../../screens/exceptions';
|
||||
import { createExceptionList, createExceptionListItem } from '../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
import { assertNumberOfExceptionItemsExists } from '../../../tasks/exceptions';
|
||||
} from '../../../../screens/exceptions';
|
||||
import { createExceptionList } from '../../../../tasks/api_calls/exceptions';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
|
||||
import { TOASTER } from '../../../screens/alerts_detection_rules';
|
||||
import { TOASTER } from '../../../../screens/alerts_detection_rules';
|
||||
|
||||
const EXCEPTION_LIST_NAME = 'My shared list';
|
||||
const EXCEPTION_LIST_NAME = 'My test list';
|
||||
const EXCEPTION_LIST_TO_DUPLICATE_NAME = 'A test list 2';
|
||||
const EXCEPTION_LIST_ITEM_NAME = 'Sample Exception List Item 1';
|
||||
const EXCEPTION_LIST_ITEM_NAME_2 = 'Sample Exception List Item 2';
|
||||
|
||||
const getExceptionList1 = () => ({
|
||||
...getExceptionList(),
|
||||
|
@ -51,14 +44,10 @@ const getExceptionList2 = () => ({
|
|||
list_id: 'exception_list_2',
|
||||
});
|
||||
|
||||
const expiredDate = new Date(Date.now() - 1000000).toISOString();
|
||||
const futureDate = new Date(Date.now() + 1000000).toISOString();
|
||||
|
||||
describe('Manage shared exception list', () => {
|
||||
describe('Create/Export/Delete', () => {
|
||||
describe('Manage lists from "Shared Exception Lists" page', () => {
|
||||
describe('Create/Export/Delete List', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
|
||||
createRule(getNewRule({ name: 'Another rule' }));
|
||||
|
||||
|
@ -151,87 +140,4 @@ describe('Manage shared exception list', () => {
|
|||
cy.contains(EXCEPTIONS_TABLE_SHOWING_LISTS, '2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Duplicate', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverResetKibana();
|
||||
login();
|
||||
|
||||
// Create exception list associated with a rule
|
||||
createExceptionList(getExceptionList2(), getExceptionList2().list_id);
|
||||
|
||||
createExceptionListItem(getExceptionList2().list_id, {
|
||||
list_id: getExceptionList2().list_id,
|
||||
item_id: 'simple_list_item_1',
|
||||
tags: [],
|
||||
type: 'simple',
|
||||
description: 'Test exception item',
|
||||
name: EXCEPTION_LIST_ITEM_NAME,
|
||||
namespace_type: 'single',
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'match_any',
|
||||
value: ['some host', 'another host'],
|
||||
},
|
||||
],
|
||||
expire_time: expiredDate,
|
||||
});
|
||||
createExceptionListItem(getExceptionList2().list_id, {
|
||||
list_id: getExceptionList2().list_id,
|
||||
item_id: 'simple_list_item_2',
|
||||
tags: [],
|
||||
type: 'simple',
|
||||
description: 'Test exception item',
|
||||
name: EXCEPTION_LIST_ITEM_NAME_2,
|
||||
namespace_type: 'single',
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'match_any',
|
||||
value: ['some host', 'another host'],
|
||||
},
|
||||
],
|
||||
expire_time: futureDate,
|
||||
});
|
||||
|
||||
visitWithoutDateRange(EXCEPTIONS_URL);
|
||||
waitForExceptionsTableToBeLoaded();
|
||||
});
|
||||
|
||||
it('Duplicate exception list with expired items', function () {
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId(
|
||||
getExceptionList2().list_id,
|
||||
true
|
||||
);
|
||||
|
||||
// After duplication - check for new list
|
||||
assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]);
|
||||
|
||||
findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [
|
||||
EXCEPTION_LIST_ITEM_NAME,
|
||||
EXCEPTION_LIST_ITEM_NAME_2,
|
||||
]);
|
||||
|
||||
assertNumberOfExceptionItemsExists(2);
|
||||
});
|
||||
|
||||
it('Duplicate exception list without expired items', function () {
|
||||
duplicateSharedExceptionListFromListsManagementPageByListId(
|
||||
getExceptionList2().list_id,
|
||||
false
|
||||
);
|
||||
|
||||
// After duplication - check for new list
|
||||
assertExceptionListsExists([`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`]);
|
||||
|
||||
findSharedExceptionListItemsByName(`${EXCEPTION_LIST_TO_DUPLICATE_NAME} [Duplicate]`, [
|
||||
EXCEPTION_LIST_ITEM_NAME_2,
|
||||
]);
|
||||
|
||||
assertNumberOfExceptionItemsExists(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,29 +5,27 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { esArchiverResetKibana } from '../../../tasks/es_archiver';
|
||||
import { cleanKibana } from '../../../tasks/common';
|
||||
import { ROLES } from '../../../../common/test';
|
||||
import { getExceptionList } from '../../../objects/exception';
|
||||
import { esArchiverResetKibana } from '../../../../tasks/es_archiver';
|
||||
import { ROLES } from '../../../../../common/test';
|
||||
import { getExceptionList } from '../../../../objects/exception';
|
||||
import {
|
||||
EXCEPTIONS_OVERFLOW_ACTIONS_BTN,
|
||||
EXCEPTIONS_TABLE_SHOWING_LISTS,
|
||||
} from '../../../screens/exceptions';
|
||||
import { createExceptionList, deleteExceptionList } from '../../../tasks/api_calls/exceptions';
|
||||
} from '../../../../screens/exceptions';
|
||||
import { createExceptionList, deleteExceptionList } from '../../../../tasks/api_calls/exceptions';
|
||||
import {
|
||||
dismissCallOut,
|
||||
getCallOut,
|
||||
waitForCallOutToBeShown,
|
||||
} from '../../../tasks/common/callouts';
|
||||
import { login, visitWithoutDateRange } from '../../../tasks/login';
|
||||
import { EXCEPTIONS_URL } from '../../../urls/navigation';
|
||||
} from '../../../../tasks/common/callouts';
|
||||
import { login, visitWithoutDateRange } from '../../../../tasks/login';
|
||||
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
|
||||
|
||||
const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
|
||||
|
||||
describe('All exception lists - read only', () => {
|
||||
describe('Shared exception lists - read only', () => {
|
||||
before(() => {
|
||||
esArchiverResetKibana();
|
||||
cleanKibana();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
|
@ -0,0 +1,2 @@
|
|||
{"_version":"WzI2MzA4LDFd","created_at":"2023-04-26T11:48:28.348Z","created_by":"elastic","description":"Test exception list description","id":"427a27c0-e428-11ed-90b9-27ae1019f1a1","immutable":false,"list_id":"exception_list_1","name":"My shared list","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"745f10e2-8647-4151-9dae-787a24301d2d","type":"detection","updated_at":"2023-04-26T11:48:28.348Z","updated_by":"elastic","version":1}
|
||||
{"exported_exception_list_count":1,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}
|
|
@ -602,3 +602,23 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response<RuleResponse
|
|||
|
||||
return `${JSON.stringify(rule)}\n${JSON.stringify(details)}\n`;
|
||||
};
|
||||
export const getEndpointRule = (): QueryRuleCreateProps => ({
|
||||
type: 'query',
|
||||
query: 'event.kind:alert and event.module:(endpoint and not endgame)',
|
||||
index: ['endpoint.alerts-*'],
|
||||
name: 'Endpoint Rule',
|
||||
description: 'The new rule description.',
|
||||
severity: 'high',
|
||||
risk_score: 17,
|
||||
interval: '10s',
|
||||
from: 'now-50000h',
|
||||
max_signals: 100,
|
||||
exceptions_list: [
|
||||
{
|
||||
id: 'endpoint_list',
|
||||
list_id: 'endpoint_list',
|
||||
namespace_type: 'agnostic',
|
||||
type: 'endpoint',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -130,6 +130,8 @@ export const TOASTER_BODY = '[data-test-subj="globalToastList"] [data-test-subj=
|
|||
|
||||
export const TOASTER_ERROR_BTN = '[data-test-subj="errorToastBtn"]';
|
||||
|
||||
export const TOASTER_CLOSE_ICON = '[data-test-subj="toastCloseButton"]';
|
||||
|
||||
export const RULE_IMPORT_OVERWRITE_CHECKBOX = '[id="importDataModalCheckboxLabel"]';
|
||||
|
||||
export const RULE_IMPORT_OVERWRITE_EXCEPTIONS_CHECKBOX =
|
||||
|
|
|
@ -19,6 +19,8 @@ export const FIELD_INPUT_PARENT =
|
|||
|
||||
export const LOADING_SPINNER = '[data-test-subj="loading-spinner"]';
|
||||
|
||||
export const EXCEPTION_FLYOUT_LOADING_SPINNER = '[data-test-subj="loadingAddExceptionFlyout"]';
|
||||
|
||||
export const OPERATOR_INPUT = '[data-test-subj="operatorAutocompleteComboBox"]';
|
||||
|
||||
export const VALUES_INPUT =
|
||||
|
@ -29,6 +31,9 @@ export const VALUES_MATCH_ANY_INPUT =
|
|||
|
||||
export const ADD_AND_BTN = '[data-test-subj="exceptionsAndButton"]';
|
||||
|
||||
export const VALUES_MATCH_INCLUDED_INPUT =
|
||||
'[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] [data-test-subj="comboBoxInput"]';
|
||||
|
||||
export const ADD_OR_BTN = '[data-test-subj="exceptionsOrButton"]';
|
||||
|
||||
export const ADD_NESTED_BTN = '[data-test-subj="exceptionsNestedButton"]';
|
||||
|
@ -99,7 +104,7 @@ export const EXCEPTION_FLYOUT_VERSION_CONFLICT =
|
|||
|
||||
export const EXCEPTION_FLYOUT_LIST_DELETED_ERROR = '[data-test-subj="errorCalloutContainer"]';
|
||||
|
||||
// Exceptions all items view
|
||||
// Exceptions all items view in Rule pages
|
||||
export const NO_EXCEPTIONS_EXIST_PROMPT =
|
||||
'[data-test-subj="exceptionItemViewerEmptyPrompts-empty"]';
|
||||
|
||||
|
@ -121,6 +126,13 @@ export const EXCEPTION_CARD_ITEM_NAME = '[data-test-subj="exceptionItemCardHeade
|
|||
export const EXCEPTION_CARD_ITEM_CONDITIONS =
|
||||
'[data-test-subj="exceptionItemCardConditions-condition"]';
|
||||
|
||||
// Affected Rules
|
||||
export const EXCEPTION_CARD_ITEM_AFFECTED_RULES =
|
||||
'[data-test-subj="exceptionItemCardMetaInfo-affectedRulesButton"]';
|
||||
|
||||
export const EXCEPTION_CARD_ITEM_AFFECTED_RULES_MENU_ITEM =
|
||||
'div.euiContextMenuPanel button.euiContextMenuItem';
|
||||
|
||||
// Exception flyout components
|
||||
export const EXCEPTION_ITEM_NAME_INPUT = 'input[data-test-subj="exceptionFlyoutNameInput"]';
|
||||
|
||||
|
@ -138,6 +150,22 @@ export const OS_SELECTION_SECTION = '[data-test-subj="osSelectionDropdown"]';
|
|||
|
||||
export const OS_INPUT = '[data-test-subj="osSelectionDropdown"] [data-test-subj="comboBoxInput"]';
|
||||
|
||||
// Exception Item comments
|
||||
export const EXCEPTION_COMMENTS_ACCORDION_BTN = '[data-test-subj="exceptionItemCommentsAccordion"]';
|
||||
|
||||
export const EXCEPTION_COMMENT_TEXT_AREA = '[data-test-subj="newExceptionItemCommentTextArea"]';
|
||||
|
||||
export const EXCEPTION_ITEM_VIEWER_CONTAINER_SHOW_COMMENTS_BTN =
|
||||
'[data-test-subj="exceptionsViewerCommentAccordion"]';
|
||||
|
||||
export const EXCEPTION_ITEM_COMMENTS_CONTAINER =
|
||||
'[data-test-subj="exceptionsViewerCommentAccordion"] li';
|
||||
|
||||
export const EXCEPTION_ITEM_COMMENTS_CONTAINER_TEXT =
|
||||
'[data-test-subj="exceptionsViewerCommentAccordion"] li div.euiCommentEvent__body';
|
||||
|
||||
export const EXCEPTION_ITEM_COMMENT_COPY_BTN = '[data-test-subj="clipboard"]';
|
||||
|
||||
// Shared Exception List Management Page
|
||||
export const MANAGE_EXCEPTION_CREATE_BUTTON_MENU =
|
||||
'[data-test-subj="manageExceptionListCreateButton"]';
|
||||
|
@ -150,6 +178,8 @@ export const MANAGE_EXCEPTION_CREATE_LIST_BUTTON =
|
|||
|
||||
export const RULE_ACTION_LINK_RULE_SWITCH = '[data-test-subj="ruleActionLinkRuleSwitch"]';
|
||||
|
||||
export const LINK_TO_SHARED_LIST_RADIO = '[data-test-subj="addToListsRadioOptionLabel"]';
|
||||
|
||||
export const CREATE_SHARED_EXCEPTION_LIST_NAME_INPUT =
|
||||
'input[data-test-subj="createSharedExceptionListNameInput"]';
|
||||
|
||||
|
@ -159,14 +189,8 @@ export const CREATE_SHARED_EXCEPTION_LIST_DESCRIPTION_INPUT =
|
|||
export const CREATE_SHARED_EXCEPTION_LIST_BTN =
|
||||
'button[data-test-subj="exception-lists-form-create-shared"]';
|
||||
|
||||
export const exceptionsTableListManagementListContainerByListId = (listId: string) =>
|
||||
`[data-test-subj="exceptionsManagementListCard-${listId}"]`;
|
||||
// Exception List detail page
|
||||
|
||||
export const LINKED_RULES_BADGE = '[data-test-subj="exceptionListCardLinkedRulesBadge"]';
|
||||
|
||||
export const MANAGE_RULES_SAVE = '[data-test-subj="manageListRulesSaveButton"]';
|
||||
|
||||
// Exception list management
|
||||
export const EXCEPTIONS_LIST_MANAGEMENT_NAME =
|
||||
'[data-test-subj="exceptionListManagementTitleText"]';
|
||||
|
||||
|
@ -175,7 +199,6 @@ export const EXCEPTIONS_LIST_MANAGEMENT_EDIT_NAME_BTN =
|
|||
|
||||
export const EXCEPTIONS_LIST_MANAGEMENT_EDIT_MODAL_NAME_INPUT =
|
||||
'[data-test-subj="editModalNameTextField"]';
|
||||
|
||||
export const EXCEPTIONS_LIST_MANAGEMENT_DESCRIPTION =
|
||||
'[data-test-subj="exceptionListManagementDescriptionText"]';
|
||||
|
||||
|
@ -188,3 +211,62 @@ export const EXCEPTIONS_LIST_DETAILS_HEADER =
|
|||
'[data-test-subj="exceptionListManagementPageHeader"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAILS_CARD_ITEM_NAME = '[data-test-subj="exceptionItemCardHeader"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAILS_LINK_RULES_BTN =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsLinkRulesButton"]';
|
||||
|
||||
// Import shared exception list
|
||||
export const IMPORT_SHARED_EXCEPTION_LISTS_BTN = '[data-test-subj="importSharedExceptionList"]';
|
||||
|
||||
export const IMPORT_SHARED_EXCEPTION_LISTS_CONFIRM_BTN =
|
||||
'[data-test-subj="exception-lists-form-import-action"]';
|
||||
|
||||
export const IMPORT_SHARED_EXCEPTION_LISTS_CLOSE_BTN =
|
||||
'[data-test-subj="exceptionListsImportFormCloseBTN"]';
|
||||
|
||||
export const IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_EXISTING_CHECKBOX =
|
||||
'[data-test-subj="importExceptionListOverwriteExistingCheckbox"]';
|
||||
|
||||
export const IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_CREATE_NEW_CHECKBOX =
|
||||
'[data-test-subj="importExceptionListCreateNewCheckbox"]';
|
||||
|
||||
export const exceptionsTableListManagementListContainerByListId = (listId: string) =>
|
||||
`[data-test-subj="exceptionsManagementListCard-${listId}"]`;
|
||||
|
||||
export const LINKED_RULES_BADGE = '[data-test-subj="exceptionListCardLinkedRulesBadge"]';
|
||||
|
||||
export const MANAGE_RULES_SAVE = '[data-test-subj="manageListRulesSaveButton"]';
|
||||
|
||||
export const LINK_RULES_FLYOUT_LINK_SWITCH = '[data-test-subj="ruleActionLinkRuleSwitch"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsLinkedRulesMenuEmptyButton"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU_ITEM =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsLinkedRulesMenuMenuPanel"] div button';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_MENU_ITEMS =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsMenuActionsButtonIcon"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_EXPORT_BTN =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsMenuActionsActionItem1"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_DUPLICATE_BTN =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsMenuActionsActionItem3"]';
|
||||
|
||||
export const EXCEPTION_LIST_DETAIL_DELETE_BTN =
|
||||
'[data-test-subj="exceptionListManagementRightSideMenuItemsMenuActionsActionItem3"]';
|
||||
|
||||
// Exception card in Shared List Detail Page
|
||||
export const EXCEPTION_ITEM_HEADER_ACTION_MENU =
|
||||
'[data-test-subj="exceptionItemCardHeaderButtonIcon"]';
|
||||
|
||||
export const EXCEPTION_ITEM_OVERFLOW_ACTION_EDIT =
|
||||
'[data-test-subj="exceptionItemCardHeaderActionItemedit"]';
|
||||
|
||||
export const EXCEPTION_ITEM_OVERFLOW_ACTION_DELETE =
|
||||
'[data-test-subj="exceptionItemCardHeaderActionItemdelete"]';
|
||||
|
||||
export const EXECPTION_ITEM_CARD_HEADER_TITLE = '[data-test-subj="exceptionItemCardHeaderTitle"]';
|
||||
|
||||
export const EMPTY_EXCEPTIONS_VIEWER = '[data-test-subj="emptyViewerState"]';
|
||||
|
|
|
@ -65,6 +65,7 @@ import {
|
|||
DUPLICATE_WITHOUT_EXCEPTIONS_OPTION,
|
||||
DUPLICATE_WITH_EXCEPTIONS_OPTION,
|
||||
DUPLICATE_WITH_EXCEPTIONS_WITHOUT_EXPIRED_OPTION,
|
||||
TOASTER_CLOSE_ICON,
|
||||
} from '../screens/alerts_detection_rules';
|
||||
import type { RULES_MONITORING_TABLE } from '../screens/alerts_detection_rules';
|
||||
import { EUI_CHECKBOX } from '../screens/common/controls';
|
||||
|
@ -571,6 +572,10 @@ export const clickErrorToastBtn = () => {
|
|||
cy.get(TOASTER_ERROR_BTN).click();
|
||||
};
|
||||
|
||||
export const closeErrorToast = () => {
|
||||
cy.get(TOASTER_CLOSE_ICON).click();
|
||||
};
|
||||
|
||||
export const goToEditRuleActionsSettingsOf = (name: string) => {
|
||||
goToTheRuleDetailsOf(name);
|
||||
goToRuleEditSettings();
|
||||
|
|
|
@ -28,7 +28,22 @@ import {
|
|||
EXCEPTION_FIELD_MAPPING_CONFLICTS_TOOLTIP,
|
||||
EXCEPTION_FIELD_MAPPING_CONFLICTS_ACCORDION_ICON,
|
||||
EXCEPTION_FIELD_MAPPING_CONFLICTS_DESCRIPTION,
|
||||
EXCEPTION_COMMENT_TEXT_AREA,
|
||||
EXCEPTION_COMMENTS_ACCORDION_BTN,
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER_SHOW_COMMENTS_BTN,
|
||||
EXCEPTION_ITEM_COMMENTS_CONTAINER,
|
||||
EXCEPTION_ITEM_COMMENT_COPY_BTN,
|
||||
VALUES_MATCH_INCLUDED_INPUT,
|
||||
EXCEPTION_ITEM_VIEWER_CONTAINER,
|
||||
EXCEPTION_CARD_ITEM_AFFECTED_RULES,
|
||||
EXCEPTION_CARD_ITEM_AFFECTED_RULES_MENU_ITEM,
|
||||
ADD_AND_BTN,
|
||||
ADD_OR_BTN,
|
||||
RULE_ACTION_LINK_RULE_SWITCH,
|
||||
LINK_TO_SHARED_LIST_RADIO,
|
||||
EXCEPTION_ITEM_HEADER_ACTION_MENU,
|
||||
EXCEPTION_ITEM_OVERFLOW_ACTION_EDIT,
|
||||
EXCEPTION_ITEM_OVERFLOW_ACTION_DELETE,
|
||||
} from '../screens/exceptions';
|
||||
|
||||
export const assertNumberOfExceptionItemsExists = (numberOfItems: number) => {
|
||||
|
@ -98,6 +113,10 @@ export const addExceptionEntryFieldMatchAnyValue = (value: string, index = 0) =>
|
|||
cy.get(VALUES_MATCH_ANY_INPUT).eq(index).type(`${value}{enter}`);
|
||||
cy.get(EXCEPTION_FLYOUT_TITLE).click();
|
||||
};
|
||||
export const addExceptionEntryFieldMatchIncludedValue = (value: string, index = 0) => {
|
||||
cy.get(VALUES_MATCH_INCLUDED_INPUT).eq(index).type(`${value}{enter}`);
|
||||
cy.get(EXCEPTION_FLYOUT_TITLE).click();
|
||||
};
|
||||
|
||||
export const closeExceptionBuilderFlyout = () => {
|
||||
cy.get(CANCEL_BTN).click();
|
||||
|
@ -170,3 +189,91 @@ export const selectOs = (os: string) => {
|
|||
cy.get(OS_SELECTION_SECTION).should('exist');
|
||||
cy.get(OS_INPUT).type(`${os}{downArrow}{enter}`);
|
||||
};
|
||||
|
||||
export const addExceptionComment = (comment: string) => {
|
||||
cy.get(EXCEPTION_COMMENTS_ACCORDION_BTN).click();
|
||||
cy.get(EXCEPTION_COMMENT_TEXT_AREA).type(`${comment}`);
|
||||
// cy.root()
|
||||
// .pipe(($el) => {
|
||||
// return $el.find(EXCEPTION_COMMENT_TEXT_AREA);
|
||||
// })
|
||||
// .clear()
|
||||
// .type(`${comment}`)
|
||||
cy.get(EXCEPTION_COMMENT_TEXT_AREA).should('have.value', comment);
|
||||
};
|
||||
export const clickOnShowComments = () => {
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER_SHOW_COMMENTS_BTN).click();
|
||||
};
|
||||
|
||||
export const clickCopyCommentToClipboard = () => {
|
||||
// Disable window prompt which is used in link creation by copy-to-clipboard library
|
||||
// as this prompt pauses test execution during `cypress open`
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT');
|
||||
});
|
||||
cy.get(EXCEPTION_ITEM_COMMENTS_CONTAINER).first().find(EXCEPTION_ITEM_COMMENT_COPY_BTN).click();
|
||||
};
|
||||
|
||||
export const validateExceptionItemAffectsTheCorrectRulesInRulePage = (rulesCount: number) => {
|
||||
cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', rulesCount);
|
||||
cy.get(EXCEPTION_CARD_ITEM_AFFECTED_RULES).should('have.text', `Affects ${rulesCount} rule`);
|
||||
};
|
||||
export const validateExceptionItemFirstAffectedRuleNameInRulePage = (ruleName: string) => {
|
||||
cy.get(EXCEPTION_CARD_ITEM_AFFECTED_RULES).click();
|
||||
|
||||
cy.get(EXCEPTION_CARD_ITEM_AFFECTED_RULES_MENU_ITEM).first().should('have.text', ruleName);
|
||||
};
|
||||
|
||||
export const addTwoAndedConditions = (
|
||||
firstEntryField: string,
|
||||
firstEntryFieldValue: string,
|
||||
secondEntryField: string,
|
||||
secondEntryFieldValue: string
|
||||
) => {
|
||||
addExceptionEntryFieldValue(firstEntryField, 0);
|
||||
addExceptionEntryFieldValueValue(firstEntryFieldValue, 0);
|
||||
|
||||
cy.get(ADD_AND_BTN).click();
|
||||
|
||||
addExceptionEntryFieldValue(secondEntryField, 1);
|
||||
addExceptionEntryFieldValueValue(secondEntryFieldValue, 1);
|
||||
};
|
||||
|
||||
export const addTwoORedConditions = (
|
||||
firstEntryField: string,
|
||||
firstEntryFieldValue: string,
|
||||
secondEntryField: string,
|
||||
secondEntryFieldValue: string
|
||||
) => {
|
||||
addExceptionEntryFieldValue(firstEntryField, 0);
|
||||
addExceptionEntryFieldValueValue(firstEntryFieldValue, 0);
|
||||
|
||||
cy.get(ADD_OR_BTN).click();
|
||||
|
||||
addExceptionEntryFieldValue(secondEntryField, 1);
|
||||
addExceptionEntryFieldValueValue(secondEntryFieldValue, 1);
|
||||
};
|
||||
|
||||
export const linkFirstRuleOnExceptionFlyout = () => {
|
||||
cy.get(RULE_ACTION_LINK_RULE_SWITCH).find('button').click();
|
||||
};
|
||||
|
||||
export const linkFirstSharedListOnExceptionFlyout = () => {
|
||||
cy.get(LINK_TO_SHARED_LIST_RADIO).click();
|
||||
cy.get(RULE_ACTION_LINK_RULE_SWITCH).find('button').click();
|
||||
};
|
||||
|
||||
export const editFirstExceptionItemInListDetailPage = () => {
|
||||
// Click on the first exception overflow menu items
|
||||
cy.get(EXCEPTION_ITEM_HEADER_ACTION_MENU).click();
|
||||
|
||||
// Open the edit modal
|
||||
cy.get(EXCEPTION_ITEM_OVERFLOW_ACTION_EDIT).click();
|
||||
};
|
||||
export const deleteFirstExceptionItemInListDetailPage = () => {
|
||||
// Click on the first exception overflow menu items
|
||||
cy.get(EXCEPTION_ITEM_HEADER_ACTION_MENU).click();
|
||||
|
||||
// Delete exception
|
||||
cy.get(EXCEPTION_ITEM_OVERFLOW_ACTION_DELETE).click();
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INPUT_FILE, TOASTER, TOASTER_BODY } from '../screens/alerts_detection_rules';
|
||||
import {
|
||||
EXCEPTIONS_TABLE,
|
||||
EXCEPTIONS_TABLE_SEARCH,
|
||||
|
@ -35,7 +36,15 @@ import {
|
|||
RULE_ACTION_LINK_RULE_SWITCH,
|
||||
LINKED_RULES_BADGE,
|
||||
MANAGE_RULES_SAVE,
|
||||
IMPORT_SHARED_EXCEPTION_LISTS_BTN,
|
||||
IMPORT_SHARED_EXCEPTION_LISTS_CONFIRM_BTN,
|
||||
EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU,
|
||||
EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU_ITEM,
|
||||
MANAGE_EXCEPTION_CREATE_BUTTON_EXCEPTION,
|
||||
IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_CREATE_NEW_CHECKBOX,
|
||||
IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_EXISTING_CHECKBOX,
|
||||
} from '../screens/exceptions';
|
||||
import { closeErrorToast } from './alerts_detection_rules';
|
||||
import { assertExceptionItemsExists } from './exceptions';
|
||||
|
||||
export const clearSearchSelection = () => {
|
||||
|
@ -46,6 +55,15 @@ export const expandExceptionActions = () => {
|
|||
cy.get(EXCEPTIONS_OVERFLOW_ACTIONS_BTN).first().click();
|
||||
};
|
||||
|
||||
export const importExceptionLists = (listsFile: string) => {
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_BTN).click();
|
||||
cy.get(INPUT_FILE).should('exist');
|
||||
cy.get(INPUT_FILE).trigger('click');
|
||||
cy.get(INPUT_FILE).selectFile(listsFile);
|
||||
cy.get(INPUT_FILE).trigger('change');
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_CONFIRM_BTN).click();
|
||||
};
|
||||
|
||||
export const exportExceptionList = (listId: string) => {
|
||||
cy.get(exceptionsTableListManagementListContainerByListId(listId))
|
||||
.find(EXCEPTIONS_OVERFLOW_ACTIONS_BTN)
|
||||
|
@ -67,7 +85,7 @@ export const linkRulesToExceptionList = (listId: string, ruleSwitch: number = 0)
|
|||
.click();
|
||||
cy.get(EXCEPTIONS_TABLE_LINK_RULES_BTN).first().click();
|
||||
cy.get(RULE_ACTION_LINK_RULE_SWITCH).eq(ruleSwitch).find('button').click();
|
||||
cy.get(MANAGE_RULES_SAVE).first().click();
|
||||
saveLinkedRules();
|
||||
};
|
||||
|
||||
export const deleteExceptionListWithoutRuleReferenceByListId = (listId: string) => {
|
||||
|
@ -198,3 +216,71 @@ export const editExceptionLisDetails = ({
|
|||
|
||||
cy.get(EXCEPTIONS_LIST_EDIT_DETAILS_SAVE_BTN).first().click();
|
||||
};
|
||||
|
||||
export const clickOnLinkRulesByRuleRowOrderInListDetail = (ruleSwitch: number = 0) => {
|
||||
cy.get(RULE_ACTION_LINK_RULE_SWITCH).eq(ruleSwitch).find('button').click();
|
||||
};
|
||||
export const linkSharedListToRulesFromListDetails = (numberOfRules: number) => {
|
||||
for (let i = 0; i < numberOfRules; i++) {
|
||||
clickOnLinkRulesByRuleRowOrderInListDetail(i);
|
||||
}
|
||||
};
|
||||
|
||||
export const saveLinkedRules = () => {
|
||||
cy.get(MANAGE_RULES_SAVE).first().click();
|
||||
};
|
||||
|
||||
export const validateSharedListLinkedRules = (
|
||||
numberOfRules: number,
|
||||
linkedRulesNames: string[]
|
||||
) => {
|
||||
cy.get(EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU).should(
|
||||
'have.text',
|
||||
`Linked to ${numberOfRules} rules`
|
||||
);
|
||||
cy.get(EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU).click();
|
||||
linkedRulesNames.forEach((ruleName) => {
|
||||
cy.get(EXCEPTION_LIST_DETAIL_LINKED_TO_RULES_HEADER_MENU_ITEM).contains('a', ruleName);
|
||||
});
|
||||
};
|
||||
|
||||
export const addExceptionListFromSharedExceptionListHeaderMenu = () => {
|
||||
// Click on "Create shared exception list" button on the header
|
||||
cy.get(MANAGE_EXCEPTION_CREATE_BUTTON_MENU).click();
|
||||
// Click on "Create exception item"
|
||||
cy.get(MANAGE_EXCEPTION_CREATE_BUTTON_EXCEPTION).click();
|
||||
};
|
||||
|
||||
export const importExceptionListWithSelectingOverwriteExistingOption = () => {
|
||||
// { force: true }: The EuiCheckbox component's label covers the checkbox
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_EXISTING_CHECKBOX).check({ force: true });
|
||||
// Close the Error toast to be able to import again
|
||||
closeErrorToast();
|
||||
|
||||
// Import after selecting overwrite option
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_CONFIRM_BTN).click();
|
||||
};
|
||||
|
||||
export const importExceptionListWithSelectingCreateNewOption = () => {
|
||||
// { force: true }: The EuiCheckbox component's label covers the checkbox
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_OVERWRITE_CREATE_NEW_CHECKBOX).check({ force: true });
|
||||
// Close the Error toast to be able to import again
|
||||
closeErrorToast();
|
||||
|
||||
// Import after selecting overwrite option
|
||||
cy.get(IMPORT_SHARED_EXCEPTION_LISTS_CONFIRM_BTN).click();
|
||||
};
|
||||
|
||||
export const validateImportExceptionListWentSuccessfully = () => {
|
||||
cy.wait('@import').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
cy.get(TOASTER).should('have.text', `Exception list imported`);
|
||||
});
|
||||
};
|
||||
export const validateImportExceptionListFailedBecauseExistingListFound = () => {
|
||||
cy.wait('@import').then(({ response }) => {
|
||||
cy.wrap(response?.statusCode).should('eql', 200);
|
||||
cy.get(TOASTER).should('have.text', 'There was an error uploading the exception list.');
|
||||
cy.get(TOASTER_BODY).should('contain', 'Found that list_id');
|
||||
});
|
||||
};
|
||||
|
|
|
@ -172,6 +172,7 @@ export const ImportExceptionListFlyout = React.memo(
|
|||
id={'basicCheckboxId'}
|
||||
label={i18n.IMPORT_EXCEPTION_LIST_OVERWRITE}
|
||||
checked={overwrite}
|
||||
data-test-subj="importExceptionListOverwriteExistingCheckbox"
|
||||
onChange={(e) => {
|
||||
setOverwrite(!overwrite);
|
||||
setAsNewList(false);
|
||||
|
@ -180,6 +181,7 @@ export const ImportExceptionListFlyout = React.memo(
|
|||
<EuiCheckbox
|
||||
id={'createNewListCheckbox'}
|
||||
label={i18n.IMPORT_EXCEPTION_LIST_AS_NEW_LIST}
|
||||
data-test-subj="importExceptionListCreateNewCheckbox"
|
||||
checked={asNewList}
|
||||
onChange={(e) => {
|
||||
setAsNewList(!asNewList);
|
||||
|
@ -193,6 +195,7 @@ export const ImportExceptionListFlyout = React.memo(
|
|||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="exceptionListsImportFormCloseBTN"
|
||||
iconType="cross"
|
||||
onClick={() => setDisplayImportListFlyout(false)}
|
||||
flush="left"
|
||||
|
|
|
@ -512,7 +512,11 @@ export const SharedLists = React.memo(() => {
|
|||
]}
|
||||
/>
|
||||
</EuiPopover>,
|
||||
<EuiButton iconType={'importAction'} onClick={() => setDisplayImportListFlyout(true)}>
|
||||
<EuiButton
|
||||
data-test-subj="importSharedExceptionList"
|
||||
iconType={'importAction'}
|
||||
onClick={() => setDisplayImportListFlyout(true)}
|
||||
>
|
||||
{i18n.IMPORT_EXCEPTION_LIST_BUTTON}
|
||||
</EuiButton>,
|
||||
]}
|
||||
|
|
|
@ -8,8 +8,14 @@
|
|||
import expect from '@kbn/expect';
|
||||
import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants';
|
||||
|
||||
import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock';
|
||||
import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock';
|
||||
import {
|
||||
getCreateExceptionListItemMinimalSchemaMock,
|
||||
getCreateExceptionListItemNewerVersionSchemaMock,
|
||||
} from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock';
|
||||
import {
|
||||
getCreateExceptionListDetectionSchemaMock,
|
||||
getCreateExceptionListMinimalSchemaMock,
|
||||
} from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { ROLES } from '@kbn/security-solution-plugin/common/test';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
@ -47,81 +53,330 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
it('should be able to reimport a rule referencing an exception list with existing comments', async () => {
|
||||
// create an exception list
|
||||
const { body: exceptionBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
describe('Endpoint Exception', () => {
|
||||
/*
|
||||
Following the release of version 8.7, this test can be considered as an evaluation of exporting
|
||||
an outdated List Item. A notable distinction lies in the absence of the "expire_time" property
|
||||
within the getCreateExceptionListMinimalSchemaMock, which allows for differentiation between older
|
||||
and newer versions. The rationale behind this approach is the lack of version tracking for both List and Rule,
|
||||
thereby enabling simulation of migration scenarios.
|
||||
*/
|
||||
it('should be able to reimport a rule referencing an old version of endpoint exception list with existing comments', async () => {
|
||||
// create an exception list
|
||||
const { body: exceptionBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
// create an exception list item
|
||||
const { body: exceptionItemBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
...getCreateExceptionListItemMinimalSchemaMock(),
|
||||
comments: [{ comment: 'this exception item rocks' }],
|
||||
})
|
||||
.expect(200);
|
||||
// create an exception list item
|
||||
const { body: exceptionItemBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
...getCreateExceptionListItemMinimalSchemaMock(), // using Old version of Exception List
|
||||
comments: [{ comment: 'this exception item rocks' }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItem } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
const { body: exceptionItem } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
expect(exceptionItem.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItem.comments[0].created_at}`,
|
||||
created_by: 'soc_manager',
|
||||
id: `${exceptionItem.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
|
||||
await createRule(supertest, log, {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
expect(exceptionItem.comments).to.eql([
|
||||
{
|
||||
id: exceptionBody.id,
|
||||
list_id: exceptionBody.list_id,
|
||||
type: exceptionBody.type,
|
||||
namespace_type: exceptionBody.namespace_type,
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItem.comments[0].created_at}`,
|
||||
created_by: 'soc_manager',
|
||||
id: `${exceptionItem.comments[0].id}`,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
await createRule(supertest, log, {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id: exceptionBody.id,
|
||||
list_id: exceptionBody.list_id,
|
||||
type: exceptionBody.type,
|
||||
namespace_type: exceptionBody.namespace_type,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true&overwrite_exceptions=true`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach('file', Buffer.from(body), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItemFind2 } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// NOTE: Existing comment is uploaded successfully
|
||||
// however, the meta now reflects who imported it,
|
||||
// not who created the initial comment
|
||||
expect(exceptionItemFind2.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItemFind2.comments[0].created_at}`,
|
||||
created_by: 'elastic',
|
||||
id: `${exceptionItemFind2.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
it('should be able to reimport a rule referencing a new version of endpoint exception list with existing comments', async () => {
|
||||
// create an exception list
|
||||
const { body: exceptionBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListMinimalSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true&overwrite_exceptions=true`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach('file', Buffer.from(body), 'rules.ndjson')
|
||||
.expect(200);
|
||||
// create an exception list item
|
||||
const { body: exceptionItemBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
...getCreateExceptionListItemNewerVersionSchemaMock(), // using newer version of Exception List
|
||||
comments: [{ comment: 'this exception item rocks' }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItemFind2 } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
const { body: exceptionItem } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// NOTE: Existing comment is uploaded successfully
|
||||
// however, the meta now reflects who imported it,
|
||||
// not who created the initial comment
|
||||
expect(exceptionItemFind2.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItemFind2.comments[0].created_at}`,
|
||||
created_by: 'elastic',
|
||||
id: `${exceptionItemFind2.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
expect(exceptionItem.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItem.comments[0].created_at}`,
|
||||
created_by: 'soc_manager',
|
||||
id: `${exceptionItem.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
|
||||
await createRule(supertest, log, {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id: exceptionBody.id,
|
||||
list_id: exceptionBody.list_id,
|
||||
type: exceptionBody.type,
|
||||
namespace_type: exceptionBody.namespace_type,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true&overwrite_exceptions=true`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach('file', Buffer.from(body), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItemFind2 } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// NOTE: Existing comment is uploaded successfully
|
||||
// however, the meta now reflects who imported it,
|
||||
// not who created the initial comment
|
||||
expect(exceptionItemFind2.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItemFind2.comments[0].created_at}`,
|
||||
created_by: 'elastic',
|
||||
id: `${exceptionItemFind2.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Detection Exception', () => {
|
||||
/*
|
||||
Following the release of version 8.7, this test can be considered as an evaluation of exporting
|
||||
an outdated List Item. A notable distinction lies in the absence of the "expire_time" property
|
||||
within the getCreateExceptionListMinimalSchemaMock, which allows for differentiation between older
|
||||
and newer versions. The rationale behind this approach is the lack of version tracking for both List and Rule,
|
||||
thereby enabling simulation of migration scenarios.
|
||||
*/
|
||||
it('should be able to reimport a rule referencing an old version of detection exception list with existing comments', async () => {
|
||||
// create an exception list
|
||||
const { body: exceptionBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListDetectionSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
// create an exception list item
|
||||
const { body: exceptionItemBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
...getCreateExceptionListItemMinimalSchemaMock(), // using Old version of Exception List
|
||||
comments: [{ comment: 'this exception item rocks' }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItem } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
expect(exceptionItem.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItem.comments[0].created_at}`,
|
||||
created_by: 'soc_manager',
|
||||
id: `${exceptionItem.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
|
||||
await createRule(supertest, log, {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id: exceptionBody.id,
|
||||
list_id: exceptionBody.list_id,
|
||||
type: exceptionBody.type,
|
||||
namespace_type: exceptionBody.namespace_type,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true&overwrite_exceptions=true`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach('file', Buffer.from(body), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItemFind2 } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// NOTE: Existing comment is uploaded successfully
|
||||
// however, the meta now reflects who imported it,
|
||||
// not who created the initial comment
|
||||
expect(exceptionItemFind2.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItemFind2.comments[0].created_at}`,
|
||||
created_by: 'elastic',
|
||||
id: `${exceptionItemFind2.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to reimport a rule referencing a new version of detection exception list with existing comments', async () => {
|
||||
// create an exception list
|
||||
const { body: exceptionBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getCreateExceptionListDetectionSchemaMock())
|
||||
.expect(200);
|
||||
|
||||
// create an exception list item
|
||||
const { body: exceptionItemBody } = await supertestWithoutAuth
|
||||
.post(EXCEPTION_LIST_ITEM_URL)
|
||||
.auth(ROLES.soc_manager, 'changeme')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
...getCreateExceptionListItemNewerVersionSchemaMock(), // using newer version of Exception List
|
||||
comments: [{ comment: 'this exception item rocks' }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItem } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
expect(exceptionItem.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItem.comments[0].created_at}`,
|
||||
created_by: 'soc_manager',
|
||||
id: `${exceptionItem.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
|
||||
await createRule(supertest, log, {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id: exceptionBody.id,
|
||||
list_id: exceptionBody.list_id,
|
||||
type: exceptionBody.type,
|
||||
namespace_type: exceptionBody.namespace_type,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_export`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200)
|
||||
.parse(binaryToString);
|
||||
|
||||
await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true&overwrite_exceptions=true`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach('file', Buffer.from(body), 'rules.ndjson')
|
||||
.expect(200);
|
||||
|
||||
const { body: exceptionItemFind2 } = await supertest
|
||||
.get(`${EXCEPTION_LIST_ITEM_URL}?item_id=${exceptionItemBody.item_id}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// NOTE: Existing comment is uploaded successfully
|
||||
// however, the meta now reflects who imported it,
|
||||
// not who created the initial comment
|
||||
expect(exceptionItemFind2.comments).to.eql([
|
||||
{
|
||||
comment: 'this exception item rocks',
|
||||
created_at: `${exceptionItemFind2.comments[0].created_at}`,
|
||||
created_by: 'elastic',
|
||||
id: `${exceptionItemFind2.comments[0].id}`,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
toNdJsonString,
|
||||
getImportExceptionsListItemSchemaMock,
|
||||
getImportExceptionsListSchemaMock,
|
||||
getImportExceptionsListItemNewerVersionSchemaMock,
|
||||
} from '@kbn/lists-plugin/common/schemas/request/import_exceptions_schema.mock';
|
||||
import { ROLES } from '@kbn/security-solution-plugin/common/test';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
@ -1421,6 +1422,53 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
await deleteAllExceptions(supertest, log);
|
||||
});
|
||||
|
||||
/*
|
||||
Following the release of version 8.7, this test can be considered as an evaluation of exporting
|
||||
an outdated List Item. A notable distinction lies in the absence of the "expire_time" property
|
||||
within the getCreateExceptionListMinimalSchemaMock, which allows for differentiation between older
|
||||
and newer versions. The rationale behind this approach is the lack of version tracking for both List and Rule,
|
||||
thereby enabling simulation of migration scenarios.
|
||||
*/
|
||||
it('should be able to import a rule and an old version exception list, then delete it successfully', async () => {
|
||||
const simpleRule = getSimpleRule('rule-1');
|
||||
|
||||
// import old exception version
|
||||
const { body } = await supertest
|
||||
.post(`${DETECTION_ENGINE_RULES_URL}/_import`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.attach(
|
||||
'file',
|
||||
Buffer.from(
|
||||
toNdJsonString([
|
||||
simpleRule,
|
||||
getImportExceptionsListSchemaMock('test_list_id'),
|
||||
getImportExceptionsListItemSchemaMock('test_item_id', 'test_list_id'),
|
||||
])
|
||||
),
|
||||
'rules.ndjson'
|
||||
)
|
||||
.expect(200);
|
||||
expect(body).to.eql({
|
||||
success: true,
|
||||
success_count: 1,
|
||||
rules_count: 1,
|
||||
errors: [],
|
||||
exceptions_errors: [],
|
||||
exceptions_success: true,
|
||||
exceptions_success_count: 1,
|
||||
action_connectors_success: true,
|
||||
action_connectors_success_count: 0,
|
||||
action_connectors_errors: [],
|
||||
action_connectors_warnings: [],
|
||||
});
|
||||
|
||||
// delete the exception list item by its item_id
|
||||
await supertest
|
||||
.delete(`${EXCEPTION_LIST_ITEM_URL}?item_id=${'test_item_id'}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should be able to import a rule and an exception list', async () => {
|
||||
const simpleRule = getSimpleRule('rule-1');
|
||||
|
||||
|
@ -1433,7 +1481,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
toNdJsonString([
|
||||
simpleRule,
|
||||
getImportExceptionsListSchemaMock('test_list_id'),
|
||||
getImportExceptionsListItemSchemaMock('test_item_id', 'test_list_id'),
|
||||
getImportExceptionsListItemNewerVersionSchemaMock('test_item_id', 'test_list_id'),
|
||||
])
|
||||
),
|
||||
'rules.ndjson'
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import type { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants';
|
||||
import {
|
||||
EXCEPTION_LIST_ITEM_URL,
|
||||
EXCEPTION_LIST_URL,
|
||||
LIST_URL,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import type {
|
||||
RuleCreateProps,
|
||||
EqlRuleCreateProps,
|
||||
|
@ -18,7 +22,10 @@ import type {
|
|||
ThresholdRuleCreateProps,
|
||||
} from '@kbn/security-solution-plugin/common/detection_engine/rule_schema';
|
||||
import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock';
|
||||
import { getCreateExceptionListMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock';
|
||||
import {
|
||||
getCreateExceptionListDetectionSchemaMock,
|
||||
getCreateExceptionListMinimalSchemaMock,
|
||||
} from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock';
|
||||
|
||||
import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { ROLES } from '@kbn/security-solution-plugin/common/test';
|
||||
|
@ -834,8 +841,176 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const signalsOpen = await getOpenSignals(supertest, log, es, createdRule);
|
||||
expect(signalsOpen.hits.hits.length).equal(0);
|
||||
});
|
||||
it('should Not allow deleting value list when there are references and ignoreReferences is false', async () => {
|
||||
const valueListId = 'value-list-id';
|
||||
await importFile(supertest, log, 'keyword', ['suricata-sensor-amsterdam'], valueListId);
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getSimpleRule(),
|
||||
query: 'host.name: "suricata-sensor-amsterdam"',
|
||||
};
|
||||
await createRuleWithExceptionEntries(supertest, log, rule, [
|
||||
[
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'list',
|
||||
list: {
|
||||
id: valueListId,
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const deleteReferences = false;
|
||||
const ignoreReferences = false;
|
||||
|
||||
// Delete the value list
|
||||
await supertest
|
||||
.delete(
|
||||
`${LIST_URL}?deleteReferences=${deleteReferences}&id=${valueListId}&ignoreReferences=${ignoreReferences}`
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(409);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Synchronizations', () => {
|
||||
/*
|
||||
This test to mimic if we have two browser tabs, and the user tried to
|
||||
edit an exception in a tab after deleting it in another
|
||||
*/
|
||||
it('should Not edit an exception after being deleted', async () => {
|
||||
const { list_id: skippedListId, ...newExceptionItem } =
|
||||
getCreateExceptionListDetectionSchemaMock();
|
||||
const {
|
||||
body: { id, list_id, namespace_type, type },
|
||||
} = await supertest
|
||||
.post(EXCEPTION_LIST_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(newExceptionItem)
|
||||
.expect(200);
|
||||
|
||||
const ruleWithException: RuleCreateProps = {
|
||||
...getSimpleRule(),
|
||||
exceptions_list: [
|
||||
{
|
||||
id,
|
||||
list_id,
|
||||
namespace_type,
|
||||
type,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await createRule(supertest, log, ruleWithException);
|
||||
|
||||
// Delete the exception
|
||||
await supertest
|
||||
.delete(`${EXCEPTION_LIST_ITEM_URL}?id=${id}&namespace_type=single`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200);
|
||||
|
||||
// Edit after delete as if it was opened in another browser tab
|
||||
const { body } = await supertest
|
||||
.put(`${EXCEPTION_LIST_ITEM_URL}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
id: list_id,
|
||||
item_id: id,
|
||||
name: 'edit',
|
||||
entries: [{ field: 'ss', operator: 'included', type: 'match', value: 'ss' }],
|
||||
namespace_type,
|
||||
description: 'Exception list item - Edit',
|
||||
type: 'simple',
|
||||
})
|
||||
.expect(404);
|
||||
|
||||
expect(body).to.eql({
|
||||
message: `exception list item id: "${list_id}" does not exist`,
|
||||
status_code: 404,
|
||||
});
|
||||
});
|
||||
/*
|
||||
This test to mimic if we have two browser tabs, and the user tried to
|
||||
edit an exception with value-list was deleted in another tab
|
||||
*/
|
||||
it('should Not allow editing an Exception with deleted ValueList', async () => {
|
||||
await createListsIndex(supertest, log);
|
||||
|
||||
const valueListId = 'value-list-id';
|
||||
await importFile(supertest, log, 'keyword', ['suricata-sensor-amsterdam'], valueListId);
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getSimpleRule(),
|
||||
query: 'host.name: "suricata-sensor-amsterdam"',
|
||||
};
|
||||
const { exceptions_list: exceptionsList } = await createRuleWithExceptionEntries(
|
||||
supertest,
|
||||
log,
|
||||
rule,
|
||||
[
|
||||
[
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'list',
|
||||
list: {
|
||||
id: valueListId,
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
const deleteReferences = false;
|
||||
const ignoreReferences = true;
|
||||
|
||||
const { id, list_id, namespace_type } = exceptionsList[0];
|
||||
|
||||
// Delete the value list
|
||||
await supertest
|
||||
.delete(
|
||||
`${LIST_URL}?deleteReferences=${deleteReferences}&id=${valueListId}&ignoreReferences=${ignoreReferences}`
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send()
|
||||
.expect(200);
|
||||
|
||||
// edit the exception with the deleted value list
|
||||
await supertest
|
||||
.put(`${EXCEPTION_LIST_ITEM_URL}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
id: list_id,
|
||||
item_id: id,
|
||||
name: 'edit',
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
operator: 'included',
|
||||
type: 'list',
|
||||
list: {
|
||||
id: valueListId,
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
],
|
||||
namespace_type,
|
||||
description: 'Exception list item - Edit',
|
||||
type: 'simple',
|
||||
})
|
||||
.expect(404);
|
||||
// expect(signalsOpen.hits.hits.length).equal(0);
|
||||
// expect(body).to.eql({
|
||||
// message: `exception list item id: "${list_id}" does not exist`,
|
||||
// status_code: 404,
|
||||
// });
|
||||
await deleteListsIndex(supertest, log);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -10,6 +10,6 @@ import { FtrProviderContext } from '../../common/ftr_provider_context';
|
|||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('detection engine api security and spaces enabled - Group 3', function () {
|
||||
loadTestFile(require.resolve('./create_exceptions'));
|
||||
loadTestFile(require.resolve('./exceptions_workflows'));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "_aZE5nwBOpWiDwiSth_F",
|
||||
"index": "endpoint.alerts-cypress",
|
||||
"source": {
|
||||
"@timestamp": "2023-02-15T16:37:12.225Z",
|
||||
"file": {
|
||||
"Ext": {
|
||||
"code_signature": {
|
||||
"subject_name": "test",
|
||||
"trusted": true
|
||||
}
|
||||
},
|
||||
"path": "123",
|
||||
"hash": {
|
||||
"sha256": "test"
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"hostname": "test.local",
|
||||
"architecture": "x86_64",
|
||||
"os": {
|
||||
"platform": "darwin",
|
||||
"version": "10.16",
|
||||
"family": "darwin",
|
||||
"name": "Mac OS X",
|
||||
"kernel": "21.3.0",
|
||||
"build": "21D62",
|
||||
"type": "macos"
|
||||
},
|
||||
"id": "44426D67-79AB-547C-7777-440AB8F5DDD2",
|
||||
"ip": [
|
||||
"fe80::bade:48ff:fe00:1122",
|
||||
"fe81::4ab:9565:1199:be3",
|
||||
"192.168.5.175",
|
||||
"fe80::40d7:d0ff:fe66:f55",
|
||||
"fe81::40d8:d0ff:fe66:f55",
|
||||
"fe82::c2c:6bdf:3307:dce0",
|
||||
"fe83::5069:fcd5:e31c:7059",
|
||||
"fe80::ce81:b2c:bd2c:69e",
|
||||
"fe80::febc:bbc1:c517:827b",
|
||||
"fe80::6d09:bee6:55a5:539d",
|
||||
"fe80::c920:752e:1e0e:edc9",
|
||||
"fe80::a4a:ca38:761f:83e2"
|
||||
],
|
||||
"mac": [
|
||||
"ad:df:48:00:11:22",
|
||||
"a6:86:e7:ae:5a:b6",
|
||||
"a9:83:e7:ae:5a:b6",
|
||||
"43:d8:d0:66:0f:55",
|
||||
"42:d8:d0:66:0f:57",
|
||||
"82:70:c7:c2:3c:01",
|
||||
"82:70:c6:c2:4c:00",
|
||||
"82:76:a6:c2:3c:05",
|
||||
"82:70:c6:b2:3c:04",
|
||||
"82:71:a6:c2:3c:01"
|
||||
],
|
||||
"name": "siem-kibana"
|
||||
},
|
||||
"agent": {
|
||||
"type": "auditbeat",
|
||||
"version": "8.1.0",
|
||||
"ephemeral_id": "f6df090f-656a-4a79-a6a1-0c8671c9752d",
|
||||
"id": "0ebd469b-c164-4734-00e6-96d018098dc7",
|
||||
"name": "test.local"
|
||||
},
|
||||
"event": {
|
||||
"module": "endpoint",
|
||||
"dataset": "process",
|
||||
"kind": "alert",
|
||||
"category": ["process"],
|
||||
"type": ["start"],
|
||||
"action": "process_started",
|
||||
"code": "test"
|
||||
},
|
||||
"destination": {
|
||||
"port": 80
|
||||
},
|
||||
"process": {
|
||||
"start": "2022-03-04T19:41:32.902Z",
|
||||
"pid": 30884,
|
||||
"working_directory": "/Users/test/security_solution",
|
||||
"hash": {
|
||||
"sha1": "ae2d46c38fa207efbea5fcecd6294eebbf5af00f"
|
||||
},
|
||||
"parent": {
|
||||
"pid": 777
|
||||
},
|
||||
"executable": "/bin/zsh",
|
||||
"name": "zsh",
|
||||
"args": ["-zsh"],
|
||||
"entity_id": "q6pltOhTWlQx3BCD",
|
||||
"entry_leader": {
|
||||
"entity_id": "q6pltOhTWlQx3BCD",
|
||||
"name": "fake entry",
|
||||
"pid": 2342342
|
||||
}
|
||||
},
|
||||
"message": "Process zsh (PID: 27884) by user test STARTED",
|
||||
"user": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"name": "staff",
|
||||
"id": "20"
|
||||
},
|
||||
"effective": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"id": "20"
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"id": "20"
|
||||
}
|
||||
},
|
||||
"name": "test"
|
||||
},
|
||||
"service": {
|
||||
"type": "system"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "8.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,494 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"aliases": {
|
||||
"endgame": {
|
||||
"is_write_index": false
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "5s"
|
||||
},
|
||||
"index.mapping.total_fields.limit": 2000
|
||||
},
|
||||
"index": "endpoint.alerts-cypress",
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {
|
||||
"type": "date"
|
||||
},
|
||||
"agent": {
|
||||
"properties": {
|
||||
"ephemeral_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ecs": {
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"category": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataset": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"properties": {
|
||||
"architecture": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"hostname": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"os": {
|
||||
"properties": {
|
||||
"build": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"family": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"kernel": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"properties": {
|
||||
"args": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"entry_leader": {
|
||||
"properties": {
|
||||
"entity_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"pid": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"executable": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"hash": {
|
||||
"properties": {
|
||||
"sha1": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"properties": {
|
||||
"pid": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pid": {
|
||||
"type": "long"
|
||||
},
|
||||
"start": {
|
||||
"type": "date"
|
||||
},
|
||||
"working_directory": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"properties": {
|
||||
"effective": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "_wZE5nwBOpWiDwiSth_W",
|
||||
"index": "endpoint.alerts-cypress-1",
|
||||
"source": {
|
||||
"@timestamp": "2023-02-15T16:37:12.225Z",
|
||||
"file": {
|
||||
"Ext": {
|
||||
"code_signature": {
|
||||
"subject_name": "test",
|
||||
"trusted": true
|
||||
}
|
||||
},
|
||||
"path": "123",
|
||||
"hash": {
|
||||
"sha256": "test"
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"hostname": "test.local",
|
||||
"architecture": "x86_64",
|
||||
"os": {
|
||||
"platform": "darwin",
|
||||
"version": "10.16",
|
||||
"family": "darwin",
|
||||
"name": "Mac OS X",
|
||||
"kernel": "21.3.0",
|
||||
"build": "21D62",
|
||||
"type": "macos"
|
||||
},
|
||||
"id": "44426D67-79AB-547C-7777-440AB8F5DDD2",
|
||||
"ip": [
|
||||
"fe80::bade:48ff:fe00:1122",
|
||||
"fe81::4ab:9565:1199:be3",
|
||||
"192.168.5.175",
|
||||
"fe80::40d7:d0ff:fe66:f55",
|
||||
"fe81::40d8:d0ff:fe66:f55",
|
||||
"fe82::c2c:6bdf:3307:dce0",
|
||||
"fe83::5069:fcd5:e31c:7059",
|
||||
"fe80::ce81:b2c:bd2c:69e",
|
||||
"fe80::febc:bbc1:c517:827b",
|
||||
"fe80::6d09:bee6:55a5:539d",
|
||||
"fe80::c920:752e:1e0e:edc9",
|
||||
"fe80::a4a:ca38:761f:83e2"
|
||||
],
|
||||
"mac": [
|
||||
"ad:df:48:00:11:22",
|
||||
"a6:86:e7:ae:5a:b6",
|
||||
"a9:83:e7:ae:5a:b6",
|
||||
"43:d8:d0:66:0f:55",
|
||||
"42:d8:d0:66:0f:57",
|
||||
"82:70:c7:c2:3c:01",
|
||||
"82:70:c6:c2:4c:00",
|
||||
"82:76:a6:c2:3c:05",
|
||||
"82:70:c6:b2:3c:04",
|
||||
"82:71:a6:c2:3c:01"
|
||||
],
|
||||
"name": "siem-kibana"
|
||||
},
|
||||
"agent": {
|
||||
"type": "auditbeat",
|
||||
"version": "8.1.0",
|
||||
"ephemeral_id": "f6df090f-656a-4a79-a6a1-0c8671c9752d",
|
||||
"id": "0ebd469b-c164-4734-00e6-96d018098dc7",
|
||||
"name": "test.local"
|
||||
},
|
||||
"event": {
|
||||
"module": "endpoint",
|
||||
"dataset": "process",
|
||||
"kind": "alert",
|
||||
"category": ["process"],
|
||||
"type": ["start"],
|
||||
"action": "process_started",
|
||||
"code": "test"
|
||||
},
|
||||
"destination": {
|
||||
"port": 80
|
||||
},
|
||||
"process": {
|
||||
"start": "2022-03-04T19:41:32.902Z",
|
||||
"pid": 30884,
|
||||
"working_directory": "/Users/test/security_solution",
|
||||
"hash": {
|
||||
"sha1": "ae2d46c38fa207efbea5fcecd6294eebbf5af00f"
|
||||
},
|
||||
"parent": {
|
||||
"pid": 777
|
||||
},
|
||||
"executable": "/bin/zsh",
|
||||
"name": "zsh",
|
||||
"args": ["-zsh"],
|
||||
"entity_id": "q6pltOhTWlQx3BCD",
|
||||
"entry_leader": {
|
||||
"entity_id": "q6pltOhTWlQx3BCD",
|
||||
"name": "fake entry",
|
||||
"pid": 2342342
|
||||
}
|
||||
},
|
||||
"message": "Process zsh (PID: 27884) by user test STARTED",
|
||||
"user": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"name": "staff",
|
||||
"id": "20"
|
||||
},
|
||||
"effective": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"id": "20"
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"id": "505",
|
||||
"group": {
|
||||
"id": "20"
|
||||
}
|
||||
},
|
||||
"name": "test"
|
||||
},
|
||||
"service": {
|
||||
"type": "system"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "8.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,494 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"aliases": {
|
||||
"endgame": {
|
||||
"is_write_index": false
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "5s"
|
||||
},
|
||||
"index.mapping.total_fields.limit": 2000
|
||||
},
|
||||
"index": "endpoint.alerts-cypress-1",
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {
|
||||
"type": "date"
|
||||
},
|
||||
"agent": {
|
||||
"properties": {
|
||||
"ephemeral_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ecs": {
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"category": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataset": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"properties": {
|
||||
"architecture": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"hostname": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"os": {
|
||||
"properties": {
|
||||
"build": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"family": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"kernel": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"properties": {
|
||||
"args": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"entry_leader": {
|
||||
"properties": {
|
||||
"entity_id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"pid": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"executable": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"hash": {
|
||||
"properties": {
|
||||
"sha1": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"properties": {
|
||||
"pid": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pid": {
|
||||
"type": "long"
|
||||
},
|
||||
"start": {
|
||||
"type": "date"
|
||||
},
|
||||
"working_directory": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"properties": {
|
||||
"effective": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"saved": {
|
||||
"properties": {
|
||||
"group": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue