From a6961aa4369688bef38a1cca6581c5e915417137 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Sat, 6 Jun 2020 11:25:12 +0200 Subject: [PATCH 1/2] [SIEM] Fix timeline buildGlobalQuery (#68320) # Conflicts: # x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx # x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx --- .../components/timeline/helpers.test.tsx | 78 +++++++++++++++---- .../public/components/timeline/helpers.tsx | 49 ++++++------ 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/siem/public/components/timeline/helpers.test.tsx b/x-pack/plugins/siem/public/components/timeline/helpers.test.tsx index 4d8d4097e92fc..4b9127a503ed1 100644 --- a/x-pack/plugins/siem/public/components/timeline/helpers.test.tsx +++ b/x-pack/plugins/siem/public/components/timeline/helpers.test.tsx @@ -18,7 +18,7 @@ const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf(); describe('Build KQL Query', () => { test('Build KQL query with one data provider', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1"'); }); @@ -56,18 +56,40 @@ describe('Build KQL Query', () => { }); test('Build KQL query with two data provider', () => { - const dataProviders = mockDataProviders.slice(0, 2); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('(name : "Provider 1") or (name : "Provider 2")'); + }); + + test('Build KQL query with two data provider and first is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].enabled = false; + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 2"'); + }); + + test('Build KQL query with two data provider and second is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[1].enabled = false; const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); - expect(cleanUpKqlQuery(kqlQuery)).toEqual('(name : "Provider 1" ) or (name : "Provider 2" )'); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1"'); }); test('Build KQL query with one data provider and one and', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); - dataProviders[0].and = mockDataProviders.slice(1, 2); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 1" and name : "Provider 2"'); }); + test('Build KQL query with one disabled data provider and one and', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); + dataProviders[0].enabled = false; + dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual('name : "Provider 2"'); + }); + test('Build KQL query with one data provider and one and as timestamp (string input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].and = cloneDeep(mockDataProviders.slice(1, 2)); @@ -106,28 +128,50 @@ describe('Build KQL Query', () => { test('Build KQL query with two data provider and multiple and', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' ); }); + test('Build KQL query with two data provider and multiple and and first data provider is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].enabled = false; + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual( + '(name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' + ); + }); + + test('Build KQL query with two data provider and multiple and and first and provider is disabled', () => { + const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[0].and[0].enabled = false; + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); + const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); + expect(cleanUpKqlQuery(kqlQuery)).toEqual( + '(name : "Provider 1" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5")' + ); + }); + test('Build KQL query with all data provider', () => { const kqlQuery = buildGlobalQuery(mockDataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( - '(name : "Provider 1" ) or (name : "Provider 2" ) or (name : "Provider 3" ) or (name : "Provider 4" ) or (name : "Provider 5" ) or (name : "Provider 6" ) or (name : "Provider 7" ) or (name : "Provider 8" ) or (name : "Provider 9" ) or (name : "Provider 10" )' + '(name : "Provider 1") or (name : "Provider 2") or (name : "Provider 3") or (name : "Provider 4") or (name : "Provider 5") or (name : "Provider 6") or (name : "Provider 7") or (name : "Provider 8") or (name : "Provider 9") or (name : "Provider 10")' ); }); test('Build complex KQL query with and and or', () => { const dataProviders = cloneDeep(mockDataProviders); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const kqlQuery = buildGlobalQuery(dataProviders, mockBrowserFields); expect(cleanUpKqlQuery(kqlQuery)).toEqual( - '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5") or (name : "Provider 3" ) or (name : "Provider 4" ) or (name : "Provider 5" ) or (name : "Provider 6" ) or (name : "Provider 7" ) or (name : "Provider 8" ) or (name : "Provider 9" ) or (name : "Provider 10" )' + '(name : "Provider 1" and name : "Provider 3" and name : "Provider 4") or (name : "Provider 2" and name : "Provider 5") or (name : "Provider 3") or (name : "Provider 4") or (name : "Provider 5") or (name : "Provider 6") or (name : "Provider 7") or (name : "Provider 8") or (name : "Provider 9") or (name : "Provider 10")' ); }); }); @@ -223,7 +267,7 @@ describe('Combined Queries', () => { }); test('Only Data Provider', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -338,7 +382,7 @@ describe('Combined Queries', () => { }); test('Data Provider & KQL search query', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -356,7 +400,7 @@ describe('Combined Queries', () => { }); test('Data Provider & KQL filter query', () => { - const dataProviders = mockDataProviders.slice(0, 1); + const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -375,8 +419,8 @@ describe('Combined Queries', () => { test('Data Provider & KQL search query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const { filterQuery } = combineQueries({ config, dataProviders, @@ -395,8 +439,8 @@ describe('Combined Queries', () => { test('Data Provider & KQL filter query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); - dataProviders[0].and = mockDataProviders.slice(2, 4); - dataProviders[1].and = mockDataProviders.slice(4, 5); + dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); + dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); const { filterQuery } = combineQueries({ config, dataProviders, diff --git a/x-pack/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/plugins/siem/public/components/timeline/helpers.tsx index 603f6ae7c3487..da518c0ce8915 100644 --- a/x-pack/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/plugins/siem/public/components/timeline/helpers.tsx @@ -63,35 +63,30 @@ const buildQueryMatch = ( : `${dataProvider.queryMatch.field} ${EXISTS_OPERATOR}` }`.trim(); -const buildQueryForAndProvider = ( - dataAndProviders: DataProvidersAnd[], - browserFields: BrowserFields -) => - dataAndProviders - .reduce((andQuery, andDataProvider) => { - const prepend = (q: string) => `${q !== '' ? `${q} and ` : ''}`; - return andDataProvider.enabled - ? `${prepend(andQuery)} ${buildQueryMatch(andDataProvider, browserFields)}` - : andQuery; - }, '') - .trim(); - export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: BrowserFields) => dataProviders - .reduce((query, dataProvider: DataProvider, i) => { - const prepend = (q: string) => `${q !== '' ? `${q} or ` : ''}`; - const openParen = i >= 0 && dataProviders.length > 1 ? '(' : ''; - const closeParen = i >= 0 && dataProviders.length > 1 ? ')' : ''; - return dataProvider.enabled - ? `${prepend(query)}${openParen}${buildQueryMatch(dataProvider, browserFields)} - ${ - dataProvider.and.length > 0 - ? ` and ${buildQueryForAndProvider(dataProvider.and, browserFields)}` - : '' - }${closeParen}`.trim() - : query; - }, '') - .trim(); + .reduce((queries: string[], dataProvider: DataProvider) => { + const flatDataProviders = [dataProvider, ...dataProvider.and]; + const activeDataProviders = flatDataProviders.filter( + flatDataProvider => flatDataProvider.enabled + ); + + if (!activeDataProviders.length) return queries; + + const activeDataProvidersQueries = activeDataProviders.map(activeDataProvider => + buildQueryMatch(activeDataProvider, browserFields) + ); + + const activeDataProvidersQueryMatch = activeDataProvidersQueries.join(' and '); + + return [...queries, activeDataProvidersQueryMatch]; + }, []) + .filter(queriesItem => !isEmpty(queriesItem)) + .reduce((globalQuery: string, queryMatch: string, index: number, queries: string[]) => { + if (queries.length <= 1) return queryMatch; + + return !index ? `(${queryMatch})` : `${globalQuery} or (${queryMatch})`; + }, ''); export const combineQueries = ({ config, From ae75d7764148240d84833800388e6425284b3460 Mon Sep 17 00:00:00 2001 From: Patryk Kopycinski Date: Sat, 6 Jun 2020 13:39:30 +0200 Subject: [PATCH 2/2] Fix lint --- x-pack/plugins/siem/public/components/timeline/helpers.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/plugins/siem/public/components/timeline/helpers.tsx index da518c0ce8915..b0e8572e08623 100644 --- a/x-pack/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/plugins/siem/public/components/timeline/helpers.tsx @@ -68,12 +68,12 @@ export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: B .reduce((queries: string[], dataProvider: DataProvider) => { const flatDataProviders = [dataProvider, ...dataProvider.and]; const activeDataProviders = flatDataProviders.filter( - flatDataProvider => flatDataProvider.enabled + (flatDataProvider) => flatDataProvider.enabled ); if (!activeDataProviders.length) return queries; - const activeDataProvidersQueries = activeDataProviders.map(activeDataProvider => + const activeDataProvidersQueries = activeDataProviders.map((activeDataProvider) => buildQueryMatch(activeDataProvider, browserFields) ); @@ -81,7 +81,7 @@ export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: B return [...queries, activeDataProvidersQueryMatch]; }, []) - .filter(queriesItem => !isEmpty(queriesItem)) + .filter((queriesItem) => !isEmpty(queriesItem)) .reduce((globalQuery: string, queryMatch: string, index: number, queries: string[]) => { if (queries.length <= 1) return queryMatch;