From 9fb17237953f9b1444d76436c8b20c9a1523ee36 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 30 Oct 2024 11:35:56 +0100 Subject: [PATCH 1/3] fix: adapt special character replacement according to kuery.peg --- .../components/utils/keury/index.test.ts | 24 +++++++++++++++++++ .../public/components/utils/keury/index.ts | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts index 936053a18be5c..692fa9303c8e8 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts @@ -62,4 +62,28 @@ describe('Kuery escape', () => { const expected = 'This\\nhas\\tnewlines\\r\\nwith\\ttabs'; expect(escapeKuery(value)).to.be(expected); }); + + it('should escape backslashes', () => { + const value = 'This\\has\\backslashes'; + const expected = 'This\\\\has\\\\backslashes'; + expect(escapeKuery(value)).to.be(expected); + }); + + it('should escape multiple backslashes and quotes', () => { + const value = 'This\\ has 2" quotes & \\ 2 "backslashes'; + const expected = 'This\\\\ has 2\\" quotes & \\\\ 2 \\"backslashes'; + expect(escapeKuery(value)).to.be(expected); + }); + + it('should escape all special character according to kuery.peg SpecialCharacter rule', () => { + /* + * Ref: packages/kbn-es-query/grammar/grammar.peggy + * + * SpecialCharacter + * = [\\():<>"*{}] + */ + const value = `\\():"*{}`; + const expected = `\\\\\\(\\)\\:\\"\\*\\{\\}`; + expect(escapeKuery(value)).to.be(expected); + }); }); diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.ts index 3eb03cc63543e..6453463860f3b 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.ts @@ -57,7 +57,7 @@ const escapeWhitespace = (val: string) => val.replace(/\t/g, '\\t').replace(/\r/g, '\\r').replace(/\n/g, '\\n'); // See the SpecialCharacter rule in kuery.peg -const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); // $& means the whole matched string +const escapeSpecialCharacters = (val: string) => val.replace(/["\\\(\)\:\<\>\*\{\}]/g, '\\$&'); // $& means the whole matched string // See the Keyword rule in kuery.peg // I do not think that we need that anymore since we are doing a full match_phrase all the time now => return `"${escapeKuery(val)}"`; From 0184821a7188409175b3b9e08e11fe1112b08f6e Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Wed, 30 Oct 2024 17:56:00 +0100 Subject: [PATCH 2/3] fix: port escapeKuery from 8.16->7.17 --- packages/kbn-es-query/src/kuery/index.ts | 1 + .../src/kuery/utils/escape_kuery.ts | 42 +++++ .../components/utils/keury/index.test.ts | 144 ++++++++---------- .../public/components/utils/keury/index.ts | 17 +-- 4 files changed, 112 insertions(+), 92 deletions(-) create mode 100644 packages/kbn-es-query/src/kuery/utils/escape_kuery.ts diff --git a/packages/kbn-es-query/src/kuery/index.ts b/packages/kbn-es-query/src/kuery/index.ts index d7b0c902e47f1..8d19a814ffd6e 100644 --- a/packages/kbn-es-query/src/kuery/index.ts +++ b/packages/kbn-es-query/src/kuery/index.ts @@ -24,3 +24,4 @@ export { KQLSyntaxError } from './kuery_syntax_error'; export { nodeTypes, nodeBuilder } from './node_types'; export { fromKueryExpression } from './ast'; export type { DslQuery, KueryNode, KueryQueryOptions } from './types'; +export { escapeKuery } from './utils/escape_kuery'; diff --git a/packages/kbn-es-query/src/kuery/utils/escape_kuery.ts b/packages/kbn-es-query/src/kuery/utils/escape_kuery.ts new file mode 100644 index 0000000000000..62eede9fe5340 --- /dev/null +++ b/packages/kbn-es-query/src/kuery/utils/escape_kuery.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { flow } from 'lodash'; + +/** + * Escapes backslashes and double-quotes. (Useful when putting a string in quotes to use as a value + * in a KQL expression. See the QuotedCharacter rule in kuery.peg.) + */ +export function escapeQuotes(str: string) { + return str.replace(/[\\"]/g, '\\$&'); +} + +/** + * Escapes a Kuery node value to ensure that special characters, operators, and whitespace do not result in a parsing error or unintended + * behavior when using the value as an argument for the `buildNode` function. + */ +export const escapeKuery = flow(escapeSpecialCharacters, escapeAndOr, escapeNot, escapeWhitespace); + +// See the SpecialCharacter rule in kuery.peg +function escapeSpecialCharacters(str: string) { + return str.replace(/[\\():<>"*]/g, '\\$&'); // $& means the whole matched string +} + +// See the Keyword rule in kuery.peg +function escapeAndOr(str: string) { + return str.replace(/(\s+)(and|or)(\s+)/gi, '$1\\$2$3'); +} + +function escapeNot(str: string) { + return str.replace(/not(\s+)/gi, '\\$&'); +} + +// See the Space rule in kuery.peg +function escapeWhitespace(str: string) { + return str.replace(/\t/g, '\\t').replace(/\r/g, '\\r').replace(/\n/g, '\\n'); +} diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts index 692fa9303c8e8..b83ad9573028b 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts @@ -6,84 +6,74 @@ */ import expect from '@kbn/expect'; -import { escapeKuery } from '.'; +import { escapeQueryValue } from '.'; -describe('Kuery escape', () => { - it('should not remove white spaces quotes', () => { - const value = ' netcat'; - const expected = ' netcat'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape quotes', () => { - const value = 'I said, "Hello."'; - const expected = 'I said, \\"Hello.\\"'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape special characters', () => { - const value = `This \\ has (a lot of) characters, don't you *think*? "Yes."`; - const expected = `This \\ has (a lot of) characters, don't you *think*? \\"Yes.\\"`; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should NOT escape keywords', () => { - const value = 'foo and bar or baz not qux'; - const expected = 'foo and bar or baz not qux'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should NOT escape keywords next to each other', () => { - const value = 'foo and bar or not baz'; - const expected = 'foo and bar or not baz'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should not escape keywords without surrounding spaces', () => { - const value = 'And this has keywords, or does it not?'; - const expected = 'And this has keywords, or does it not?'; - expect(escapeKuery(value)).to.be(expected); - }); +const TEST_QUERIES = [ + { + description: 'should not remove white spaces quotes', + value: ' netcat', + expected: ' netcat', + }, + { + description: 'should escape quotes', + value: 'I said, "Hello."', + expected: 'I said, \\"Hello.\\"', + }, + { + description: 'should escape special characters', + value: `This \\ has (a lot of) characters, don't you *think*? "Yes."`, + expected: `This \\\\ has \\(a lot of\\) \\ characters, don't you \\*think\\*? \\"Yes.\\"`, + }, + { + description: 'should NOT escape keywords', + value: 'foo and bar or baz not qux', + expected: 'foo and bar or baz not qux', + }, + { + description: 'should NOT escape keywords next to each other', + value: 'foo and bar or not baz', + expected: 'foo and bar or not baz', + }, + { + description: 'should not escape keywords without surrounding spaces', + value: 'And this has keywords, or does it not?', + expected: 'And this has keywords, or does it not?', + }, + { + description: 'should NOT escape uppercase keywords', + value: 'foo AND bar', + expected: 'foo AND bar', + }, + { + description: 'should escape special characters and NOT keywords', + value: 'Hello, "world", and to meet you!', + expected: 'Hello, \\"world\\", and to meet you!', + }, + { + description: 'should escape newlines and tabs', + value: 'This\nhas\tnewlines\r\nwith\ttabs', + expected: 'This\\nhas\\tnewlines\\r\\nwith\\ttabs', + }, + { + description: 'should escape backslashes', + value: 'This\\has\\backslashes', + expected: 'This\\\\has\\\\backslashes', + }, + { + description: 'should escape multiple backslashes and quotes', + value: 'This\\ has 2" quotes & \\ 2 "backslashes', + expected: 'This\\\\ has 2\\" quotes & \\\\ 2 \\"backslashes', + }, + { + description: 'should escape all special character according to kuery.peg SpecialCharacter rule', + value: '\\():"*', + expected: '\\\\\\(\\)\\:\\"\\*', + }, +]; - it('should NOT escape uppercase keywords', () => { - const value = 'foo AND bar'; - const expected = 'foo AND bar'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape special characters and NOT keywords', () => { - const value = 'Hello, "world", and to meet you!'; - const expected = 'Hello, \\"world\\", and to meet you!'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape newlines and tabs', () => { - const value = 'This\nhas\tnewlines\r\nwith\ttabs'; - const expected = 'This\\nhas\\tnewlines\\r\\nwith\\ttabs'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape backslashes', () => { - const value = 'This\\has\\backslashes'; - const expected = 'This\\\\has\\\\backslashes'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape multiple backslashes and quotes', () => { - const value = 'This\\ has 2" quotes & \\ 2 "backslashes'; - const expected = 'This\\\\ has 2\\" quotes & \\\\ 2 \\"backslashes'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape all special character according to kuery.peg SpecialCharacter rule', () => { - /* - * Ref: packages/kbn-es-query/grammar/grammar.peggy - * - * SpecialCharacter - * = [\\():<>"*{}] - */ - const value = `\\():"*{}`; - const expected = `\\\\\\(\\)\\:\\"\\*\\{\\}`; - expect(escapeKuery(value)).to.be(expected); +describe('Kuery escape', () => { + it.each(TEST_QUERIES)('$description', ({ description, value, expected }) => { + const result = escapeQueryValue(value); + expect(result).to.be(`"${expected}"`); }); }); diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.ts index 6453463860f3b..485a4bdde9972 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty, isString, flow } from 'lodash/fp'; +import { isEmpty, isString } from 'lodash/fp'; import { buildEsQuery, EsQueryConfig, @@ -14,6 +14,7 @@ import { IndexPatternBase, Query, toElasticsearchQuery, + escapeKuery, } from '@kbn/es-query'; export const convertKueryToElasticSearchQuery = ( @@ -53,20 +54,6 @@ export const escapeQueryValue = (val: number | string = ''): string | number => return val; }; -const escapeWhitespace = (val: string) => - val.replace(/\t/g, '\\t').replace(/\r/g, '\\r').replace(/\n/g, '\\n'); - -// See the SpecialCharacter rule in kuery.peg -const escapeSpecialCharacters = (val: string) => val.replace(/["\\\(\)\:\<\>\*\{\}]/g, '\\$&'); // $& means the whole matched string - -// See the Keyword rule in kuery.peg -// I do not think that we need that anymore since we are doing a full match_phrase all the time now => return `"${escapeKuery(val)}"`; -// const escapeAndOr = (val: string) => val.replace(/(\s+)(and|or)(\s+)/gi, '$1\\$2$3'); - -// const escapeNot = (val: string) => val.replace(/not(\s+)/gi, '\\$&'); - -export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace); - export const convertToBuildEsQuery = ({ config, indexPattern, From c0b7a1bf7e84af82c7aed664f9f1a83ffd4c4723 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Mon, 4 Nov 2024 12:48:10 +0100 Subject: [PATCH 3/3] replicate PR #128289 --- .../src/kuery/utils/escape_kuery.test.ts | 74 +++++++++++ .../lib/escape_kuery.test.ts | 51 +------- .../kql_query_suggestion/lib/escape_kuery.ts | 24 +--- .../public/common/lib/keury/index.test.ts | 120 ++++++++++-------- .../public/common/lib/keury/index.ts | 17 +-- .../components/utils/keury/index.test.ts | 18 +-- 6 files changed, 156 insertions(+), 148 deletions(-) create mode 100644 packages/kbn-es-query/src/kuery/utils/escape_kuery.test.ts diff --git a/packages/kbn-es-query/src/kuery/utils/escape_kuery.test.ts b/packages/kbn-es-query/src/kuery/utils/escape_kuery.test.ts new file mode 100644 index 0000000000000..933449e779ef7 --- /dev/null +++ b/packages/kbn-es-query/src/kuery/utils/escape_kuery.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { escapeQuotes, escapeKuery } from './escape_kuery'; + +describe('Kuery escape', () => { + test('should escape quotes', () => { + const value = 'I said, "Hello."'; + const expected = 'I said, \\"Hello.\\"'; + + expect(escapeQuotes(value)).toBe(expected); + }); + + test('should escape backslashes and quotes', () => { + const value = 'Backslashes \\" in the middle and ends with quotes \\"'; + const expected = 'Backslashes \\\\\\" in the middle and ends with quotes \\\\\\"'; + + expect(escapeQuotes(value)).toBe(expected); + }); + + test('should escape special characters', () => { + const value = `This \\ has (a lot of) characters, don't you *think*? "Yes."`; + const expected = `This \\\\ has \\(a lot of\\) \\ characters, don't you \\*think\\*? \\"Yes.\\"`; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should escape keywords', () => { + const value = 'foo and bar or baz not qux'; + const expected = 'foo \\and bar \\or baz \\not qux'; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should escape keywords next to each other', () => { + const value = 'foo and bar or not baz'; + const expected = 'foo \\and bar \\or \\not baz'; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should not escape keywords without surrounding spaces', () => { + const value = 'And this has keywords, or does it not?'; + const expected = 'And this has keywords, \\or does it not?'; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should escape uppercase keywords', () => { + const value = 'foo AND bar'; + const expected = 'foo \\AND bar'; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should escape both keywords and special characters', () => { + const value = 'Hello, world, and to meet you!'; + const expected = 'Hello, world, \\and \\ to meet you!'; + + expect(escapeKuery(value)).toBe(expected); + }); + + test('should escape newlines and tabs', () => { + const value = 'This\nhas\tnewlines\r\nwith\ttabs'; + const expected = 'This\\nhas\\tnewlines\\r\\nwith\\ttabs'; + + expect(escapeKuery(value)).toBe(expected); + }); +}); diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts index 933449e779ef7..162c461f7a175 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts +++ b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { escapeQuotes, escapeKuery } from './escape_kuery'; +import { escapeQuotes } from './escape_kuery'; describe('Kuery escape', () => { test('should escape quotes', () => { @@ -22,53 +22,4 @@ describe('Kuery escape', () => { expect(escapeQuotes(value)).toBe(expected); }); - - test('should escape special characters', () => { - const value = `This \\ has (a lot of) characters, don't you *think*? "Yes."`; - const expected = `This \\\\ has \\(a lot of\\) \\ characters, don't you \\*think\\*? \\"Yes.\\"`; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should escape keywords', () => { - const value = 'foo and bar or baz not qux'; - const expected = 'foo \\and bar \\or baz \\not qux'; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should escape keywords next to each other', () => { - const value = 'foo and bar or not baz'; - const expected = 'foo \\and bar \\or \\not baz'; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should not escape keywords without surrounding spaces', () => { - const value = 'And this has keywords, or does it not?'; - const expected = 'And this has keywords, \\or does it not?'; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should escape uppercase keywords', () => { - const value = 'foo AND bar'; - const expected = 'foo \\AND bar'; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should escape both keywords and special characters', () => { - const value = 'Hello, world, and to meet you!'; - const expected = 'Hello, world, \\and \\ to meet you!'; - - expect(escapeKuery(value)).toBe(expected); - }); - - test('should escape newlines and tabs', () => { - const value = 'This\nhas\tnewlines\r\nwith\ttabs'; - const expected = 'This\\nhas\\tnewlines\\r\\nwith\\ttabs'; - - expect(escapeKuery(value)).toBe(expected); - }); }); diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts index 54f03803a893e..6636f9b602687 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts +++ b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { flow } from 'lodash'; +import { escapeKuery } from '@kbn/es-query'; /** * Escapes backslashes and double-quotes. (Useful when putting a string in quotes to use as a value @@ -16,23 +16,5 @@ export function escapeQuotes(str: string) { return str.replace(/[\\"]/g, '\\$&'); } -export const escapeKuery = flow(escapeSpecialCharacters, escapeAndOr, escapeNot, escapeWhitespace); - -// See the SpecialCharacter rule in kuery.peg -function escapeSpecialCharacters(str: string) { - return str.replace(/[\\():<>"*]/g, '\\$&'); // $& means the whole matched string -} - -// See the Keyword rule in kuery.peg -function escapeAndOr(str: string) { - return str.replace(/(\s+)(and|or)(\s+)/gi, '$1\\$2$3'); -} - -function escapeNot(str: string) { - return str.replace(/not(\s+)/gi, '\\$&'); -} - -// See the Space rule in kuery.peg -function escapeWhitespace(str: string) { - return str.replace(/\t/g, '\\t').replace(/\r/g, '\\r').replace(/\n/g, '\\n'); -} +// Re-export this function from the @kbn/es-query package to avoid refactoring +export { escapeKuery }; diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.test.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.test.ts index 936053a18be5c..2b29b251204af 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.test.ts @@ -6,60 +6,74 @@ */ import expect from '@kbn/expect'; -import { escapeKuery } from '.'; +import { escapeQueryValue } from '.'; -describe('Kuery escape', () => { - it('should not remove white spaces quotes', () => { - const value = ' netcat'; - const expected = ' netcat'; - expect(escapeKuery(value)).to.be(expected); - }); +const TEST_QUERIES = [ + { + description: 'should not remove white spaces quotes', + value: ' netcat', + expected: ' netcat', + }, + { + description: 'should escape quotes', + value: 'I said, "Hello."', + expected: 'I said, \\"Hello.\\"', + }, + { + description: 'should escape special characters', + value: `This \\ has (a lot of) characters, don't you *think*? "Yes."`, + expected: `This \\\\ has \\(a lot of\\) \\ characters, don't you \\*think\\*? \\"Yes.\\"`, + }, + { + description: 'should escape keywords', + value: 'foo and bar or baz not qux', + expected: 'foo \\and bar \\or baz \\not qux', + }, + { + description: 'should escape keywords next to each other', + value: 'foo and bar or not baz', + expected: 'foo \\and bar \\or \\not baz', + }, + { + description: 'should NOT escape keywords without surrounding spaces', + value: 'And this has keywords, or does it not?', + expected: 'And this has keywords, \\or does it not?', + }, + { + description: 'should escape uppercase keywords', + value: 'foo AND bar', + expected: 'foo \\AND bar', + }, + { + description: 'should escape special characters and NOT keywords', + value: 'Hello, "world", and to meet you!', + expected: 'Hello, \\"world\\", \\and \\ to meet you!', + }, + { + description: 'should escape newlines and tabs', + value: 'This\nhas\tnewlines\r\nwith\ttabs', + expected: 'This\\nhas\\tnewlines\\r\\nwith\\ttabs', + }, + { + description: 'should escape backslashes', + value: 'This\\has\\backslashes', + expected: 'This\\\\has\\\\backslashes', + }, + { + description: 'should escape multiple backslashes and quotes', + value: 'This\\ has 2" quotes & \\ 2 "backslashes', + expected: 'This\\\\ has 2\\" quotes & \\\\ 2 \\"backslashes', + }, + { + description: 'should escape all special character according to kuery.peg SpecialCharacter rule', + value: '\\():"*', + expected: '\\\\\\(\\)\\:\\"\\*', + }, +]; - it('should escape quotes', () => { - const value = 'I said, "Hello."'; - const expected = 'I said, \\"Hello.\\"'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape special characters', () => { - const value = `This \\ has (a lot of) characters, don't you *think*? "Yes."`; - const expected = `This \\ has (a lot of) characters, don't you *think*? \\"Yes.\\"`; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should NOT escape keywords', () => { - const value = 'foo and bar or baz not qux'; - const expected = 'foo and bar or baz not qux'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should NOT escape keywords next to each other', () => { - const value = 'foo and bar or not baz'; - const expected = 'foo and bar or not baz'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should not escape keywords without surrounding spaces', () => { - const value = 'And this has keywords, or does it not?'; - const expected = 'And this has keywords, or does it not?'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should NOT escape uppercase keywords', () => { - const value = 'foo AND bar'; - const expected = 'foo AND bar'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape special characters and NOT keywords', () => { - const value = 'Hello, "world", and to meet you!'; - const expected = 'Hello, \\"world\\", and to meet you!'; - expect(escapeKuery(value)).to.be(expected); - }); - - it('should escape newlines and tabs', () => { - const value = 'This\nhas\tnewlines\r\nwith\ttabs'; - const expected = 'This\\nhas\\tnewlines\\r\\nwith\\ttabs'; - expect(escapeKuery(value)).to.be(expected); +describe('Kuery escape', () => { + it.each(TEST_QUERIES)('$description', ({ description, value, expected }) => { + const result = escapeQueryValue(value); + expect(result).to.be(`"${expected}"`); }); }); diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index 1e7574cdae26d..4a387ec494e6a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isEmpty, isString, flow } from 'lodash/fp'; +import { isEmpty, isString } from 'lodash/fp'; import { EsQueryConfig, @@ -15,6 +15,7 @@ import { toElasticsearchQuery, fromKueryExpression, IndexPatternBase, + escapeKuery, } from '@kbn/es-query'; export const convertKueryToElasticSearchQuery = ( @@ -54,20 +55,6 @@ export const escapeQueryValue = (val: number | string = ''): string | number => return val; }; -const escapeWhitespace = (val: string) => - val.replace(/\t/g, '\\t').replace(/\r/g, '\\r').replace(/\n/g, '\\n'); - -// See the SpecialCharacter rule in kuery.peg -const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); // $& means the whole matched string - -// See the Keyword rule in kuery.peg -// I do not think that we need that anymore since we are doing a full match_phrase all the time now => return `"${escapeKuery(val)}"`; -// const escapeAndOr = (val: string) => val.replace(/(\s+)(and|or)(\s+)/gi, '$1\\$2$3'); - -// const escapeNot = (val: string) => val.replace(/not(\s+)/gi, '\\$&'); - -export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace); - export const convertToBuildEsQuery = ({ config, indexPattern, diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts index b83ad9573028b..2b29b251204af 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.test.ts @@ -25,29 +25,29 @@ const TEST_QUERIES = [ expected: `This \\\\ has \\(a lot of\\) \\ characters, don't you \\*think\\*? \\"Yes.\\"`, }, { - description: 'should NOT escape keywords', + description: 'should escape keywords', value: 'foo and bar or baz not qux', - expected: 'foo and bar or baz not qux', + expected: 'foo \\and bar \\or baz \\not qux', }, { - description: 'should NOT escape keywords next to each other', + description: 'should escape keywords next to each other', value: 'foo and bar or not baz', - expected: 'foo and bar or not baz', + expected: 'foo \\and bar \\or \\not baz', }, { - description: 'should not escape keywords without surrounding spaces', + description: 'should NOT escape keywords without surrounding spaces', value: 'And this has keywords, or does it not?', - expected: 'And this has keywords, or does it not?', + expected: 'And this has keywords, \\or does it not?', }, { - description: 'should NOT escape uppercase keywords', + description: 'should escape uppercase keywords', value: 'foo AND bar', - expected: 'foo AND bar', + expected: 'foo \\AND bar', }, { description: 'should escape special characters and NOT keywords', value: 'Hello, "world", and to meet you!', - expected: 'Hello, \\"world\\", and to meet you!', + expected: 'Hello, \\"world\\", \\and \\ to meet you!', }, { description: 'should escape newlines and tabs',