You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ge...@apache.org on 2020/07/23 12:55:03 UTC
[lucene-solr] branch branch_8x updated: SOLR-13205: Improve
empty-string handling in SolrQueryParserBase
This is an automated email from the ASF dual-hosted git repository.
gerlowskija pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/branch_8x by this push:
new 995a612 SOLR-13205: Improve empty-string handling in SolrQueryParserBase
995a612 is described below
commit 995a6125c246cfa4448eedae3851f9dbd20ef835
Author: Jason Gerlowski <ja...@lucidworks.com>
AuthorDate: Fri Jul 17 19:38:53 2020 -0400
SOLR-13205: Improve empty-string handling in SolrQueryParserBase
Contributed-By: pramodkumar9
---
solr/CHANGES.txt | 3 +
.../apache/solr/parser/SolrQueryParserBase.java | 21 +++-
.../solr/parser/SolrQueryParserBaseTest.java | 137 +++++++++++++++++++++
3 files changed, 156 insertions(+), 5 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 81b9405..6d6f9ec 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -28,6 +28,9 @@ Improvements
* SOLR-11262: Implement writeMap and writeIterator in XMLWriter (yonik, Munendra S N)
+* SOLR-13205: Prevent StringIndexOutOfBoundsException when parsing field names in SolrQueryParserBase
+ (pramodkumar9 via Jason Gerlowski)
+
Optimizations
---------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
index f0d6ed1..9447d12 100644
--- a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
+++ b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import com.google.common.base.Strings;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.reverse.ReverseStringFilter;
import org.apache.lucene.analysis.util.TokenFilterFactory;
@@ -231,6 +232,16 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
public void init(String defaultField, QParser parser) {
+ if ((parser == null) || (parser.getReq() == null) || (parser.getReq().getSchema() == null)) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "query parser is null or invalid");
+ }
+ if ((defaultField != null) && (defaultField.isEmpty())) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "default field name is empty");
+ }
this.schema = parser.getReq().getSchema();
this.parser = parser;
this.flags = parser.getFlags();
@@ -282,7 +293,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
/** Handles the default field if null is passed */
public String getField(String fieldName) {
explicitField = fieldName;
- return fieldName != null ? fieldName : this.defaultField;
+ return !Strings.isNullOrEmpty(fieldName) ? fieldName : this.defaultField;
}
/** For a fielded query, returns the actual field specified (i.e. null if default is being used)
@@ -850,7 +861,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
if (boost == null || boost.image.length()==0 || q == null) {
return q;
}
- if (boost.image.charAt(0) == '=') {
+ if (boost.image.startsWith("=")) {
// syntax looks like foo:x^=3
float val = Float.parseFloat(boost.image.substring(1));
Query newQ = q;
@@ -1001,7 +1012,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
private void checkNullField(String field) throws SolrException {
- if (field == null && defaultField == null) {
+ if (Strings.isNullOrEmpty(field) && Strings.isNullOrEmpty(defaultField)) {
throw new SolrException
(SolrException.ErrorCode.BAD_REQUEST,
"no field name specified in query and no default specified via 'df' param");
@@ -1066,7 +1077,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
} else {
// intercept magic field name of "_" to use as a hook for our
// own functions.
- if (allowSubQueryParsing && field.charAt(0) == '_' && parser != null) {
+ if (allowSubQueryParsing && field.startsWith("_") && parser != null) {
MagicFieldName magic = MagicFieldName.get(field);
if (null != magic) {
subQParser = parser.subQuery(queryText, magic.subParser);
@@ -1114,7 +1125,7 @@ public abstract class SolrQueryParserBase extends QueryBuilder {
} else {
// intercept magic field name of "_" to use as a hook for our
// own functions.
- if (allowSubQueryParsing && field.charAt(0) == '_' && parser != null) {
+ if (allowSubQueryParsing && field.startsWith("_") && parser != null) {
MagicFieldName magic = MagicFieldName.get(field);
if (null != magic) {
subQParser = parser.subQuery(String.join(" ", queryTerms), magic.subParser);
diff --git a/solr/core/src/test/org/apache/solr/parser/SolrQueryParserBaseTest.java b/solr/core/src/test/org/apache/solr/parser/SolrQueryParserBaseTest.java
new file mode 100644
index 0000000..68dd38c
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/parser/SolrQueryParserBaseTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.parser;
+
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.search.QParser;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.solr.SolrTestCaseJ4.assumeWorkingMockito;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SolrQueryParserBaseTest {
+
+ @BeforeClass
+ public static void setUpClass() {
+ assumeWorkingMockito();
+ }
+
+ private static final String DEFAULT_FIELD_NAME = "TestDefaultFieldname";
+
+ private static class MockSolrQueryParser extends SolrQueryParserBase {
+ public void ReInit(CharStream stream) {
+ }
+
+ public Query TopLevelQuery(String field) {
+ return null;
+ }
+ }
+
+ @Mock
+ private QParser qParser;
+ @Mock
+ private QParser subQParser;
+ @Mock
+ private SolrQueryRequest solrQueryRequest;
+ @Mock
+ private Query query;
+ @Mock
+ private IndexSchema indexSchema;
+
+ private MockSolrQueryParser solrQueryParser;
+
+ @Before
+ public void setUp() throws Exception {
+ solrQueryParser = new MockSolrQueryParser();
+ }
+
+ private void initQParser() {
+ doReturn(indexSchema).when(solrQueryRequest).getSchema();
+ doReturn(solrQueryRequest).when(qParser).getReq();
+ }
+
+ @Test
+ public void testInitHappyCases() {
+ initQParser();
+ solrQueryParser.init(null, qParser);
+ solrQueryParser.init(DEFAULT_FIELD_NAME, qParser);
+ }
+
+ @Test(expected = SolrException.class)
+ public void testInitBadDefaultField() {
+ solrQueryParser.init("", qParser);
+ }
+
+ @Test(expected = SolrException.class)
+ public void testInitNullQParser() {
+ solrQueryParser.init(DEFAULT_FIELD_NAME, null);
+ }
+
+ @Test(expected = SolrException.class)
+ public void testInitNullQParserReq() {
+ solrQueryParser.init(DEFAULT_FIELD_NAME, qParser);
+ }
+
+ @Test(expected = SolrException.class)
+ public void testInitNullQParserReqSchema() {
+ doReturn(solrQueryRequest).when(qParser).getReq();
+ solrQueryParser.init(DEFAULT_FIELD_NAME, qParser);
+ }
+
+ @Test
+ public void testGetField() {
+ initQParser();
+ solrQueryParser.init(DEFAULT_FIELD_NAME, qParser);
+ assertEquals(DEFAULT_FIELD_NAME, solrQueryParser.getField(null));
+ assertEquals(DEFAULT_FIELD_NAME, solrQueryParser.getField(""));
+ final String nonNullFieldName = "testFieldName";
+ assertEquals(nonNullFieldName, solrQueryParser.getField(nonNullFieldName));
+ }
+
+ @Test
+ public void testGetMagicFieldQuery() throws Exception {
+ String magicField = "_val_";
+ String magicFieldSubParser = SolrQueryParserBase.MagicFieldName.get(magicField).subParser;
+ initQParser();
+ solrQueryParser.init(DEFAULT_FIELD_NAME, qParser);
+ solrQueryParser.setAllowSubQueryParsing(true);
+ doReturn(query).when(subQParser).getQuery();
+ doReturn(subQParser).when(qParser).subQuery(anyString(), eq(magicFieldSubParser));
+
+ String queryText = "queryText";
+ List<String> queryTerms = Arrays.asList("query", "terms");
+ boolean quoted = true; //value doesn't matter for this test
+ boolean raw = true; //value doesn't matter for this test
+ assertEquals(query, solrQueryParser.getFieldQuery(magicField, queryText, quoted, raw));
+ assertEquals(query, solrQueryParser.getFieldQuery(magicField, queryTerms, raw));
+ }
+}