diff --git a/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts b/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts index f38bec6d1f59f6..8b25a59ed857a6 100644 --- a/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts +++ b/x-pack/plugins/data_enhanced/common/search/es_search/es_search_rxjs_utils.ts @@ -5,7 +5,8 @@ */ import { of, merge, timer, throwError } from 'rxjs'; -import { takeWhile, switchMap, expand, mergeMap, tap } from 'rxjs/operators'; +import { map, takeWhile, switchMap, expand, mergeMap, tap } from 'rxjs/operators'; +import { ApiResponse } from '@elastic/elasticsearch'; import { doSearch, @@ -35,6 +36,15 @@ export const doPartialSearch = ( takeWhile((response) => !isCompleteResponse(response), true) ); +export const normalizeEqlResponse = () => + map((eqlResponse) => ({ + ...eqlResponse, + body: { + ...eqlResponse.body, + ...eqlResponse, + }, + })); + export const throwOnEsError = () => mergeMap((r: IKibanaSearchResponse) => isErrorResponse(r) ? merge(of(r), throwError(new AbortError())) : of(r) diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts index 88aaee8eb7da2d..cd94d91db8c5ec 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts @@ -22,6 +22,8 @@ const getMockEqlResponse = () => ({ sequences: [], }, }, + meta: {}, + statusCode: 200, }); describe('EQL search strategy', () => { @@ -193,5 +195,20 @@ describe('EQL search strategy', () => { expect(requestOptions).toEqual(expect.objectContaining({ ignore: [400] })); }); }); + + describe('response', () => { + it('contains a rawResponse field containing the full search response', async () => { + const eqlSearch = await eqlSearchStrategyProvider(mockLogger); + const response = await eqlSearch + .search({ id: 'my-search-id', options: { ignore: [400] } }, {}, mockDeps) + .toPromise(); + + expect(response).toEqual( + expect.objectContaining({ + rawResponse: expect.objectContaining(getMockEqlResponse()), + }) + ); + }); + }); }); }); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index a75f2617a9bf35..7b3d0db450b044 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -8,7 +8,10 @@ import type { Logger } from 'kibana/server'; import type { ApiResponse } from '@elastic/elasticsearch'; import { search } from '../../../../../src/plugins/data/server'; -import { doPartialSearch } from '../../common/search/es_search/es_search_rxjs_utils'; +import { + doPartialSearch, + normalizeEqlResponse, +} from '../../common/search/es_search/es_search_rxjs_utils'; import { getAsyncOptions, getDefaultSearchParams } from './get_default_search_params'; import type { ISearchStrategy, IEsRawSearchResponse } from '../../../../../src/plugins/data/server'; @@ -64,7 +67,7 @@ export const eqlSearchStrategyProvider = ( (response) => response.body.id, request.id, options - ).pipe(utils.toKibanaSearchResponse()); + ).pipe(normalizeEqlResponse(), utils.toKibanaSearchResponse()); }, }; }; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 5e7dce6966195d..618ddbad9f44a4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -50,6 +50,10 @@ export const EQL_TYPE = '[data-test-subj="eqlRuleType"]'; export const EQL_QUERY_INPUT = '[data-test-subj="eqlQueryBarTextInput"]'; +export const EQL_QUERY_PREVIEW_HISTOGRAM = '[data-test-subj="queryPreviewEqlHistogram"]'; + +export const EQL_QUERY_VALIDATION_SPINNER = '[data-test-subj="eql-validation-loading"]'; + export const IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK = '[data-test-subj="importQueryFromSavedTimeline"]'; @@ -80,6 +84,8 @@ export const MITRE_TACTIC_DROPDOWN = '[data-test-subj="mitreTactic"]'; export const MITRE_TECHNIQUES_INPUT = '[data-test-subj="mitreTechniques"] [data-test-subj="comboBoxSearchInput"]'; +export const QUERY_PREVIEW_BUTTON = '[data-test-subj="queryPreviewButton"]'; + export const REFERENCE_URLS_INPUT = '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input'; diff --git a/x-pack/plugins/security_solution/cypress/screens/shared.ts b/x-pack/plugins/security_solution/cypress/screens/shared.ts new file mode 100644 index 00000000000000..ccfe0f97c732c3 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/screens/shared.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const NOTIFICATION_TOASTS = '[data-test-subj="globalToastList"]'; + +export const TOAST_ERROR_CLASS = 'euiToast--danger'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index 7327d5399065a2..251a7ccc4b9c9e 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -61,7 +61,11 @@ import { THRESHOLD_TYPE, EQL_TYPE, EQL_QUERY_INPUT, + QUERY_PREVIEW_BUTTON, + EQL_QUERY_PREVIEW_HISTOGRAM, + EQL_QUERY_VALIDATION_SPINNER, } from '../screens/create_new_rule'; +import { NOTIFICATION_TOASTS, TOAST_ERROR_CLASS } from '../screens/shared'; import { TIMELINE } from '../screens/timelines'; import { refreshPage } from './security_header'; @@ -225,9 +229,12 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { cy.get(EQL_QUERY_INPUT).type(rule.customQuery); - cy.get(EQL_QUERY_INPUT).invoke('text').should('eq', rule.customQuery); - cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); + cy.get(EQL_QUERY_VALIDATION_SPINNER).should('not.exist'); + cy.get(QUERY_PREVIEW_BUTTON).should('not.be.disabled').click({ force: true }); + cy.get(EQL_QUERY_PREVIEW_HISTOGRAM).should('contain.text', 'Hits'); + cy.get(NOTIFICATION_TOASTS).children().should('not.have.class', TOAST_ERROR_CLASS); // asserts no error toast on page + cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(EQL_QUERY_INPUT).should('not.exist'); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts index fb2e484c0e3f17..5b78a0e6ae5827 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts @@ -32,8 +32,7 @@ export const validateEql = async ({ const { rawResponse: response } = await data.search .search( { - // @ts-expect-error allow_no_indices is missing on EqlSearch - params: { allow_no_indices: true, index: index.join(), body: { query, size: 0 } }, + params: { index: index.join(), body: { query, size: 0 } }, options: { ignore: [400] }, }, { diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts index 1f4424a4f28b80..f259db6e932bf3 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/use_eql_preview.ts @@ -74,8 +74,6 @@ export const useEqlPreview = (): [ .search>>( { params: { - // @ts-expect-error allow_no_indices is missing on EqlSearch - allow_no_indices: true, index: index.join(), body: { filter: { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.test.tsx index 3dc3213d653146..8c1a59f912f946 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.test.tsx @@ -51,7 +51,7 @@ describe('PreviewCustomQueryHistogram', () => { expect(wrapper.find('[data-test-subj="queryPreviewLoading"]').exists()).toBeTruthy(); expect( - wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').at(0).prop('subtitle') + wrapper.find('[dataTestSubj="queryPreviewCustomHistogram"]').at(0).prop('subtitle') ).toEqual(i18n.QUERY_PREVIEW_SUBTITLE_LOADING); }); @@ -78,32 +78,32 @@ describe('PreviewCustomQueryHistogram', () => { expect(wrapper.find('[data-test-subj="queryPreviewLoading"]').exists()).toBeFalsy(); expect( - wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').at(0).prop('subtitle') + wrapper.find('[dataTestSubj="queryPreviewCustomHistogram"]').at(0).prop('subtitle') ).toEqual(i18n.QUERY_PREVIEW_TITLE(9154)); - expect( - wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').at(0).props().data - ).toEqual([ - { - key: 'hits', - value: [ - { - g: 'All others', - x: 1602247050000, - y: 2314, - }, - { - g: 'All others', - x: 1602247162500, - y: 3471, - }, - { - g: 'All others', - x: 1602247275000, - y: 3369, - }, - ], - }, - ]); + expect(wrapper.find('[dataTestSubj="queryPreviewCustomHistogram"]').at(0).props().data).toEqual( + [ + { + key: 'hits', + value: [ + { + g: 'All others', + x: 1602247050000, + y: 2314, + }, + { + g: 'All others', + x: 1602247162500, + y: 3471, + }, + { + g: 'All others', + x: 1602247275000, + y: 3369, + }, + ], + }, + ] + ); }); test('it invokes setQuery with id, inspect, isLoading and refetch', async () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.tsx index 77b6fbb938e208..42243bca0fca55 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/custom_histogram.tsx @@ -69,7 +69,7 @@ export const PreviewCustomQueryHistogram = ({ subtitle={subtitle} disclaimer={i18n.QUERY_PREVIEW_DISCLAIMER} isLoading={isLoading} - data-test-subj="queryPreviewCustomHistogram" + dataTestSubj="queryPreviewCustomHistogram" /> ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.test.tsx index 3e7807f423be92..c39d14f6257b7e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.test.tsx @@ -51,7 +51,7 @@ describe('PreviewEqlQueryHistogram', () => { expect(wrapper.find('[data-test-subj="queryPreviewLoading"]').exists()).toBeTruthy(); expect( - wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').at(0).prop('subtitle') + wrapper.find('[dataTestSubj="queryPreviewEqlHistogram"]').at(0).prop('subtitle') ).toEqual(i18n.QUERY_PREVIEW_SUBTITLE_LOADING); }); @@ -78,9 +78,9 @@ describe('PreviewEqlQueryHistogram', () => { expect(wrapper.find('[data-test-subj="queryPreviewLoading"]').exists()).toBeFalsy(); expect( - wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').at(0).prop('subtitle') + wrapper.find('[dataTestSubj="queryPreviewEqlHistogram"]').at(0).prop('subtitle') ).toEqual(i18n.QUERY_PREVIEW_TITLE(9154)); - expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').at(0).props().data).toEqual([ + expect(wrapper.find('[dataTestSubj="queryPreviewEqlHistogram"]').at(0).props().data).toEqual([ { key: 'hits', value: [ diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.tsx index ed1fd5b7367d4f..88fe228a364aba 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/eql_histogram.tsx @@ -66,7 +66,7 @@ export const PreviewEqlQueryHistogram = ({ subtitle={subtitle} disclaimer={i18n.QUERY_PREVIEW_DISCLAIMER_EQL} isLoading={isLoading} - data-test-subj="queryPreviewEqlHistogram" + dataTestSubj="queryPreviewEqlHistogram" /> ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/histogram.tsx index 2c43dac7b6bce9..a7357672b486f9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/histogram.tsx @@ -21,6 +21,7 @@ const LoadingChart = styled(EuiLoadingChart)` interface PreviewHistogramProps { id: string; data: ChartSeriesData[]; + dataTestSubj?: string; barConfig: ChartSeriesConfigs; title: string; subtitle: string; @@ -31,6 +32,7 @@ interface PreviewHistogramProps { export const PreviewHistogram = ({ id, data, + dataTestSubj, barConfig, title, subtitle, @@ -39,7 +41,7 @@ export const PreviewHistogram = ({ }: PreviewHistogramProps) => { return ( <> - + diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx index 26891dae1752a6..3b6d3bca675748 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.test.tsx @@ -79,9 +79,9 @@ describe('PreviewQuery', () => { expect( wrapper.find('[data-test-subj="queryPreviewButton"] button').props().disabled ).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it renders preview button disabled if "isDisabled" is true', () => { @@ -146,9 +146,9 @@ describe('PreviewQuery', () => { const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; expect(mockCalls.length).toEqual(1); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it renders noise warning when rule type is query, timeframe is last hour and hit average is greater than 1/hour', async () => { @@ -209,9 +209,9 @@ describe('PreviewQuery', () => { const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; expect(mockCalls.length).toEqual(1); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it renders eql histogram when preview button clicked and rule type is eql', () => { @@ -236,9 +236,9 @@ describe('PreviewQuery', () => { const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; expect(mockCalls.length).toEqual(1); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeTruthy(); }); test('it renders noise warning when rule type is eql, timeframe is last hour and hit average is greater than 1/hour', async () => { @@ -314,9 +314,9 @@ describe('PreviewQuery', () => { expect(mockCalls.length).toEqual(1); expect(wrapper.find('[data-test-subj="previewQueryWarning"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it renders noise warning when rule type is threshold, and threshold field is defined, timeframe is last hour and hit average is greater than 1/hour', async () => { @@ -380,9 +380,9 @@ describe('PreviewQuery', () => { const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; expect(mockCalls.length).toEqual(1); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it renders query histogram when preview button clicked, rule type is threshold, and threshold field is empty string', () => { @@ -407,9 +407,9 @@ describe('PreviewQuery', () => { const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; expect(mockCalls.length).toEqual(1); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeTruthy(); - expect(wrapper.find('[data-test-subj="previewThresholdQueryHistogram"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="previewEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewEqlHistogram"]').exists()).toBeFalsy(); }); test('it hides histogram when timeframe changes', () => { @@ -431,13 +431,13 @@ describe('PreviewQuery', () => { wrapper.find('[data-test-subj="queryPreviewButton"] button').at(0).simulate('click'); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeTruthy(); wrapper .find('[data-test-subj="queryPreviewTimeframeSelect"] select') .at(0) .simulate('change', { target: { value: 'd' } }); - expect(wrapper.find('[data-test-subj="previewNonEqlQueryHistogram"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="queryPreviewCustomHistogram"]').exists()).toBeFalsy(); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx index 6669ea6d979692..52f19704ed917d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/index.tsx @@ -312,7 +312,6 @@ export const PreviewQuery = ({ inspect={inspect} refetch={refetch} isLoading={isMatrixHistogramLoading} - data-test-subj="previewNonEqlQueryHistogram" /> )} {ruleType === 'threshold' && thresholdFieldExists && showHistogram && ( @@ -321,7 +320,6 @@ export const PreviewQuery = ({ buckets={buckets} inspect={inspect} refetch={refetch} - data-test-subj="previewThresholdQueryHistogram" /> )} {ruleType === 'eql' && showHistogram && ( @@ -333,7 +331,6 @@ export const PreviewQuery = ({ inspect={eqlQueryInspect} refetch={eqlQueryRefetch} isLoading={eqlQueryLoading} - data-test-subj="previewEqlQueryHistogram" /> )} {showHistogram && diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.test.tsx index 8a0cfef1b62564..44655a91075a59 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.test.tsx @@ -68,7 +68,7 @@ describe('PreviewThresholdQueryHistogram', () => { expect(wrapper.find('[data-test-subj="queryPreviewLoading"]').exists()).toBeFalsy(); expect( - wrapper.find('[data-test-subj="thresholdQueryPreviewHistogram"]').at(0).props().data + wrapper.find('[dataTestSubj="thresholdQueryPreviewHistogram"]').at(0).props().data ).toEqual([ { key: 'hits', diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.tsx index a102c567a98e88..360b834bb77228 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/threshold_histogram.tsx @@ -75,7 +75,7 @@ export const PreviewThresholdQueryHistogram = ({ subtitle={subtitle} disclaimer={i18n.QUERY_PREVIEW_DISCLAIMER} isLoading={isLoading} - data-test-subj="thresholdQueryPreviewHistogram" + dataTestSubj="thresholdQueryPreviewHistogram" /> ); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 0ba2abb466f7bb..f76bdb4ebc718d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -6,7 +6,10 @@ import expect from '@kbn/expect'; -import { QueryCreateSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; +import { + EqlCreateSchema, + QueryCreateSchema, +} from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; import { DEFAULT_SIGNALS_INDEX } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -191,6 +194,130 @@ export default ({ getService }: FtrProviderContext) => { }, }); }); + + describe('EQL Rules', () => { + it('generates signals from EQL sequences in the expected form', async () => { + const rule: EqlCreateSchema = { + ...getSimpleRule(), + from: '1900-01-01T00:00:00.000Z', + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'sequence by host.name [any where true] [any where true]', + }; + await createRule(supertest, rule); + await waitForSignalsToBePresent(supertest, 1); + const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); + const signal = signals.hits.hits[0]._source.signal; + + expect(signal).eql({ + rule: signal.rule, + group: signal.group, + original_time: signal.original_time, + status: 'open', + depth: 1, + ancestors: [ + { + depth: 0, + id: 'UBXOBmkBR346wHgnLP8T', + index: 'auditbeat-8.0.0-2019.02.19-000001', + type: 'event', + }, + ], + original_event: { + action: 'boot', + dataset: 'login', + kind: 'event', + module: 'system', + origin: '/var/log/wtmp', + }, + parent: { + depth: 0, + id: 'UBXOBmkBR346wHgnLP8T', + index: 'auditbeat-8.0.0-2019.02.19-000001', + type: 'event', + }, + parents: [ + { + depth: 0, + id: 'UBXOBmkBR346wHgnLP8T', + index: 'auditbeat-8.0.0-2019.02.19-000001', + type: 'event', + }, + ], + }); + }); + + it('generates building block signals from EQL sequences in the expected form', async () => { + const rule: EqlCreateSchema = { + ...getSimpleRule(), + from: '1900-01-01T00:00:00.000Z', + rule_id: 'eql-rule', + type: 'eql', + language: 'eql', + query: 'sequence by host.name [any where true] [any where true]', + }; + await createRule(supertest, rule); + await waitForSignalsToBePresent(supertest, 1); + const signalsOpen = await getSignalsByRuleIds(supertest, ['eql-rule']); + const sequenceSignal = signalsOpen.hits.hits.find( + (signal) => signal._source.signal.depth === 2 + ); + const signal = sequenceSignal!._source.signal; + const eventIds = signal.parents.map((event) => event.id); + + expect(signal).eql({ + status: 'open', + depth: 2, + group: signal.group, + rule: signal.rule, + ancestors: [ + { + depth: 0, + id: 'UBXOBmkBR346wHgnLP8T', + index: 'auditbeat-8.0.0-2019.02.19-000001', + type: 'event', + }, + { + depth: 1, + id: eventIds[0], + index: '.siem-signals-default', + rule: signal.rule.id, + type: 'signal', + }, + { + depth: 0, + id: 'URXOBmkBR346wHgnLP8T', + index: 'auditbeat-8.0.0-2019.02.19-000001', + type: 'event', + }, + { + depth: 1, + id: eventIds[1], + index: '.siem-signals-default', + rule: signal.rule.id, + type: 'signal', + }, + ], + parents: [ + { + depth: 1, + id: eventIds[0], + index: '.siem-signals-default', + rule: signal.rule.id, + type: 'signal', + }, + { + depth: 1, + id: eventIds[1], + index: '.siem-signals-default', + rule: signal.rule.id, + type: 'signal', + }, + ], + }); + }); + }); }); /**