Skip to content

Commit

Permalink
[Security Solution][Lists] - Updates exception flyout edit error mess…
Browse files Browse the repository at this point in the history
…ages (#126875) (#127099)

Addresses #126669

Error messages on flyout were being truncated/were out of view.

(cherry picked from commit 50ba761)

Co-authored-by: Yara Tercero <yctercero@users.noreply.github.com>
  • Loading branch information
kibanamachine and yctercero authored Mar 15, 2022
1 parent cbeb60d commit 691cbcd
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,21 @@ import {
EXCEPTION_ITEM_CONTAINER,
ADD_EXCEPTIONS_BTN,
EXCEPTION_FIELD_LIST,
EDIT_EXCEPTIONS_BTN,
EXCEPTION_EDIT_FLYOUT_SAVE_BTN,
EXCEPTION_FLYOUT_VERSION_CONFLICT,
EXCEPTION_FLYOUT_LIST_DELETED_ERROR,
} from '../../screens/exceptions';

import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation';
import { cleanKibana, reload } from '../../tasks/common';
import {
createExceptionList,
createExceptionListItem,
updateExceptionListItem,
deleteExceptionList,
} from '../../tasks/api_calls/exceptions';
import { getExceptionList } from '../../objects/exception';

// NOTE: You might look at these tests and feel they're overkill,
// but the exceptions flyout has a lot of logic making it difficult
Expand All @@ -42,18 +53,28 @@ import { cleanKibana, reload } from '../../tasks/common';
describe('Exceptions flyout', () => {
before(() => {
cleanKibana();
loginAndWaitForPageWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
createCustomRule({ ...getNewRule(), index: ['exceptions-*'] });
reload();
goToRuleDetails();

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

// 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');

loginAndWaitForPageWithoutDateRange(DETECTIONS_RULE_MANAGEMENT_URL);
createExceptionList(getExceptionList(), getExceptionList().list_id).then((response) =>
createCustomRule({
...getNewRule(),
index: ['exceptions-*'],
exceptionLists: [
{
id: response.body.id,
list_id: getExceptionList().list_id,
type: getExceptionList().type,
namespace_type: getExceptionList().namespace_type,
},
],
})
);
reload();
goToRuleDetails();
cy.get(RULE_STATUS).should('have.text', '—');
goToExceptionsTab();
});

Expand All @@ -62,7 +83,12 @@ describe('Exceptions flyout', () => {
});

it('Does not overwrite values and-ed together', () => {
cy.get(ADD_EXCEPTIONS_BTN).click({ force: true });
cy.root()
.pipe(($el) => {
$el.find(ADD_EXCEPTIONS_BTN).trigger('click');
return $el.find(ADD_AND_BTN);
})
.should('be.visible');

// add multiple entries with invalid field values
addExceptionEntryFieldValue('agent.name', 0);
Expand All @@ -80,8 +106,12 @@ describe('Exceptions flyout', () => {
});

it('Does not overwrite values or-ed together', () => {
cy.get(ADD_EXCEPTIONS_BTN).click({ force: true });

cy.root()
.pipe(($el) => {
$el.find(ADD_EXCEPTIONS_BTN).trigger('click');
return $el.find(ADD_AND_BTN);
})
.should('be.visible');
// exception item 1
addExceptionEntryFieldValueOfItemX('agent.name', 0, 0);
cy.get(ADD_AND_BTN).click();
Expand Down Expand Up @@ -197,11 +227,89 @@ describe('Exceptions flyout', () => {
});

it('Contains custom index fields', () => {
cy.get(ADD_EXCEPTIONS_BTN).click({ force: true });

cy.root()
.pipe(($el) => {
$el.find(ADD_EXCEPTIONS_BTN).trigger('click');
return $el.find(ADD_AND_BTN);
})
.should('be.visible');
cy.get(FIELD_INPUT).eq(0).click({ force: true });
cy.get(EXCEPTION_FIELD_LIST).contains('unique_value.test');

closeExceptionBuilderFlyout();
});

describe('flyout errors', () => {
before(() => {
// create exception item via api
createExceptionListItem(getExceptionList().list_id, {
list_id: getExceptionList().list_id,
item_id: 'simple_list_item',
tags: [],
type: 'simple',
description: 'Test exception item',
name: 'Sample Exception List Item',
namespace_type: 'single',
entries: [
{
field: 'host.name',
operator: 'included',
type: 'match_any',
value: ['some host', 'another host'],
},
],
});

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

context('When updating an item with version conflict', () => {
it('Displays version conflict error', () => {
cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible');
cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true });

// update exception item via api
updateExceptionListItem('simple_list_item', {
name: 'Updated item name',
item_id: 'simple_list_item',
tags: [],
type: 'simple',
description: 'Test exception item',
namespace_type: 'single',
entries: [
{
field: 'host.name',
operator: 'included',
type: 'match_any',
value: ['some host', 'another host'],
},
],
});

// try to save and see version conflict error
cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click({ force: true });

cy.get(EXCEPTION_FLYOUT_VERSION_CONFLICT).should('be.visible');

closeExceptionBuilderFlyout();
});
});

context('When updating an item for a list that has since been deleted', () => {
it('Displays missing exception list error', () => {
cy.get(EDIT_EXCEPTIONS_BTN).should('be.visible');
cy.get(EDIT_EXCEPTIONS_BTN).click({ force: true });

// delete exception list via api
deleteExceptionList(getExceptionList().list_id, getExceptionList().namespace_type);

// try to save and see error
cy.get(EXCEPTION_EDIT_FLYOUT_SAVE_BTN).click({ force: true });

cy.get(EXCEPTION_FLYOUT_LIST_DELETED_ERROR).should('be.visible');
});
});
});
});
11 changes: 11 additions & 0 deletions x-pack/plugins/security_solution/cypress/objects/exception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ export interface ExceptionList {
type: 'detection' | 'endpoint';
}

export interface ExceptionListItem {
description: string;
list_id: string;
item_id: string;
name: string;
namespace_type: 'single' | 'agnostic';
tags: string[];
type: 'simple';
entries: Array<{ field: string; operator: string; type: string; value: string[] }>;
}

export const getExceptionList = (): ExceptionList => ({
description: 'Test exception list description',
list_id: 'test_exception_list',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

export const EDIT_EXCEPTIONS_BTN = '[data-test-subj="exceptionsViewerEditBtn"]';

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

export const CLOSE_ALERTS_CHECKBOX =
Expand Down Expand Up @@ -61,3 +63,10 @@ export const EXCEPTION_FIELD_LIST =
'[data-test-subj="comboBoxOptionsList fieldAutocompleteComboBox-optionsList"]';

export const EXCEPTION_FLYOUT_TITLE = '[data-test-subj="exception-flyout-title"]';

export const EXCEPTION_EDIT_FLYOUT_SAVE_BTN = '[data-test-subj="edit-exception-confirm-button"]';

export const EXCEPTION_FLYOUT_VERSION_CONFLICT =
'[data-test-subj="exceptionsFlyoutVersionConflict"]';

export const EXCEPTION_FLYOUT_LIST_DELETED_ERROR = '[data-test-subj="errorCalloutContainer"]';
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const DETAILS_TITLE = '.euiDescriptionList__title';

export const EXCEPTIONS_TAB = '[data-test-subj="exceptionsTab"]';

export const EXCEPTIONS_TAB_SEARCH = '[data-test-subj="exceptionsHeaderSearch"]';

export const FALSE_POSITIVES_DETAILS = 'False positive examples';

export const INDEX_PATTERNS_DETAILS = 'Index patterns';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { ExceptionList } from '../../objects/exception';
import { ExceptionList, ExceptionListItem } from '../../objects/exception';

export const createExceptionList = (
exceptionList: ExceptionList,
Expand All @@ -23,3 +23,58 @@ export const createExceptionList = (
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});

export const createExceptionListItem = (
exceptionListId: string,
exceptionListItem?: ExceptionListItem
) =>
cy.request({
method: 'POST',
url: '/api/exception_lists/items',
body: {
list_id: exceptionListItem?.list_id ?? exceptionListId,
item_id: exceptionListItem?.item_id ?? 'simple_list_item',
tags: exceptionListItem?.tags ?? ['user added string for a tag', 'malware'],
type: exceptionListItem?.type ?? 'simple',
description: exceptionListItem?.description ?? 'This is a sample endpoint type exception',
name: exceptionListItem?.name ?? 'Sample Exception List Item',
entries: exceptionListItem?.entries ?? [
{
field: 'actingProcess.file.signer',
operator: 'excluded',
type: 'exists',
},
{
field: 'host.name',
operator: 'included',
type: 'match_any',
value: ['some host', 'another host'],
},
],
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});

export const updateExceptionListItem = (
exceptionListItemId: string,
exceptionListItemUpdate?: Partial<ExceptionListItem>
) =>
cy.request({
method: 'PUT',
url: '/api/exception_lists/items',
body: {
item_id: exceptionListItemId,
...exceptionListItemUpdate,
},
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});

export const deleteExceptionList = (listId: string, namespaceType: string) =>
cy.request({
method: 'DELETE',
url: `/api/exception_lists?list_id=${listId}&namespace_type=${namespaceType}`,
headers: { 'kbn-xsrf': 'cypress-creds' },
failOnStatusCode: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ export const goToAlertsTab = () => {
};

export const goToExceptionsTab = () => {
cy.get(EXCEPTIONS_TAB).click();
cy.root()
.pipe(($el) => {
$el.find(EXCEPTIONS_TAB).trigger('click');
return $el.find(ADD_EXCEPTIONS_BTN);
})
.should('be.visible');
};

export const removeException = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,27 +410,35 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({
</FlyoutCheckboxesSection>
</>
)}
{updateError != null && (
<EuiFlyoutFooter>
<ErrorCallout
http={http}
errorInfo={updateError}
rule={maybeRule}
onCancel={onCancel}
onSuccess={handleDissasociationSuccess}
onError={handleDissasociationError}
/>
</EuiFlyoutFooter>
)}
{hasVersionConflict && (
<EuiFlyoutFooter>
<EuiCallOut title={i18n.VERSION_CONFLICT_ERROR_TITLE} color="danger" iconType="alert">
<p>{i18n.VERSION_CONFLICT_ERROR_DESCRIPTION}</p>
</EuiCallOut>
</EuiFlyoutFooter>
)}
{updateError == null && (
<EuiFlyoutFooter>

<EuiFlyoutFooter>
{hasVersionConflict && (
<>
<EuiCallOut
title={i18n.VERSION_CONFLICT_ERROR_TITLE}
color="danger"
iconType="alert"
data-test-subj="exceptionsFlyoutVersionConflict"
>
<p>{i18n.VERSION_CONFLICT_ERROR_DESCRIPTION}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</>
)}
{updateError != null && (
<>
<ErrorCallout
http={http}
errorInfo={updateError}
rule={maybeRule}
onCancel={onCancel}
onSuccess={handleDissasociationSuccess}
onError={handleDissasociationError}
/>
<EuiSpacer size="m" />
</>
)}
{updateError === null && (
<FlyoutFooterGroup justifyContent="spaceBetween">
<EuiButtonEmpty data-test-subj="cancelExceptionAddButton" onClick={onCancel}>
{i18n.CANCEL}
Expand All @@ -446,8 +454,8 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({
{i18n.EDIT_EXCEPTION_SAVE_BUTTON}
</EuiButton>
</FlyoutFooterGroup>
</EuiFlyoutFooter>
)}
)}
</EuiFlyoutFooter>
</EuiFlyout>
);
});

0 comments on commit 691cbcd

Please sign in to comment.