diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index ac401626659dd..c73c52cecf5f8 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -22,6 +22,7 @@ compatible with other configuration settings. Deleting a custom setting removes .Kibana Settings Reference [horizontal] `query:queryString:options`:: Options for the Lucene query string parser. +`query:allowLeadingWildcards`:: When set, * is allowed as the first character in a query clause. Currently only applies when experimental query features are enabled in the query bar. To disallow leading wildcards in basic lucene queries, use query:queryString:options. `search:queryLanguage`:: Default is `lucene`. Query language used by the query bar. Choose between the lucene query syntax and kuery, an experimental new language built specifically for Kibana. `search:queryLanguage:switcher:enable`:: Show or hide the query language switcher in the query bar. `sort:options`:: Options for the Elasticsearch {ref}/search-request-sort.html[sort] parameter. diff --git a/src/core_plugins/kibana/ui_setting_defaults.js b/src/core_plugins/kibana/ui_setting_defaults.js index 3840376307296..25116389fe479 100644 --- a/src/core_plugins/kibana/ui_setting_defaults.js +++ b/src/core_plugins/kibana/ui_setting_defaults.js @@ -18,6 +18,10 @@ export function getUiSettingDefaults() { description: 'Options for the lucene query string parser', type: 'json' }, + 'query:allowLeadingWildcards': { + value: true, + description: 'When set, * is allowed as the first character in a query clause. Currently only applies when experimental query features are enabled in the query bar. To disallow leading wildcards in basic lucene queries, use query:queryString:options', + }, 'search:queryLanguage': { value: 'lucene', description: 'Query language used by the query bar. Kuery is an experimental new language built specifically for Kibana.', diff --git a/src/ui/public/courier/data_source/build_query/build_es_query.js b/src/ui/public/courier/data_source/build_query/build_es_query.js index 00e133268b3e7..fe855f3db9ade 100644 --- a/src/ui/public/courier/data_source/build_query/build_es_query.js +++ b/src/ui/public/courier/data_source/build_query/build_es_query.js @@ -4,7 +4,7 @@ import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; -export function BuildESQueryProvider(Private) { +export function BuildESQueryProvider(Private, config) { const decorateQuery = Private(DecorateQueryProvider); /** @@ -16,7 +16,7 @@ export function BuildESQueryProvider(Private) { const validQueries = queries.filter((query) => has(query, 'query')); const queriesByLanguage = groupBy(validQueries, 'language'); - const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery); + const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, config); const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, decorateQuery); const filterQuery = buildQueryFromFilters(filters, decorateQuery, indexPattern); diff --git a/src/ui/public/courier/data_source/build_query/from_kuery.js b/src/ui/public/courier/data_source/build_query/from_kuery.js index 574e05486d2e4..3c639f9ce0c8b 100644 --- a/src/ui/public/courier/data_source/build_query/from_kuery.js +++ b/src/ui/public/courier/data_source/build_query/from_kuery.js @@ -1,14 +1,23 @@ import { fromLegacyKueryExpression, fromKueryExpression, toElasticsearchQuery, nodeTypes } from '../../../kuery'; import { documentationLinks } from '../../../documentation_links'; +import { NoLeadingWildcardsError } from '../../../kuery/errors'; const queryDocs = documentationLinks.query; -export function buildQueryFromKuery(indexPattern, queries = []) { +export function buildQueryFromKuery(indexPattern, queries = [], config) { + const allowLeadingWildcards = config.get('query:allowLeadingWildcards'); + + + const queryASTs = queries.map((query) => { try { - return fromKueryExpression(query.query); + return fromKueryExpression(query.query, { allowLeadingWildcards }); } catch (parseError) { + if (parseError instanceof NoLeadingWildcardsError) { + throw parseError; + } + try { fromLegacyKueryExpression(query.query); } diff --git a/src/ui/public/kuery/ast/ast.js b/src/ui/public/kuery/ast/ast.js index df79ae2405fc4..7351e5e33f904 100644 --- a/src/ui/public/kuery/ast/ast.js +++ b/src/ui/public/kuery/ast/ast.js @@ -1,5 +1,6 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types/index'; +import * as errors from '../errors'; import { parse as parseKuery } from './kuery'; import { parse as parseLegacyKuery } from './legacy_kuery'; @@ -27,7 +28,7 @@ function fromExpression(expression, parseOptions = {}, parse = parseKuery) { parseOptions = { ...parseOptions, - helpers: { nodeTypes } + helpers: { nodeTypes, errors } }; return parse(expression, parseOptions); diff --git a/src/ui/public/kuery/ast/kuery.js b/src/ui/public/kuery/ast/kuery.js index 5fc9ecbb69af1..58939dfcbd2b4 100644 --- a/src/ui/public/kuery/ast/kuery.js +++ b/src/ui/public/kuery/ast/kuery.js @@ -152,6 +152,11 @@ module.exports = (function() { }, peg$c19 = function(value) { if (value.type === 'cursor') return value; + + if (!allowLeadingWildcards && value.type === 'wildcard' && nodeTypes.wildcard.hasLeadingWildcard(value)) { + throw new errors.NoLeadingWildcardsError(); + } + const isPhrase = buildLiteralNode(false); return (field) => buildFunctionNode('is', [field, value, isPhrase]); }, @@ -1653,7 +1658,7 @@ module.exports = (function() { } - const { parseCursor, cursorSymbol, helpers: { nodeTypes } } = options; + const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes, errors } } = options; const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; const buildLiteralNode = nodeTypes.literal.buildNode; const buildWildcardNode = nodeTypes.wildcard.buildNode; diff --git a/src/ui/public/kuery/ast/kuery.peg b/src/ui/public/kuery/ast/kuery.peg index 6120ea14eea3d..b9eee9d7476b5 100644 --- a/src/ui/public/kuery/ast/kuery.peg +++ b/src/ui/public/kuery/ast/kuery.peg @@ -1,6 +1,6 @@ // Initialization block { - const { parseCursor, cursorSymbol, helpers: { nodeTypes } } = options; + const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes, errors } } = options; const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; const buildLiteralNode = nodeTypes.literal.buildNode; const buildWildcardNode = nodeTypes.wildcard.buildNode; @@ -156,6 +156,11 @@ Value } / value:UnquotedLiteral { if (value.type === 'cursor') return value; + + if (!allowLeadingWildcards && value.type === 'wildcard' && nodeTypes.wildcard.hasLeadingWildcard(value)) { + throw new errors.NoLeadingWildcardsError(); + } + const isPhrase = buildLiteralNode(false); return (field) => buildFunctionNode('is', [field, value, isPhrase]); } diff --git a/src/ui/public/kuery/errors/index.js b/src/ui/public/kuery/errors/index.js new file mode 100644 index 0000000000000..25480716a3421 --- /dev/null +++ b/src/ui/public/kuery/errors/index.js @@ -0,0 +1,10 @@ +import { KbnError } from '../../errors'; + +export class NoLeadingWildcardsError extends KbnError { + constructor() { + super( + 'Leading wildcards are disabled. See query:allowLeadingWildcards in Advanced Settings.', + NoLeadingWildcardsError + ); + } +} diff --git a/src/ui/public/kuery/node_types/wildcard.js b/src/ui/public/kuery/node_types/wildcard.js index 0d51b4ae644db..98f82a4202964 100644 --- a/src/ui/public/kuery/node_types/wildcard.js +++ b/src/ui/public/kuery/node_types/wildcard.js @@ -33,3 +33,8 @@ export function toQueryStringQuery(node) { const { value } = node; return value.split(wildcardSymbol).map(escapeQueryString).join('*'); } + +export function hasLeadingWildcard(node) { + const { value } = node; + return value.startsWith(wildcardSymbol); +}