diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/form_based.test.ts b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/form_based.test.ts index 990e8a1e30c5e..7814a30769142 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/form_based.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/form_based.test.ts @@ -2481,6 +2481,59 @@ describe('IndexPattern Data Source', () => { disabled: { kuery: [], lucene: [] }, }); }); + it('should escape special characters in term values for valid KQL syntax', () => { + publicAPI = FormBasedDatasource.getPublicAPI({ + state: { + ...baseState, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'path.keyword', + params: { + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + size: 10, + }, + } as TermsIndexPatternColumn, + }, + }, + }, + }, + layerId: 'first', + indexPatterns, + }); + const data = { + first: { + type: 'datatable' as const, + columns: [{ id: 'col1', name: 'path.keyword', meta: { type: 'string' as const } }], + rows: [ + { col1: 'C:\\' }, + { col1: 'path with "quotes"' }, + { col1: 'backslash\\and"quote' }, + ], + }, + }; + expect(publicAPI.getFilters(data)).toEqual({ + enabled: { + kuery: [ + [ + { language: 'kuery', query: 'path.keyword: "C:\\\\"' }, + { language: 'kuery', query: 'path.keyword: "path with \\"quotes\\""' }, + { language: 'kuery', query: 'path.keyword: "backslash\\\\and\\"quote"' }, + ], + ], + lucene: [], + }, + disabled: { kuery: [], lucene: [] }, + }); + }); it('should ignore top values fields if other/missing option is enabled', () => { publicAPI = FormBasedDatasource.getPublicAPI({ state: { diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/utils.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/utils.tsx index 1ba8f7ca959c7..75fefd1bc9b7c 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/form_based/utils.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/form_based/utils.tsx @@ -11,11 +11,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { DocLinksStart, ThemeServiceStart } from '@kbn/core/public'; import { hasUnsupportedDownsampledAggregationFailure } from '@kbn/search-response-warnings'; import type { DatatableUtilitiesService } from '@kbn/data-plugin/common'; -import type { TimeRange } from '@kbn/es-query'; +import { escapeQuotes, type TimeRange } from '@kbn/es-query'; import { EuiLink, EuiSpacer } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { groupBy, escape, uniq, uniqBy } from 'lodash'; +import { groupBy, uniq, uniqBy } from 'lodash'; import type { Query } from '@kbn/data-plugin/common'; import { @@ -747,13 +747,10 @@ function extractQueriesFromTerms( } if (typeof value !== 'string' && Array.isArray(value.keys)) { return value.keys - .map( - (term: string, index: number) => - `${fields[index]}: ${`"${term === '' ? escape(term) : term}"`}` - ) + .map((term: string, index: number) => `${fields[index]}: "${escapeQuotes(term)}"`) .join(' AND '); } - return `${column.sourceField}: ${`"${value === '' ? escape(value) : value}"`}`; + return `${column.sourceField}: "${escapeQuotes(String(value))}"`; }) .filter(Boolean) as string[];