From d644c7085a2efec54073e60c9d3f06686f54fc53 Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Wed, 8 Nov 2023 22:16:23 +0100 Subject: [PATCH] [Security Solution] Unfriendly error message when creating an invalid exception (#168213) (#170764) ## Summary Addresses https://github.com/elastic/kibana/issues/168213 With this PR we limit the length of the comments in exceptions to 30K characters. We took same approach as used in Cases. User will not be allowed to create/edit exception with the comment longer than 30K and validation error will be shown. Right now if user tries to add a very long comment (above 32K characters) the server throws an exception due to the length limitation of the `keyword` type. After the fix, user will see a validation error on putting very long text as a comment Screenshot 2023-11-07 at 16 47 10 --- .../security_solution/common/constants.ts | 5 ++ .../add_exception_flyout/index.test.tsx | 44 +++++++++++++- .../components/add_exception_flyout/index.tsx | 14 +++++ .../add_exception_flyout/reducer.ts | 15 +++++ .../edit_exception_flyout/index.test.tsx | 58 ++++++++++++++++++- .../edit_exception_flyout/index.tsx | 24 +++++++- .../edit_exception_flyout/reducer.ts | 13 +++++ .../components/item_comments/index.test.tsx | 50 ++++++++++++++++ .../components/item_comments/index.tsx | 35 ++++++++--- .../components/item_comments/translations.ts | 7 +++ .../view/components/form.test.tsx | 32 ++++++++++ .../event_filters/view/components/form.tsx | 5 +- .../manage_exceptions.cy.ts | 27 +++------ .../cypress/tasks/exceptions.ts | 3 +- 14 files changed, 300 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index c6edaf898f67f..a7460bcd70345 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -505,3 +505,8 @@ export const DEFAULT_ALERT_TAGS_VALUE = [ i18n.FALSE_POSITIVE, i18n.FURTHER_INVESTIGATION_REQUIRED, ] as const; + +/** + * Max length for the comments within security solution + */ +export const MAX_COMMENT_LENGTH = 30000 as const; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx index 573419676a086..63b0490b91e60 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import type { ReactWrapper } from 'enzyme'; import { mount, shallow } from 'enzyme'; -import { waitFor, render } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; @@ -35,6 +35,7 @@ import { import type { AlertData } from '../../utils/types'; import { useFindRules } from '../../../rule_management/logic/use_find_rules'; import { useFindExceptionListReferences } from '../../logic/use_find_references'; +import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../common/lib/kibana'); @@ -1305,5 +1306,46 @@ describe('When the add exception modal is opened', () => { wrapper.find('button[data-test-subj="addExceptionConfirmButton"]').getDOMNode() ).toBeDisabled(); }); + + test('when there is a comment error has submit button disabled', async () => { + const { getByLabelText, queryByText, getByTestId } = render( + + + + ); + + const commentInput = getByLabelText('Comment Input'); + + const commentErrorMessage = `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.`; + expect(queryByText(commentErrorMessage)).toBeNull(); + + // Put comment with the length above maximum allowed + act(() => { + fireEvent.change(commentInput, { + target: { + value: [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join(''), + }, + }); + fireEvent.blur(commentInput); + }); + expect(queryByText(commentErrorMessage)).not.toBeNull(); + expect(getByTestId('addExceptionConfirmButton')).toBeDisabled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index d4108c3eddede..9eefb96be62c9 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -157,6 +157,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ selectedRulesToAddTo, exceptionListsToAddTo, newComment, + commentErrorExists, itemConditionValidationErrorExists, errorSubmitting, expireTime, @@ -267,6 +268,16 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ [dispatch] ); + const setCommentError = useCallback( + (errorExists: boolean): void => { + dispatch({ + type: 'setCommentError', + errorExists, + }); + }, + [dispatch] + ); + const setBulkCloseIndex = useCallback( (index: string[] | undefined): void => { dispatch({ @@ -445,6 +456,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ exceptionItemName.trim() === '' || exceptionItems.every((item) => item.entries.length === 0) || itemConditionValidationErrorExists || + commentErrorExists || expireErrorExists || (addExceptionToRadioSelection === 'add_to_lists' && isEmpty(exceptionListsToAddTo)) || (addExceptionToRadioSelection === 'select_rules_to_add_to' && @@ -462,6 +474,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ expireErrorExists, selectedRulesToAddTo, listType, + commentErrorExists, ] ); @@ -555,6 +568,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ initialIsOpen={!!newComment} newCommentValue={newComment} newCommentOnChange={setComment} + setCommentError={setCommentError} /> {listType !== ExceptionListTypeEnum.ENDPOINT && ( <> diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/reducer.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/reducer.ts index 04d13c3a1b4e9..ec8040d1fe7cc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/reducer.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/reducer.ts @@ -21,6 +21,7 @@ export interface State { initialItems: ExceptionsBuilderExceptionItem[]; exceptionItems: ExceptionsBuilderReturnExceptionItem[]; newComment: string; + commentErrorExists: boolean; addExceptionToRadioSelection: string; itemConditionValidationErrorExists: boolean; closeSingleAlert: boolean; @@ -40,6 +41,7 @@ export const initialState: State = { exceptionItems: [], exceptionItemMeta: { name: '' }, newComment: '', + commentErrorExists: false, itemConditionValidationErrorExists: false, closeSingleAlert: false, bulkCloseAlerts: false, @@ -76,6 +78,10 @@ export type Action = type: 'setComment'; comment: string; } + | { + type: 'setCommentError'; + errorExists: boolean; + } | { type: 'setCloseSingleAlert'; close: boolean; @@ -127,6 +133,7 @@ export type Action = export const createExceptionItemsReducer = () => + /* eslint complexity: ["error", 21]*/ (state: State, action: Action): State => { switch (action.type) { case 'setExceptionItemMeta': { @@ -172,6 +179,14 @@ export const createExceptionItemsReducer = newComment: comment, }; } + case 'setCommentError': { + const { errorExists } = action; + + return { + ...state, + commentErrorExists: errorExists, + }; + } case 'setCloseSingleAlert': { const { close } = action; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx index faa7c1385142c..077befdad52ba 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { waitFor } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { ThemeProvider } from 'styled-components'; import type { ReactWrapper } from 'enzyme'; import { mount } from 'enzyme'; @@ -34,6 +34,7 @@ import { useFetchIndexPatterns } from '../../logic/use_exception_flyout_data'; import { useCreateOrUpdateException } from '../../logic/use_create_update_exception'; import { useFindExceptionListReferences } from '../../logic/use_find_references'; import * as i18n from './translations'; +import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; const mockTheme = getMockTheme({ eui: { @@ -693,5 +694,60 @@ describe('When the edit exception modal is opened', () => { wrapper.find('button[data-test-subj="editExceptionConfirmButton"]').getDOMNode() ).toBeDisabled(); }); + + test('when there is a comment error has submit button disabled', async () => { + const { getByLabelText, queryByText, getByTestId } = render( + + + + ); + + const commentInput = getByLabelText('Comment Input'); + + const commentErrorMessage = `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.`; + expect(queryByText(commentErrorMessage)).toBeNull(); + + // Put comment with the length above maximum allowed + act(() => { + fireEvent.change(commentInput, { + target: { + value: [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join(''), + }, + }); + fireEvent.blur(commentInput); + }); + expect(queryByText(commentErrorMessage)).not.toBeNull(); + expect(getByTestId('editExceptionConfirmButton')).toBeDisabled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx index 4d9e7c3bbc4ef..6d2526cdbf239 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx @@ -117,6 +117,7 @@ const EditExceptionFlyoutComponent: React.FC = ({ exceptionItems, exceptionItemMeta: { name: exceptionItemName }, newComment, + commentErrorExists, bulkCloseAlerts, disableBulkClose, bulkCloseIndex, @@ -129,6 +130,7 @@ const EditExceptionFlyoutComponent: React.FC = ({ exceptionItems: [itemToEdit], exceptionItemMeta: { name: itemToEdit.name }, newComment: '', + commentErrorExists: false, bulkCloseAlerts: false, disableBulkClose: true, bulkCloseIndex: undefined, @@ -197,6 +199,16 @@ const EditExceptionFlyoutComponent: React.FC = ({ [dispatch] ); + const setCommentError = useCallback( + (errorExists: boolean): void => { + dispatch({ + type: 'setCommentError', + errorExists, + }); + }, + [dispatch] + ); + const setBulkCloseAlerts = useCallback( (bulkClose: boolean): void => { dispatch({ @@ -337,8 +349,17 @@ const EditExceptionFlyoutComponent: React.FC = ({ exceptionItems.every((item) => item.entries.length === 0) || isLoading || entryErrorExists || + expireErrorExists || + commentErrorExists, + [ + isLoading, + entryErrorExists, + exceptionItems, + isSubmitting, + isClosingAlerts, expireErrorExists, - [isLoading, entryErrorExists, exceptionItems, isSubmitting, isClosingAlerts, expireErrorExists] + commentErrorExists, + ] ); return ( @@ -398,6 +419,7 @@ const EditExceptionFlyoutComponent: React.FC = ({ exceptionItemComments={itemToEdit.comments} newCommentValue={newComment} newCommentOnChange={setComment} + setCommentError={setCommentError} /> {listType !== ExceptionListTypeEnum.ENDPOINT && ( <> diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/reducer.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/reducer.ts index e08b3c8d135c0..e6dee3af16572 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/reducer.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/reducer.ts @@ -12,6 +12,7 @@ export interface State { exceptionItems: ExceptionsBuilderReturnExceptionItem[]; exceptionItemMeta: { name: string }; newComment: string; + commentErrorExists: boolean; bulkCloseAlerts: boolean; disableBulkClose: boolean; bulkCloseIndex: string[] | undefined; @@ -29,6 +30,10 @@ export type Action = type: 'setComment'; comment: string; } + | { + type: 'setCommentError'; + errorExists: boolean; + } | { type: 'setBulkCloseAlerts'; bulkClose: boolean; @@ -81,6 +86,14 @@ export const createExceptionItemsReducer = newComment: comment, }; } + case 'setCommentError': { + const { errorExists } = action; + + return { + ...state, + commentErrorExists: errorExists, + }; + } case 'setBulkCloseAlerts': { const { bulkClose } = action; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx index 47933db0b3522..6dd8684eaeaac 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.test.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { act, fireEvent, render } from '@testing-library/react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { EuiTextArea } from '@elastic/eui'; @@ -13,6 +14,7 @@ import { ExceptionItemComments } from '.'; import { TestProviders } from '../../../../common/mock'; import { useCurrentUser } from '../../../../common/lib/kibana'; import { shallow } from 'enzyme'; +import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; jest.mock('../../../../common/lib/kibana'); @@ -38,6 +40,7 @@ describe('ExceptionItemComments', () => { ); @@ -65,6 +68,7 @@ describe('ExceptionItemComments', () => { ); @@ -92,6 +96,7 @@ describe('ExceptionItemComments', () => { ); @@ -106,6 +111,7 @@ describe('ExceptionItemComments', () => { ); @@ -122,6 +128,7 @@ describe('ExceptionItemComments', () => { ); @@ -152,10 +159,53 @@ describe('ExceptionItemComments', () => { ]} newCommentValue={''} newCommentOnChange={mockOnCommentChange} + setCommentError={jest.fn()} /> ); expect(wrapper.find('[data-test-subj="exceptionItemCommentsAccordion"]').exists()).toBeTruthy(); }); + + it('it calls setCommentError on comment error update change', async () => { + const mockSetCommentError = jest.fn(); + const { getByLabelText, queryByText } = render( + + + + ); + + const commentInput = getByLabelText('Comment Input'); + + const commentErrorMessage = `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.`; + expect(queryByText(commentErrorMessage)).toBeNull(); + + // Put comment with the length above maximum allowed + act(() => { + fireEvent.change(commentInput, { + target: { + value: [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join(''), + }, + }); + fireEvent.blur(commentInput); + }); + expect(queryByText(commentErrorMessage)).not.toBeNull(); + expect(mockSetCommentError).toHaveBeenCalledWith(true); + + // Put comment with the allowed length + act(() => { + fireEvent.change(commentInput, { + target: { + value: 'Updating my new comment', + }, + }); + fireEvent.blur(commentInput); + }); + expect(queryByText(commentErrorMessage)).toBeNull(); + expect(mockSetCommentError).toHaveBeenCalledWith(false); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx index 0f32e2b4d1ab8..f262c7a07754a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/index.tsx @@ -5,19 +5,21 @@ * 2.0. */ -import React, { memo, useState, useCallback, useMemo } from 'react'; +import React, { memo, useState, useCallback, useMemo, useEffect } from 'react'; import styled, { css } from 'styled-components'; import type { EuiCommentProps } from '@elastic/eui'; import { EuiTextArea, EuiFlexGroup, EuiFlexItem, + EuiFormRow, EuiAvatar, EuiAccordion, EuiCommentList, EuiText, } from '@elastic/eui'; import type { Comment } from '@kbn/securitysolution-io-ts-list-types'; +import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; import * as i18n from './translations'; import { useCurrentUser } from '../../../../common/lib/kibana'; import { getFormattedComments } from '../../utils/helpers'; @@ -28,6 +30,7 @@ interface ExceptionItemCommentsProps { accordionTitle?: JSX.Element; initialIsOpen?: boolean; newCommentOnChange: (value: string) => void; + setCommentError: (errorExists: boolean) => void; } const COMMENT_ACCORDION_BUTTON_CLASS_NAME = 'exceptionCommentAccordionButton'; @@ -53,8 +56,11 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ accordionTitle, initialIsOpen = false, newCommentOnChange, + setCommentError, }: ExceptionItemCommentsProps) { + const [errorExists, setErrorExists] = useState(false); const [shouldShowComments, setShouldShowComments] = useState(false); + const currentUser = useCurrentUser(); const fullName = currentUser?.fullName; const userName = currentUser?.username; @@ -73,9 +79,14 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ return userName && userName.length > 0 ? userName : i18n.UNKNOWN_AVATAR_NAME; }, [fullName, userEmail, userName]); + useEffect(() => { + setCommentError(errorExists); + }, [errorExists, setCommentError]); + const handleOnChange = useCallback( (event: React.ChangeEvent) => { newCommentOnChange(event.target.value); + setErrorExists(event.target.value.length > MAX_COMMENT_LENGTH); }, [newCommentOnChange] ); @@ -121,14 +132,20 @@ export const ExceptionItemComments = memo(function ExceptionItemComments({ - + + + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts index afe20c6aada98..90c3d9bd0bc48 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/item_comments/translations.ts @@ -32,3 +32,10 @@ export const COMMENTS_HIDE = (comments: number) => values: { comments }, defaultMessage: 'Hide ({comments}) {comments, plural, =1 {Comment} other {Comments}}', }); + +export const COMMENT_MAX_LENGTH_ERROR = (length: number) => + i18n.translate('xpack.securitySolution.rule_exceptions.itemComments.maxLengthError', { + values: { length }, + defaultMessage: + 'The length of the comment is too long. The maximum length is {length} characters.', + }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx index be028512cb0a8..2870a67e7042c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx @@ -25,6 +25,7 @@ import { OperatingSystem } from '@kbn/securitysolution-utils'; import { EventFiltersForm } from './form'; import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data'; import type { PolicyData } from '../../../../../../common/endpoint/types'; +import { MAX_COMMENT_LENGTH } from '../../../../../../common/constants'; jest.mock('../../../../../common/lib/kibana'); jest.mock('../../../../../common/containers/source'); @@ -466,4 +467,35 @@ describe('Event filter form', () => { expect(renderResult.findByTestId('duplicate-fields-warning-message')).not.toBeNull(); }); }); + + describe('Errors', () => { + beforeEach(() => { + render(); + }); + + it('should not show warning text when unique fields are added', async () => { + rerender(); + + const commentInput = renderResult.getByLabelText('Comment Input'); + + expect( + renderResult.queryByText( + `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.` + ) + ).toBeNull(); + act(() => { + fireEvent.change(commentInput, { + target: { + value: [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join(''), + }, + }); + fireEvent.blur(commentInput); + }); + expect( + renderResult.queryByText( + `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.` + ) + ).not.toBeNull(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx index 3b4ff4e394a82..e4e1fa7e14638 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx @@ -133,6 +133,7 @@ export const EventFiltersForm: React.FC(!exception.name); const [newComment, setNewComment] = useState(''); + const [hasCommentError, setHasCommentError] = useState(false); const [hasBeenInputNameVisited, setHasBeenInputNameVisited] = useState(false); const [selectedPolicies, setSelectedPolicies] = useState([]); const isPlatinumPlus = useLicense().isPlatinumPlus(); @@ -173,10 +174,11 @@ export const EventFiltersForm: React.FC e.value !== '' || e.value.length) ); - }, [hasNameError, exception.entries]); + }, [hasCommentError, hasNameError, exception.entries]); const processChanged = useCallback( (updatedItem?: Partial) => { @@ -340,6 +342,7 @@ export const EventFiltersForm: React.FC ), [existingComments, handleOnChangeComment, newComment] diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts index eba0c3a64570f..023140d420f2a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts @@ -6,6 +6,7 @@ */ import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { MAX_COMMENT_LENGTH } from '@kbn/security-solution-plugin/common/constants'; import { getNewRule } from '../../../objects/rule'; import { login } from '../../../tasks/login'; import { visit } from '../../../tasks/navigation'; @@ -20,9 +21,7 @@ import { submitEditedExceptionItem, submitNewExceptionItem, deleteFirstExceptionItemInListDetailPage, - dismissExceptionItemErrorCallOut, addExceptionHugeComment, - submitNewExceptionItemWithFailure, editExceptionComment, } from '../../../tasks/exceptions'; import { EXCEPTIONS_URL } from '../../../urls/navigation'; @@ -42,7 +41,6 @@ import { } from '../../../tasks/exceptions_table'; import { visitRuleDetailsPage } from '../../../tasks/rule_details'; import { deleteEndpointExceptionList, deleteExceptionLists } from '../../../tasks/common'; -import { closeErrorToast } from '../../../tasks/alerts_detection_rules'; describe('Add, edit and delete exception', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { @@ -147,7 +145,7 @@ describe('Add, edit and delete exception', { tags: ['@ess', '@serverless'] }, () cy.get(EMPTY_EXCEPTIONS_VIEWER).should('exist'); }); - it('should handle huge text as a comment gracefully and allow user create exception item after user updates the comment', function () { + it('should not allow to add huge text as a comment', function () { createSharedExceptionList( { name: 'Newly created list', description: 'This is my list.' }, true @@ -173,26 +171,19 @@ describe('Add, edit and delete exception', { tags: ['@ess', '@serverless'] }, () linkFirstSharedListOnExceptionFlyout(); // add exception comment which is super long - addExceptionHugeComment([...new Array(5000).keys()].map((_) => `Test text!`).join('')); - - // submit - submitNewExceptionItemWithFailure(); + addExceptionHugeComment( + [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join('') + ); - // Failed to add exception due to comment length and submit button should be disabled + // submit button should be disabled due to comment length cy.get(CONFIRM_BTN).should('have.attr', 'disabled'); - // Close error toast - closeErrorToast(); - - // Dismiss error callout - dismissExceptionItemErrorCallOut(); - - // Submit button should be enabled after we dismissed error callout - cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled'); - // update exception comment to a reasonable (length wise) text editExceptionComment('Exceptional comment'); + // submit button should be enabled + cy.get(CONFIRM_BTN).should('not.have.attr', 'disabled'); + // submit submitNewExceptionItem(); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts index 00d6c2d405b87..4ed5ff5ef2f8d 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts @@ -237,8 +237,9 @@ export const addExceptionComment = (comment: string) => { export const addExceptionHugeComment = (comment: string) => { cy.get(EXCEPTION_COMMENTS_ACCORDION_BTN).click(); + cy.get(EXCEPTION_COMMENT_TEXT_AREA).type(` {backspace}`); cy.get(EXCEPTION_COMMENT_TEXT_AREA).invoke('val', comment); - cy.get(EXCEPTION_COMMENT_TEXT_AREA).type(`!{backspace}`); + cy.get(EXCEPTION_COMMENT_TEXT_AREA).type(` {backspace}`); cy.get(EXCEPTION_COMMENT_TEXT_AREA).should('have.value', comment); };