From ca674afbc206485b2109a1cf9f936ef3a41bb67d Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Wed, 15 Jul 2020 00:21:47 -0400 Subject: [PATCH] updates tests --- .../scripts/lists/new/items/keyword_item.json | 2 +- .../build_exceptions_query.test.ts | 847 +++++------------- .../build_exceptions_query.ts | 4 + .../detection_engine/get_query_filter.test.ts | 42 +- .../detection_engine/signals/utils.test.ts | 1 - 5 files changed, 254 insertions(+), 642 deletions(-) diff --git a/x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json b/x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json index 96d925c157490d..0848dc4c1bd94f 100644 --- a/x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json +++ b/x-pack/plugins/lists/server/scripts/lists/new/items/keyword_item.json @@ -1,4 +1,4 @@ { "list_id": "keyword_list", - "value": "kibana" + "value": "zeek" } diff --git a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts b/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts index e5388fcb44234b..caf2dfb761ed0d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.test.ts @@ -113,226 +113,97 @@ describe('build_exceptions_query', () => { }); describe('operatorBuilder', () => { - describe("when 'exclude' is true", () => { - describe('and langauge is kuery', () => { - test('it returns "not " when operator is "included"', () => { - const operator = operatorBuilder({ operator: 'included', language: 'kuery', exclude }); - expect(operator).toEqual('not '); - }); - test('it returns empty string when operator is "excluded"', () => { - const operator = operatorBuilder({ operator: 'excluded', language: 'kuery', exclude }); - expect(operator).toEqual(''); - }); + describe('and language is kuery', () => { + test('it returns empty string when operator is "included"', () => { + const operator = operatorBuilder({ operator: 'included', language: 'kuery' }); + expect(operator).toEqual(''); }); - - describe('and language is lucene', () => { - test('it returns "NOT " when operator is "included"', () => { - const operator = operatorBuilder({ operator: 'included', language: 'lucene', exclude }); - expect(operator).toEqual('NOT '); - }); - test('it returns empty string when operator is "excluded"', () => { - const operator = operatorBuilder({ operator: 'excluded', language: 'lucene', exclude }); - expect(operator).toEqual(''); - }); + test('it returns "not " when operator is "excluded"', () => { + const operator = operatorBuilder({ operator: 'excluded', language: 'kuery' }); + expect(operator).toEqual('not '); }); }); - describe("when 'exclude' is false", () => { - beforeEach(() => { - exclude = false; - }); - describe('and language is kuery', () => { - test('it returns empty string when operator is "included"', () => { - const operator = operatorBuilder({ operator: 'included', language: 'kuery', exclude }); - expect(operator).toEqual(''); - }); - test('it returns "not " when operator is "excluded"', () => { - const operator = operatorBuilder({ operator: 'excluded', language: 'kuery', exclude }); - expect(operator).toEqual('not '); - }); + describe('and language is lucene', () => { + test('it returns empty string when operator is "included"', () => { + const operator = operatorBuilder({ operator: 'included', language: 'lucene' }); + expect(operator).toEqual(''); }); - - describe('and language is lucene', () => { - test('it returns empty string when operator is "included"', () => { - const operator = operatorBuilder({ operator: 'included', language: 'lucene', exclude }); - expect(operator).toEqual(''); - }); - test('it returns "NOT " when operator is "excluded"', () => { - const operator = operatorBuilder({ operator: 'excluded', language: 'lucene', exclude }); - expect(operator).toEqual('NOT '); - }); + test('it returns "NOT " when operator is "excluded"', () => { + const operator = operatorBuilder({ operator: 'excluded', language: 'lucene' }); + expect(operator).toEqual('NOT '); }); }); }); describe('buildExists', () => { - describe("when 'exclude' is true", () => { - describe('kuery', () => { - test('it returns formatted wildcard string when operator is "excluded"', () => { - const query = buildExists({ - item: existsEntryWithExcluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('host.name:*'); - }); - test('it returns formatted wildcard string when operator is "included"', () => { - const query = buildExists({ - item: existsEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('not host.name:*'); + describe('kuery', () => { + test('it returns formatted wildcard string when operator is "excluded"', () => { + const query = buildExists({ + item: existsEntryWithExcluded, + language: 'kuery', }); + expect(query).toEqual('not host.name:*'); }); - - describe('lucene', () => { - test('it returns formatted wildcard string when operator is "excluded"', () => { - const query = buildExists({ - item: existsEntryWithExcluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('_exists_host.name'); - }); - test('it returns formatted wildcard string when operator is "included"', () => { - const query = buildExists({ - item: existsEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('NOT _exists_host.name'); + test('it returns formatted wildcard string when operator is "included"', () => { + const query = buildExists({ + item: existsEntryWithIncluded, + language: 'kuery', }); + expect(query).toEqual('host.name:*'); }); }); - describe("when 'exclude' is false", () => { - beforeEach(() => { - exclude = false; - }); - - describe('kuery', () => { - test('it returns formatted wildcard string when operator is "excluded"', () => { - const query = buildExists({ - item: existsEntryWithExcluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('not host.name:*'); - }); - test('it returns formatted wildcard string when operator is "included"', () => { - const query = buildExists({ - item: existsEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('host.name:*'); + describe('lucene', () => { + test('it returns formatted wildcard string when operator is "excluded"', () => { + const query = buildExists({ + item: existsEntryWithExcluded, + language: 'lucene', }); + expect(query).toEqual('NOT _exists_host.name'); }); - - describe('lucene', () => { - test('it returns formatted wildcard string when operator is "excluded"', () => { - const query = buildExists({ - item: existsEntryWithExcluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('NOT _exists_host.name'); - }); - test('it returns formatted wildcard string when operator is "included"', () => { - const query = buildExists({ - item: existsEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('_exists_host.name'); + test('it returns formatted wildcard string when operator is "included"', () => { + const query = buildExists({ + item: existsEntryWithIncluded, + language: 'lucene', }); + expect(query).toEqual('_exists_host.name'); }); }); }); describe('buildMatch', () => { - describe("when 'exclude' is true", () => { - describe('kuery', () => { - test('it returns formatted string when operator is "included"', () => { - const query = buildMatch({ - item: matchEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('not host.name:"suricata"'); - }); - test('it returns formatted string when operator is "excluded"', () => { - const query = buildMatch({ - item: matchEntryWithExcluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('host.name:"suricata"'); + describe('kuery', () => { + test('it returns formatted string when operator is "included"', () => { + const query = buildMatch({ + item: matchEntryWithIncluded, + language: 'kuery', }); + expect(query).toEqual('host.name:"suricata"'); }); - - describe('lucene', () => { - test('it returns formatted string when operator is "included"', () => { - const query = buildMatch({ - item: matchEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('NOT host.name:"suricata"'); - }); - test('it returns formatted string when operator is "excluded"', () => { - const query = buildMatch({ - item: matchEntryWithExcluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('host.name:"suricata"'); + test('it returns formatted string when operator is "excluded"', () => { + const query = buildMatch({ + item: matchEntryWithExcluded, + language: 'kuery', }); + expect(query).toEqual('not host.name:"suricata"'); }); }); - describe("when 'exclude' is false", () => { - beforeEach(() => { - exclude = false; - }); - - describe('kuery', () => { - test('it returns formatted string when operator is "included"', () => { - const query = buildMatch({ - item: matchEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('host.name:"suricata"'); - }); - test('it returns formatted string when operator is "excluded"', () => { - const query = buildMatch({ - item: matchEntryWithExcluded, - language: 'kuery', - exclude, - }); - expect(query).toEqual('not host.name:"suricata"'); + describe('lucene', () => { + test('it returns formatted string when operator is "included"', () => { + const query = buildMatch({ + item: matchEntryWithIncluded, + language: 'lucene', }); + expect(query).toEqual('host.name:"suricata"'); }); - - describe('lucene', () => { - test('it returns formatted string when operator is "included"', () => { - const query = buildMatch({ - item: matchEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('host.name:"suricata"'); - }); - test('it returns formatted string when operator is "excluded"', () => { - const query = buildMatch({ - item: matchEntryWithExcluded, - language: 'lucene', - exclude, - }); - expect(query).toEqual('NOT host.name:"suricata"'); + test('it returns formatted string when operator is "excluded"', () => { + const query = buildMatch({ + item: matchEntryWithExcluded, + language: 'lucene', }); + expect(query).toEqual('NOT host.name:"suricata"'); }); }); }); @@ -352,150 +223,79 @@ describe('build_exceptions_query', () => { operator: 'excluded', }); - describe("when 'exclude' is true", () => { - describe('kuery', () => { - test('it returns empty string if given an empty array for "values"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndNoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual(''); - }); - - test('it returns formatted string when "values" includes only one item', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndOneValue, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('not host.name:("suricata")'); + describe('kuery', () => { + test('it returns empty string if given an empty array for "values"', () => { + const exceptionSegment = buildMatchAny({ + item: entryWithIncludedAndNoValues, + language: 'kuery', }); + expect(exceptionSegment).toEqual(''); + }); - test('it returns formatted string when operator is "included"', () => { - const exceptionSegment = buildMatchAny({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('not host.name:("suricata" or "auditd")'); + test('it returns formatted string when "values" includes only one item', () => { + const exceptionSegment = buildMatchAny({ + item: entryWithIncludedAndOneValue, + language: 'kuery', }); - test('it returns formatted string when operator is "excluded"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithExcludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata" or "auditd")'); - }); + expect(exceptionSegment).toEqual('host.name:("suricata")'); }); - describe('lucene', () => { - test('it returns formatted string when operator is "included"', () => { - const exceptionSegment = buildMatchAny({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('NOT host.name:("suricata" OR "auditd")'); - }); - test('it returns formatted string when operator is "excluded"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithExcludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata" OR "auditd")'); - }); - test('it returns formatted string when "values" includes only one item', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndOneValue, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('NOT host.name:("suricata")'); + test('it returns formatted string when operator is "included"', () => { + const exceptionSegment = buildMatchAny({ + item: matchAnyEntryWithIncludedAndTwoValues, + language: 'kuery', }); - }); - }); - describe("when 'exclude' is false", () => { - beforeEach(() => { - exclude = false; + expect(exceptionSegment).toEqual('host.name:("suricata" or "auditd")'); }); - describe('kuery', () => { - test('it returns empty string if given an empty array for "values"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndNoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual(''); - }); - test('it returns formatted string when "values" includes only one item', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndOneValue, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata")'); - }); - test('it returns formatted string when operator is "included"', () => { - const exceptionSegment = buildMatchAny({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata" or "auditd")'); + test('it returns formatted string when operator is "excluded"', () => { + const exceptionSegment = buildMatchAny({ + item: entryWithExcludedAndTwoValues, + language: 'kuery', }); - test('it returns formatted string when operator is "excluded"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithExcludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(exceptionSegment).toEqual('not host.name:("suricata" or "auditd")'); - }); + expect(exceptionSegment).toEqual('not host.name:("suricata" or "auditd")'); }); + }); - describe('lucene', () => { - test('it returns formatted string when operator is "included"', () => { - const exceptionSegment = buildMatchAny({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata" OR "auditd")'); + describe('lucene', () => { + test('it returns formatted string when operator is "included"', () => { + const exceptionSegment = buildMatchAny({ + item: matchAnyEntryWithIncludedAndTwoValues, + language: 'lucene', }); - test('it returns formatted string when operator is "excluded"', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithExcludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('NOT host.name:("suricata" OR "auditd")'); + + expect(exceptionSegment).toEqual('host.name:("suricata" OR "auditd")'); + }); + test('it returns formatted string when operator is "excluded"', () => { + const exceptionSegment = buildMatchAny({ + item: entryWithExcludedAndTwoValues, + language: 'lucene', }); - test('it returns formatted string when "values" includes only one item', () => { - const exceptionSegment = buildMatchAny({ - item: entryWithIncludedAndOneValue, - language: 'lucene', - exclude, - }); - expect(exceptionSegment).toEqual('host.name:("suricata")'); + + expect(exceptionSegment).toEqual('NOT host.name:("suricata" OR "auditd")'); + }); + test('it returns formatted string when "values" includes only one item', () => { + const exceptionSegment = buildMatchAny({ + item: entryWithIncludedAndOneValue, + language: 'lucene', }); + + expect(exceptionSegment).toEqual('host.name:("suricata")'); }); }); }); describe('buildNested', () => { + // NOTE: Only KQL supports nested describe('kuery', () => { test('it returns formatted query when one item in nested entry', () => { const item: EntryNested = { field: 'parent', type: 'nested', - entries: [makeMatchEntry({ field: 'nestedField', operator: 'excluded' })], + entries: [makeMatchEntry({ field: 'nestedField', operator: 'included' })], }; const result = buildNested({ item, language: 'kuery' }); @@ -507,8 +307,8 @@ describe('build_exceptions_query', () => { field: 'parent', type: 'nested', entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded' }), - makeMatchEntry({ field: 'nestedFieldB', operator: 'excluded', value: 'value-2' }), + makeMatchEntry({ field: 'nestedField', operator: 'included' }), + makeMatchEntry({ field: 'nestedFieldB', operator: 'included', value: 'value-2' }), ], }; const result = buildNested({ item, language: 'kuery' }); @@ -516,119 +316,49 @@ describe('build_exceptions_query', () => { expect(result).toEqual('parent:{ nestedField:"value-1" and nestedFieldB:"value-2" }'); }); }); - - // TODO: Does lucene support nested query syntax? - describe.skip('lucene', () => { - test('it returns formatted query when one item in nested entry', () => { - const item: EntryNested = { - field: 'parent', - type: 'nested', - entries: [makeMatchEntry({ field: 'nestedField', operator: 'excluded' })], - }; - const result = buildNested({ item, language: 'lucene' }); - - expect(result).toEqual('parent:{ nestedField:"value-1" }'); - }); - - test('it returns formatted query when multiple items in nested entry', () => { - const item: EntryNested = { - field: 'parent', - type: 'nested', - entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded' }), - makeMatchEntry({ field: 'nestedFieldB', operator: 'excluded', value: 'value-2' }), - ], - }; - const result = buildNested({ item, language: 'lucene' }); - - expect(result).toEqual('parent:{ nestedField:"value-1" AND nestedFieldB:"value-2" }'); - }); - }); }); describe('evaluateValues', () => { - describe("when 'exclude' is true", () => { - describe('kuery', () => { - test('it returns formatted wildcard string when "type" is "exists"', () => { - const result = evaluateValues({ - item: existsEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(result).toEqual('not host.name:*'); - }); - - test('it returns formatted string when "type" is "match"', () => { - const result = evaluateValues({ - item: matchEntryWithIncluded, - language: 'kuery', - exclude, - }); - expect(result).toEqual('not host.name:"suricata"'); + describe('kuery', () => { + test('it returns formatted wildcard string when "type" is "exists"', () => { + const result = evaluateValues({ + item: existsEntryWithIncluded, + language: 'kuery', }); + expect(result).toEqual('host.name:*'); + }); - test('it returns formatted string when "type" is "match_any"', () => { - const result = evaluateValues({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(result).toEqual('not host.name:("suricata" or "auditd")'); + test('it returns formatted string when "type" is "match"', () => { + const result = evaluateValues({ + item: matchEntryWithIncluded, + language: 'kuery', }); + expect(result).toEqual('host.name:"suricata"'); }); - describe('lucene', () => { - describe('kuery', () => { - test('it returns formatted wildcard string when "type" is "exists"', () => { - const result = evaluateValues({ - item: existsEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(result).toEqual('NOT _exists_host.name'); - }); - - test('it returns formatted string when "type" is "match"', () => { - const result = evaluateValues({ - item: matchEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(result).toEqual('NOT host.name:"suricata"'); - }); - - test('it returns formatted string when "type" is "match_any"', () => { - const result = evaluateValues({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(result).toEqual('NOT host.name:("suricata" OR "auditd")'); - }); + test('it returns formatted string when "type" is "match_any"', () => { + const result = evaluateValues({ + item: matchAnyEntryWithIncludedAndTwoValues, + language: 'kuery', }); + expect(result).toEqual('host.name:("suricata" or "auditd")'); }); }); - describe("when 'exclude' is false", () => { - beforeEach(() => { - exclude = false; - }); - + describe('lucene', () => { describe('kuery', () => { test('it returns formatted wildcard string when "type" is "exists"', () => { const result = evaluateValues({ item: existsEntryWithIncluded, - language: 'kuery', - exclude, + language: 'lucene', }); - expect(result).toEqual('host.name:*'); + expect(result).toEqual('_exists_host.name'); }); test('it returns formatted string when "type" is "match"', () => { const result = evaluateValues({ item: matchEntryWithIncluded, - language: 'kuery', - exclude, + language: 'lucene', }); expect(result).toEqual('host.name:"suricata"'); }); @@ -636,76 +366,69 @@ describe('build_exceptions_query', () => { test('it returns formatted string when "type" is "match_any"', () => { const result = evaluateValues({ item: matchAnyEntryWithIncludedAndTwoValues, - language: 'kuery', - exclude, - }); - expect(result).toEqual('host.name:("suricata" or "auditd")'); - }); - }); - - describe('lucene', () => { - describe('kuery', () => { - test('it returns formatted wildcard string when "type" is "exists"', () => { - const result = evaluateValues({ - item: existsEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(result).toEqual('_exists_host.name'); - }); - - test('it returns formatted string when "type" is "match"', () => { - const result = evaluateValues({ - item: matchEntryWithIncluded, - language: 'lucene', - exclude, - }); - expect(result).toEqual('host.name:"suricata"'); - }); - - test('it returns formatted string when "type" is "match_any"', () => { - const result = evaluateValues({ - item: matchAnyEntryWithIncludedAndTwoValues, - language: 'lucene', - exclude, - }); - expect(result).toEqual('host.name:("suricata" OR "auditd")'); + language: 'lucene', }); + expect(result).toEqual('host.name:("suricata" OR "auditd")'); }); }); }); }); describe('formatQuery', () => { - describe('when query is empty string', () => { - test('it returns empty string if "exceptions" is empty array', () => { - const formattedQuery = formatQuery({ exceptions: [], language: 'kuery' }); - expect(formattedQuery).toEqual(''); + describe('exclude is true', () => { + describe('when query is empty string', () => { + test('it returns empty string if "exceptions" is empty array', () => { + const formattedQuery = formatQuery({ exceptions: [], language: 'kuery', exclude: true }); + expect(formattedQuery).toEqual(''); + }); + + test('it returns expected query string when single exception in array', () => { + const formattedQuery = formatQuery({ + exceptions: ['b:("value-1" or "value-2") and not c:*'], + language: 'kuery', + exclude: true, + }); + expect(formattedQuery).toEqual('not ((b:("value-1" or "value-2") and not c:*))'); + }); }); - test('it returns expected query string when single exception in array', () => { + test('it returns expected query string when multiple exceptions in array', () => { const formattedQuery = formatQuery({ - exceptions: ['b:("value-1" or "value-2") and not c:*'], + exceptions: ['b:("value-1" or "value-2") and not c:*', 'not d:*'], language: 'kuery', + exclude: true, }); - expect(formattedQuery).toEqual('b:("value-1" or "value-2") and not c:*'); + expect(formattedQuery).toEqual( + 'not ((b:("value-1" or "value-2") and not c:*) or (not d:*))' + ); }); }); - test('it returns expected query string when single exception in array', () => { - const formattedQuery = formatQuery({ - exceptions: ['b:("value-1" or "value-2") and not c:*'], - language: 'kuery', + describe('exclude is false', () => { + describe('when query is empty string', () => { + test('it returns empty string if "exceptions" is empty array', () => { + const formattedQuery = formatQuery({ exceptions: [], language: 'kuery', exclude: false }); + expect(formattedQuery).toEqual(''); + }); + + test('it returns expected query string when single exception in array', () => { + const formattedQuery = formatQuery({ + exceptions: ['b:("value-1" or "value-2") and not c:*'], + language: 'kuery', + exclude: false, + }); + expect(formattedQuery).toEqual('(b:("value-1" or "value-2") and not c:*)'); + }); }); - expect(formattedQuery).toEqual('b:("value-1" or "value-2") and not c:*'); - }); - test('it returns expected query string when multiple exceptions in array', () => { - const formattedQuery = formatQuery({ - exceptions: ['b:("value-1" or "value-2") and not c:*', 'not d:*'], - language: 'kuery', + test('it returns expected query string when multiple exceptions in array', () => { + const formattedQuery = formatQuery({ + exceptions: ['b:("value-1" or "value-2") and not c:*', 'not d:*'], + language: 'kuery', + exclude: false, + }); + expect(formattedQuery).toEqual('(b:("value-1" or "value-2") and not c:*) or (not d:*)'); }); - expect(formattedQuery).toEqual('b:("value-1" or "value-2") and not c:* and not d:*'); }); }); @@ -713,73 +436,69 @@ describe('build_exceptions_query', () => { test('it returns empty string if empty lists array passed in', () => { const query = buildExceptionItemEntries({ language: 'kuery', - lists: [], - exclude, + entries: [], }); expect(query).toEqual(''); }); - test('it returns expected query when more than one item in list', () => { + test('it returns expected query when more than one item in exception item', () => { const payload: EntriesArray = [ makeMatchAnyEntry({ field: 'b' }), makeMatchEntry({ field: 'c', operator: 'excluded', value: 'value-3' }), ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists: payload, - exclude, + entries: payload, }); - const expectedQuery = 'not b:("value-1" or "value-2") and c:"value-3"'; + const expectedQuery = 'b:("value-1" or "value-2") and not c:"value-3"'; expect(query).toEqual(expectedQuery); }); - test('it returns expected query when list item includes nested value', () => { - const lists: EntriesArray = [ + test('it returns expected query when exception item includes nested value', () => { + const entries: EntriesArray = [ makeMatchAnyEntry({ field: 'b' }), { field: 'parent', type: 'nested', entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded', value: 'value-3' }), + makeMatchEntry({ field: 'nestedField', operator: 'included', value: 'value-3' }), ], }, ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:("value-1" or "value-2") and parent:{ nestedField:"value-3" }'; + const expectedQuery = 'b:("value-1" or "value-2") and parent:{ nestedField:"value-3" }'; expect(query).toEqual(expectedQuery); }); - test('it returns expected query when list includes multiple items and nested "and" values', () => { - const lists: EntriesArray = [ + test('it returns expected query when exception item includes multiple items and nested "and" values', () => { + const entries: EntriesArray = [ makeMatchAnyEntry({ field: 'b' }), { field: 'parent', type: 'nested', entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded', value: 'value-3' }), + makeMatchEntry({ field: 'nestedField', operator: 'included', value: 'value-3' }), ], }, makeExistsEntry({ field: 'd' }), ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); const expectedQuery = - 'not b:("value-1" or "value-2") and parent:{ nestedField:"value-3" } and not d:*'; + 'b:("value-1" or "value-2") and parent:{ nestedField:"value-3" } and d:*'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when language is "lucene"', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchAnyEntry({ field: 'b' }), { field: 'parent', @@ -792,156 +511,56 @@ describe('build_exceptions_query', () => { ]; const query = buildExceptionItemEntries({ language: 'lucene', - lists, - exclude, + entries, }); const expectedQuery = - 'NOT b:("value-1" OR "value-2") AND parent:{ nestedField:"value-3" } AND _exists_e'; + 'b:("value-1" OR "value-2") AND parent:{ nestedField:"value-3" } AND NOT _exists_e'; expect(query).toEqual(expectedQuery); }); - describe('when "exclude" is false', () => { - beforeEach(() => { - exclude = false; - }); - - test('it returns empty string if empty lists array passed in', () => { - const query = buildExceptionItemEntries({ - language: 'kuery', - lists: [], - exclude, - }); - - expect(query).toEqual(''); - }); - - test('it returns expected query when more than one item in list', () => { - const payload: EntriesArray = [ - makeMatchAnyEntry({ field: 'b' }), - makeMatchEntry({ field: 'c', operator: 'excluded', value: 'value-3' }), - ]; - const query = buildExceptionItemEntries({ - language: 'kuery', - lists: payload, - exclude, - }); - const expectedQuery = 'b:("value-1" or "value-2") and not c:"value-3"'; - - expect(query).toEqual(expectedQuery); - }); - - test('it returns expected query when list item includes nested value', () => { - const lists: EntriesArray = [ - makeMatchAnyEntry({ field: 'b' }), - { - field: 'parent', - type: 'nested', - entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded', value: 'value-3' }), - ], - }, - ]; - const query = buildExceptionItemEntries({ - language: 'kuery', - lists, - exclude, - }); - const expectedQuery = 'b:("value-1" or "value-2") and parent:{ nestedField:"value-3" }'; - - expect(query).toEqual(expectedQuery); - }); - - test('it returns expected query when list includes multiple items and nested "and" values', () => { - const lists: EntriesArray = [ - makeMatchAnyEntry({ field: 'b' }), - { - field: 'parent', - type: 'nested', - entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded', value: 'value-3' }), - ], - }, - makeExistsEntry({ field: 'd' }), - ]; - const query = buildExceptionItemEntries({ - language: 'kuery', - lists, - exclude, - }); - const expectedQuery = - 'b:("value-1" or "value-2") and parent:{ nestedField:"value-3" } and d:*'; - expect(query).toEqual(expectedQuery); - }); - - test('it returns expected query when language is "lucene"', () => { - const lists: EntriesArray = [ - makeMatchAnyEntry({ field: 'b' }), - { - field: 'parent', - type: 'nested', - entries: [ - makeMatchEntry({ field: 'nestedField', operator: 'excluded', value: 'value-3' }), - ], - }, - makeExistsEntry({ field: 'e', operator: 'excluded' }), - ]; - const query = buildExceptionItemEntries({ - language: 'lucene', - lists, - exclude, - }); - const expectedQuery = - 'b:("value-1" OR "value-2") AND parent:{ nestedField:"value-3" } AND NOT _exists_e'; - expect(query).toEqual(expectedQuery); - }); - }); - describe('exists', () => { test('it returns expected query when list includes single list item with operator of "included"', () => { - const lists: EntriesArray = [makeExistsEntry({ field: 'b' })]; + const entries: EntriesArray = [makeExistsEntry({ field: 'b' })]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:*'; + const expectedQuery = 'b:*'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes single list item with operator of "excluded"', () => { - const lists: EntriesArray = [makeExistsEntry({ field: 'b', operator: 'excluded' })]; + const entries: EntriesArray = [makeExistsEntry({ field: 'b', operator: 'excluded' })]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:*'; + const expectedQuery = 'not b:*'; expect(query).toEqual(expectedQuery); }); - test('it returns expected query when list includes list item with "and" values', () => { - const lists: EntriesArray = [ + test('it returns expected query when exception item includes entry item with "and" values', () => { + const entries: EntriesArray = [ makeExistsEntry({ field: 'b', operator: 'excluded' }), { field: 'parent', type: 'nested', - entries: [makeMatchEntry({ field: 'c', operator: 'excluded', value: 'value-1' })], + entries: [makeMatchEntry({ field: 'c', operator: 'included', value: 'value-1' })], }, ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:* and parent:{ c:"value-1" }'; + const expectedQuery = 'not b:* and parent:{ c:"value-1" }'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes multiple items', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeExistsEntry({ field: 'b' }), { field: 'parent', @@ -955,10 +574,9 @@ describe('build_exceptions_query', () => { ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:* and parent:{ c:"value-1" and d:"value-2" } and not e:*'; + const expectedQuery = 'b:* and parent:{ c:"value-1" and d:"value-2" } and e:*'; expect(query).toEqual(expectedQuery); }); @@ -966,52 +584,49 @@ describe('build_exceptions_query', () => { describe('match', () => { test('it returns expected query when list includes single list item with operator of "included"', () => { - const lists: EntriesArray = [makeMatchEntry({ field: 'b', value: 'value' })]; + const entries: EntriesArray = [makeMatchEntry({ field: 'b', value: 'value' })]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:"value"'; + const expectedQuery = 'b:"value"'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes single list item with operator of "excluded"', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchEntry({ field: 'b', operator: 'excluded', value: 'value' }), ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:"value"'; + const expectedQuery = 'not b:"value"'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes list item with "and" values', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchEntry({ field: 'b', operator: 'excluded', value: 'value' }), { field: 'parent', type: 'nested', - entries: [makeMatchEntry({ field: 'c', operator: 'excluded', value: 'valueC' })], + entries: [makeMatchEntry({ field: 'c', operator: 'included', value: 'valueC' })], }, ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:"value" and parent:{ c:"valueC" }'; + const expectedQuery = 'not b:"value" and parent:{ c:"valueC" }'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes multiple items', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchEntry({ field: 'b', value: 'value' }), { field: 'parent', @@ -1025,11 +640,9 @@ describe('build_exceptions_query', () => { ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = - 'not b:"value" and parent:{ c:"valueC" and d:"valueD" } and not e:"valueE"'; + const expectedQuery = 'b:"value" and parent:{ c:"valueC" and d:"valueD" } and e:"valueE"'; expect(query).toEqual(expectedQuery); }); @@ -1037,31 +650,29 @@ describe('build_exceptions_query', () => { describe('match_any', () => { test('it returns expected query when list includes single list item with operator of "included"', () => { - const lists: EntriesArray = [makeMatchAnyEntry({ field: 'b' })]; + const entries: EntriesArray = [makeMatchAnyEntry({ field: 'b' })]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:("value-1" or "value-2")'; + const expectedQuery = 'b:("value-1" or "value-2")'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes single list item with operator of "excluded"', () => { - const lists: EntriesArray = [makeMatchAnyEntry({ field: 'b', operator: 'excluded' })]; + const entries: EntriesArray = [makeMatchAnyEntry({ field: 'b', operator: 'excluded' })]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:("value-1" or "value-2")'; + const expectedQuery = 'not b:("value-1" or "value-2")'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes list item with nested values', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchAnyEntry({ field: 'b', operator: 'excluded' }), { field: 'parent', @@ -1071,25 +682,23 @@ describe('build_exceptions_query', () => { ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'b:("value-1" or "value-2") and parent:{ c:"valueC" }'; + const expectedQuery = 'not b:("value-1" or "value-2") and parent:{ c:"valueC" }'; expect(query).toEqual(expectedQuery); }); test('it returns expected query when list includes multiple items', () => { - const lists: EntriesArray = [ + const entries: EntriesArray = [ makeMatchAnyEntry({ field: 'b' }), makeMatchAnyEntry({ field: 'c' }), ]; const query = buildExceptionItemEntries({ language: 'kuery', - lists, - exclude, + entries, }); - const expectedQuery = 'not b:("value-1" or "value-2") and not c:("value-1" or "value-2")'; + const expectedQuery = 'b:("value-1" or "value-2") and c:("value-1" or "value-2")'; expect(query).toEqual(expectedQuery); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts b/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts index ce9be112ba95b0..fc4fbae02b8fbd 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/build_exceptions_query.ts @@ -160,6 +160,10 @@ export const formatQuery = ({ language: Language; exclude: boolean; }): string => { + if (exceptions == null || (exceptions != null && exceptions.length === 0)) { + return ''; + } + const or = getLanguageBooleanOperator({ language, value: 'or' }); const not = getLanguageBooleanOperator({ language, value: 'not' }); const formattedExceptionItems = exceptions.map((exceptionItem, index) => { diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts index b75bcc14392de7..a8eb4e7bbb15bb 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts @@ -365,28 +365,28 @@ describe('get_filter', () => { { bool: { minimum_should_match: 1, should: [{ match: { 'host.name': 'linux' } }] } }, { bool: { - filter: [ - { - nested: { - path: 'some.parentField', - query: { - bool: { - minimum_should_match: 1, - should: [ - { - match_phrase: { - 'some.parentField.nested.field': 'some value', - }, + must_not: { + bool: { + filter: [ + { + nested: { + path: 'some.parentField', + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + 'some.parentField.nested.field': 'some value', + }, + }, + ], }, - ], + }, + score_mode: 'none', }, }, - score_mode: 'none', - }, - }, - { - bool: { - must_not: { + { bool: { minimum_should_match: 1, should: [ @@ -398,9 +398,9 @@ describe('get_filter', () => { ], }, }, - }, + ], }, - ], + }, }, }, ], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 779d54070fd211..fa1635324e0f26 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -9,7 +9,6 @@ import sinon from 'sinon'; import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import { listMock } from '../../../../../lists/server/mocks'; -import { EntriesArray } from '../../../../common/shared_imports'; import { buildRuleMessageFactory } from './rule_messages'; import { ExceptionListClient } from '../../../../../lists/server'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock';