66 */
77import * as essSecurityHeaders from '@kbn/test-suites-xpack-security/security_solution_cypress/cypress/screens/security_header' ;
88import * as serverlessSecurityHeaders from '@kbn/test-suites-xpack-security/security_solution_cypress/cypress/screens/serverless_security_header' ;
9- import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants' ;
9+ import {
10+ ENDPOINT_ARTIFACT_LISTS ,
11+ EXCEPTION_LIST_ITEM_URL ,
12+ } from '@kbn/securitysolution-list-constants' ;
13+ import { recurse } from 'cypress-recurse' ;
1014import { ENDPOINT_EXCEPTIONS_PER_POLICY_OPT_IN_ROUTE } from '../../../../../common/endpoint/constants' ;
1115import {
16+ APP_ALERTS_PATH ,
1217 APP_ENDPOINT_EXCEPTIONS_PATH ,
1318 APP_MANAGE_PATH ,
1419 APP_PATH ,
@@ -24,6 +29,10 @@ import {
2429 removeAllArtifacts ,
2530} from '../../tasks/artifacts' ;
2631import { getArtifactsListTestDataForArtifact } from '../../fixtures/artifacts_page' ;
32+ import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts' ;
33+ import type { ReturnTypeFromChainable } from '../../types' ;
34+ import { performUserActions , type FormAction } from '../../tasks/perform_user_actions' ;
35+ import { getArtifactListEmptyStateAddButton } from '../../screens' ;
2736
2837describe (
2938 'Endpoint exceptions - under Security Management/Assets' ,
@@ -102,6 +111,7 @@ describe(
102111 } ) ;
103112 } ) ;
104113
114+ // Only running on ESS, because on Serverless we cannot remove the opt-in status SO
105115 describe ( 'Per-policy opt-in behaviour' , { tags : [ '@ess' ] } , ( ) => {
106116 before ( ( ) => {
107117 removeAllArtifacts ( ) ;
@@ -143,5 +153,264 @@ describe(
143153 } ) ;
144154 } ) ;
145155 } ) ;
156+
157+ describe ( 'OR operator' , { tags : [ '@ess' , '@serverless' ] } , ( ) => {
158+ let endpointData : ReturnTypeFromChainable < typeof indexEndpointHosts > | undefined ;
159+
160+ const artifactNameActions : FormAction [ ] = [
161+ {
162+ type : 'input' ,
163+ selector : 'endpointExceptions-form-name-input' ,
164+ value : 'Endpoint exception name' ,
165+ } ,
166+ {
167+ type : 'input' ,
168+ selector : 'endpointExceptions-form-description-input' ,
169+ value : 'This is the endpoint exception description' ,
170+ } ,
171+ ] ;
172+
173+ const firstConditionActions : FormAction [ ] = [
174+ {
175+ type : 'input' ,
176+ selector : 'fieldAutocompleteComboBox' ,
177+ value : 'agent.version' ,
178+ } ,
179+ {
180+ type : 'click' ,
181+ selector : 'valuesAutocompleteMatch' ,
182+ } ,
183+ {
184+ type : 'input' ,
185+ selector : 'valuesAutocompleteMatch' ,
186+ value : '1234' ,
187+ } ,
188+ {
189+ type : 'click' ,
190+ selector : 'endpointExceptions-form-description-input' ,
191+ } ,
192+ ] ;
193+
194+ before ( ( ) => {
195+ indexEndpointHosts ( ) . then ( ( indexEndpoints ) => {
196+ endpointData = indexEndpoints ;
197+ } ) ;
198+ } ) ;
199+
200+ beforeEach ( ( ) => {
201+ removeAllArtifacts ( ) ;
202+ } ) ;
203+
204+ after ( ( ) => {
205+ removeAllArtifacts ( ) ;
206+
207+ endpointData ?. cleanup ( ) ;
208+ endpointData = undefined ;
209+ } ) ;
210+
211+ const addConditionWithOR = ( field : string , value : string ) => {
212+ cy . getByTestSubj ( 'exceptionsOrButton' ) . click ( ) ;
213+
214+ cy . getByTestSubj ( 'fieldAutocompleteComboBox' ) . last ( ) . type ( field ) ;
215+ cy . getByTestSubj ( 'valuesAutocompleteMatch' ) . last ( ) . click ( ) ;
216+ cy . getByTestSubj ( 'valuesAutocompleteMatch' ) . last ( ) . type ( value ) ;
217+ } ;
218+
219+ const shouldHaveConditionsOnScreen = ( conditions : string [ ] ) =>
220+ cy
221+ . getByTestSubj ( 'endpointExceptionsListPage-card-criteriaConditions-condition' )
222+ . then ( ( $conditions ) => {
223+ const conditionsText = $conditions . map ( ( _ , element ) => Cypress . $ ( element ) . text ( ) ) . get ( ) ;
224+
225+ expect ( conditionsText ) . to . include . members ( conditions ) ;
226+ } ) ;
227+
228+ describe ( 'on Artifacts page' , ( ) => {
229+ it ( 'should create 2 artifacts when using 1 OR operator during CREATE' , ( ) => {
230+ cy . intercept ( 'POST' , EXCEPTION_LIST_ITEM_URL ) . as ( 'createExceptionItem' ) ;
231+
232+ login ( ) ;
233+ cy . visit ( APP_ENDPOINT_EXCEPTIONS_PATH ) ;
234+
235+ getArtifactListEmptyStateAddButton ( 'endpointExceptions' ) . click ( ) ;
236+
237+ performUserActions ( artifactNameActions ) ;
238+ performUserActions ( firstConditionActions ) ;
239+
240+ addConditionWithOR ( 'agent.type' , 'endpoint' ) ;
241+
242+ cy . getByTestSubj ( 'endpointExceptionsListPage-flyout-submitButton' ) . click ( ) ;
243+
244+ // There should be 2 artifacts created
245+ cy . get ( '@createExceptionItem.all' ) . should ( 'have.length' , 2 ) ;
246+
247+ // All with same name
248+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-title' )
249+ . should ( 'have.length' , 2 )
250+ . each ( ( card ) => expect ( card ) . to . have . text ( 'Endpoint exception name' ) ) ;
251+
252+ // and different conditions
253+ shouldHaveConditionsOnScreen ( [ 'AND agent.versionIS 1234' , 'AND agent.typeIS endpoint' ] ) ;
254+ } ) ;
255+
256+ it ( 'should create 3 artifacts when using 2 OR operators during CREATE' , ( ) => {
257+ cy . intercept ( 'POST' , EXCEPTION_LIST_ITEM_URL ) . as ( 'createExceptionItem' ) ;
258+
259+ login ( ) ;
260+ cy . visit ( APP_ENDPOINT_EXCEPTIONS_PATH ) ;
261+
262+ getArtifactListEmptyStateAddButton ( 'endpointExceptions' ) . click ( ) ;
263+
264+ performUserActions ( artifactNameActions ) ;
265+ performUserActions ( firstConditionActions ) ;
266+
267+ addConditionWithOR ( 'agent.type' , 'endpoint' ) ;
268+ addConditionWithOR ( 'host.user.email' , 'cheese' ) ;
269+
270+ cy . getByTestSubj ( 'endpointExceptionsListPage-flyout-submitButton' ) . click ( ) ;
271+
272+ // There should be 3 artifacts created
273+ cy . get ( '@createExceptionItem.all' ) . should ( 'have.length' , 3 ) ;
274+
275+ // All with same name
276+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-title' )
277+ . should ( 'have.length' , 3 )
278+ . each ( ( card ) => expect ( card ) . to . have . text ( 'Endpoint exception name' ) ) ;
279+
280+ // and different conditions
281+ shouldHaveConditionsOnScreen ( [
282+ 'AND agent.versionIS 1234' ,
283+ 'AND agent.typeIS endpoint' ,
284+ 'AND host.user.emailIS cheese' ,
285+ ] ) ;
286+ } ) ;
287+
288+ it ( 'should create multiple artifacts when using OR operator during EDIT' , ( ) => {
289+ login ( ) ;
290+ cy . visit ( APP_ENDPOINT_EXCEPTIONS_PATH ) ;
291+
292+ // Create one artifact
293+ getArtifactListEmptyStateAddButton ( 'endpointExceptions' ) . click ( ) ;
294+ performUserActions ( artifactNameActions ) ;
295+ performUserActions ( firstConditionActions ) ;
296+ cy . getByTestSubj ( 'endpointExceptionsListPage-flyout-submitButton' ) . click ( ) ;
297+ cy . getByTestSubj ( 'endpointExceptionsListPage-card' ) . should ( 'have.length' , 1 ) ;
298+
299+ // Open artifact to edit
300+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-actions-button' ) . click ( ) ;
301+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-cardEditAction' ) . click ( ) ;
302+
303+ addConditionWithOR ( 'agent.type' , 'endpoint' ) ;
304+ addConditionWithOR ( 'host.user.email' , 'cheese' ) ;
305+
306+ cy . intercept ( 'PUT' , EXCEPTION_LIST_ITEM_URL ) . as ( 'updateExceptionItem' ) ;
307+ cy . intercept ( 'POST' , EXCEPTION_LIST_ITEM_URL ) . as ( 'createExceptionItem' ) ;
308+
309+ cy . getByTestSubj ( 'endpointExceptionsListPage-flyout-submitButton' ) . click ( ) ;
310+
311+ // There should be 1 artifact edited and 2 new created
312+ cy . get ( '@updateExceptionItem.all' ) . should ( 'have.length' , 1 ) ;
313+ cy . get ( '@createExceptionItem.all' ) . should ( 'have.length' , 2 ) ;
314+
315+ // All 3 with the same name
316+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-title' )
317+ . should ( 'have.length' , 3 )
318+ . each ( ( card ) => expect ( card ) . to . have . text ( 'Endpoint exception name' ) ) ;
319+
320+ // and different conditions
321+ shouldHaveConditionsOnScreen ( [
322+ 'AND agent.versionIS 1234' ,
323+ 'AND agent.typeIS endpoint' ,
324+ 'AND host.user.emailIS cheese' ,
325+ ] ) ;
326+ } ) ;
327+ } ) ;
328+
329+ describe ( 'on Alerts page' , ( ) => {
330+ const clearPrefilledConditions = ( ) =>
331+ recurse (
332+ ( ) => {
333+ cy . getByTestSubj ( 'builderItemEntryDeleteButton' ) . first ( ) . click ( ) ;
334+ return cy . getByTestSubj ( 'builderItemEntryDeleteButton' ) . first ( ) ;
335+ } ,
336+
337+ // recurse until first button is disabled
338+ ( firstDeleteButton ) => firstDeleteButton . prop ( 'disabled' ) === true ,
339+
340+ { delay : 100 }
341+ ) ;
342+
343+ it ( 'should create 2 artifacts when using 1 OR operator during CREATE' , ( ) => {
344+ cy . intercept ( 'POST' , EXCEPTION_LIST_ITEM_URL ) . as ( 'createExceptionItem' ) ;
345+
346+ login ( ) ;
347+ cy . visit ( APP_ALERTS_PATH ) ;
348+
349+ cy . getByTestSubj ( 'timeline-context-menu-button' ) . first ( ) . click ( ) ;
350+ cy . getByTestSubj ( 'add-endpoint-exception-menu-item' ) . click ( ) ;
351+
352+ clearPrefilledConditions ( ) ;
353+
354+ performUserActions ( artifactNameActions ) ;
355+ performUserActions ( firstConditionActions ) ;
356+
357+ addConditionWithOR ( 'agent.type' , 'endpoint' ) ;
358+
359+ cy . getByTestSubj ( `add-endpoint-exception-confirm-button` ) . click ( ) ;
360+
361+ // There should be 2 artifacts created
362+ cy . get ( '@createExceptionItem.all' ) . should ( 'have.length' , 2 ) ;
363+
364+ // Navigate to Endpoint Exceptions page to check the artifacts
365+ cy . visit ( APP_ENDPOINT_EXCEPTIONS_PATH ) ;
366+
367+ // All with same name
368+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-title' )
369+ . should ( 'have.length' , 2 )
370+ . each ( ( card ) => expect ( card ) . to . have . text ( 'Endpoint exception name' ) ) ;
371+
372+ // and different conditions
373+ shouldHaveConditionsOnScreen ( [ 'AND agent.versionIS 1234' , 'AND agent.typeIS endpoint' ] ) ;
374+ } ) ;
375+
376+ it ( 'should create 3 artifacts when using 2 OR operators during CREATE' , ( ) => {
377+ cy . intercept ( 'POST' , EXCEPTION_LIST_ITEM_URL ) . as ( 'createExceptionItem' ) ;
378+
379+ login ( ) ;
380+ cy . visit ( APP_ALERTS_PATH ) ;
381+
382+ cy . getByTestSubj ( 'timeline-context-menu-button' ) . first ( ) . click ( ) ;
383+ cy . getByTestSubj ( 'add-endpoint-exception-menu-item' ) . click ( ) ;
384+
385+ clearPrefilledConditions ( ) ;
386+
387+ performUserActions ( artifactNameActions ) ;
388+ performUserActions ( firstConditionActions ) ;
389+
390+ addConditionWithOR ( 'agent.type' , 'endpoint' ) ;
391+ addConditionWithOR ( 'host.user.email' , 'cheese' ) ;
392+
393+ cy . getByTestSubj ( `add-endpoint-exception-confirm-button` ) . click ( ) ;
394+
395+ // There should be 3 artifacts created
396+ cy . get ( '@createExceptionItem.all' ) . should ( 'have.length' , 3 ) ;
397+
398+ // Navigate to Endpoint Exceptions page to check the artifacts
399+ cy . visit ( APP_ENDPOINT_EXCEPTIONS_PATH ) ;
400+
401+ // All with same name
402+ cy . getByTestSubj ( 'endpointExceptionsListPage-card-header-title' )
403+ . should ( 'have.length' , 3 )
404+ . each ( ( card ) => expect ( card ) . to . have . text ( 'Endpoint exception name' ) ) ;
405+
406+ // and different conditions
407+ shouldHaveConditionsOnScreen ( [
408+ 'AND agent.versionIS 1234' ,
409+ 'AND agent.typeIS endpoint' ,
410+ 'AND host.user.emailIS cheese' ,
411+ ] ) ;
412+ } ) ;
413+ } ) ;
414+ } ) ;
146415 }
147416) ;
0 commit comments