Skip to content

Commit

Permalink
[SIEM] Fix timeline buildGlobalQuery (elastic#68320)
Browse files Browse the repository at this point in the history
# Conflicts:
#	x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx
#	x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx
  • Loading branch information
patrykkopycinski committed Jun 6, 2020
1 parent f71e531 commit 6b1e915
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"');
});
Expand Down Expand Up @@ -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 1") or (name : "Provider 2" )');
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"');
});

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));
Expand Down Expand Up @@ -106,13 +128,52 @@ 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")'
);
});

test('Build complex KQL query with and and or', () => {
const dataProviders = cloneDeep(mockDataProviders);
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")'
);
});
});

describe('Combined Queries', () => {
Expand Down Expand Up @@ -206,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,
Expand Down Expand Up @@ -321,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,
Expand All @@ -339,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,
Expand All @@ -358,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,
Expand All @@ -378,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,
Expand Down
49 changes: 22 additions & 27 deletions x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? '(' : '';
const closeParen = i > 0 ? ')' : '';
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,
Expand Down

0 comments on commit 6b1e915

Please sign in to comment.