You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2020/09/22 20:13:03 UTC

[lucene-solr] branch branch_8x updated: SOLR-13181: param macro expansion could throw (#1877)

This is an automated email from the ASF dual-hosted git repository.

dsmiley 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 05b7a4a  SOLR-13181: param macro expansion could throw (#1877)
05b7a4a is described below

commit 05b7a4a40d33403210a278c14ee951cfc2b7103f
Author: David Smiley <ds...@apache.org>
AuthorDate: Tue Sep 22 15:46:59 2020 -0400

    SOLR-13181: param macro expansion could throw (#1877)
    
    ...StringIndexOutOfBoundsException on bad syntax
    * failOnMissingParams: should have been returning null (failing) on bad syntax cases
    
    Co-authored-by: Christine Poerschke <cp...@apache.org>
    (cherry picked from commit 2197776be67384d628f12a5def8663db9e4220cf)
---
 solr/CHANGES.txt                                   |  4 ++
 .../apache/solr/request/macro/MacroExpander.java   | 35 ++++++++------
 .../solr/request/macro/TestMacroExpander.java      | 56 +++++++++++++++-------
 3 files changed, 63 insertions(+), 32 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index e988c47..8ed7a5a 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -109,6 +109,10 @@ Bug Fixes
 * SOLR-14821: {!terms} docValuesTermsFilterTopLevel method now works with single-valued strings (Anatolii Siuniaev
   via Jason Gerlowski)
 
+* SOLR-13181: macro expansion of parameters could result in a StringIndexOutOfBoundsException.
+  Also, params with macros will be processed more strictly by LTR FeatureWeight.
+  (David Smiley, Cesar Rodriguez, Christine Poerschke)
+
 Other Changes
 ---------------------
 
diff --git a/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java b/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java
index 67219e5..144ce06 100644
--- a/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java
+++ b/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java
@@ -119,8 +119,8 @@ public class MacroExpander {
     int start = 0;  // start of the unprocessed part of the string
     StringBuilder sb = null;
     for (;;) {
+      assert idx >= start;
       idx = val.indexOf(macroStart, idx);
-      int matchedStart = idx;
 
       // check if escaped
       if (idx > 0) {
@@ -135,18 +135,19 @@ public class MacroExpander {
         }
       }
       else if (idx < 0) {
-        if (sb == null) return val;
-        sb.append(val.substring(start));
-        return sb.toString();
+        break;
       }
 
       // found unescaped "${"
-      idx += macroStart.length();
+      final int matchedStart = idx;
 
-      int rbrace = val.indexOf('}', idx);
+      int rbrace = val.indexOf('}', matchedStart + macroStart.length());
       if (rbrace == -1) {
         // no matching close brace...
-        continue;
+        if (failOnMissingParams) {
+          return null;
+        }
+        break;
       }
 
       if (sb == null) {
@@ -154,14 +155,14 @@ public class MacroExpander {
       }
 
       if (matchedStart > 0) {
-        sb.append(val.substring(start, matchedStart));
+        sb.append(val, start, matchedStart);
       }
 
       // update "start" to be at the end of ${...}
-      start = rbrace + 1;
+      idx = start = rbrace + 1;
 
-      // String inbetween = val.substring(idx, rbrace);
-      StrParser parser = new StrParser(val, idx, rbrace);
+      // String in-between braces
+      StrParser parser = new StrParser(val, matchedStart + macroStart.length(), rbrace);
       try {
         String paramName = parser.getId();
         String defVal = null;
@@ -187,13 +188,19 @@ public class MacroExpander {
         }
 
       } catch (SyntaxError syntaxError) {
+        if (failOnMissingParams) {
+          return null;
+        }
         // append the part we would have skipped
-        sb.append( val.substring(matchedStart, start) );
-        continue;
+        sb.append(val, matchedStart, start);
       }
+    } // loop idx
 
+    if (sb == null) {
+      return val;
     }
-
+    sb.append(val, start, val.length());
+    return sb.toString();
   }
 
 
diff --git a/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java b/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java
index c6c60cb..99717e2 100644
--- a/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java
+++ b/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java
@@ -16,14 +16,14 @@
  */
 package org.apache.solr.request.macro;
 
-import java.util.Map;
+import java.util.Collections;
 import java.util.HashMap;
-
+import java.util.Map;
 
 import org.apache.solr.SolrTestCase;
 import org.junit.Test;
 
-/*
+/**
  * Tests for the MacroExpander
  */
 public class TestMacroExpander extends SolrTestCase {
@@ -121,14 +121,13 @@ public class TestMacroExpander extends SolrTestCase {
     request.put("expr", new String[] {"${one_ref}"}); // expr is for streaming expressions, no replacement by default
     request.put("one_ref",new String[] {"one"});
     request.put("three_ref",new String[] {"three"});
-    @SuppressWarnings({"rawtypes"})
-    Map expanded = MacroExpander.expand(request);
-    assertEquals("zero", ((String[])expanded.get("fq"))[0]);
-    assertEquals("one", ((String[])expanded.get("fq"))[1]);
-    assertEquals("two", ((String[]) expanded.get("fq"))[2]);
-    assertEquals("three", ((String[]) expanded.get("fq"))[3]);
-
-    assertEquals("${one_ref}", ((String[])expanded.get("expr"))[0]);
+    Map<String, String[]> expanded = MacroExpander.expand(request);
+    assertEquals("zero", expanded.get("fq")[0]);
+    assertEquals("one", expanded.get("fq")[1]);
+    assertEquals("two", expanded.get("fq")[2]);
+    assertEquals("three", expanded.get("fq")[3]);
+
+    assertEquals("${one_ref}", expanded.get("expr")[0]);
   }
 
   @Test
@@ -143,15 +142,36 @@ public class TestMacroExpander extends SolrTestCase {
     String oldVal = System.getProperty("StreamingExpressionMacros","false");
     System.setProperty("StreamingExpressionMacros", "true");
     try {
-      @SuppressWarnings({"rawtypes"})
-      Map expanded = MacroExpander.expand(request);
-      assertEquals("zero", ((String[])expanded.get("fq"))[0]);
-      assertEquals("one", ((String[])expanded.get("fq"))[1]);
-      assertEquals("two", ((String[]) expanded.get("fq"))[2]);
-      assertEquals("three", ((String[]) expanded.get("fq"))[3]);
-      assertEquals("one", ((String[])expanded.get("expr"))[0]);
+      Map<String, String[]> expanded = MacroExpander.expand(request);
+      assertEquals("zero", expanded.get("fq")[0]);
+      assertEquals("one", expanded.get("fq")[1]);
+      assertEquals("two", expanded.get("fq")[2]);
+      assertEquals("three", expanded.get("fq")[3]);
+      assertEquals("one", expanded.get("expr")[0]);
     } finally {
       System.setProperty("StreamingExpressionMacros", oldVal);
     }
   }
+
+  @Test
+  public void testUnbalanced() { // SOLR-13181
+    final Map<String, String[]> request = Collections.singletonMap("answer", new String[]{ "42" });
+    final MacroExpander meSkipOnMissingParams = new MacroExpander(request);
+    final MacroExpander meFailOnMissingParams = new MacroExpander(request, true);
+    assertEquals("${noClose", meSkipOnMissingParams.expand("${noClose"));
+    assertEquals("42 ${noClose", meSkipOnMissingParams.expand("${answer} ${noClose"));
+    assertEquals("42 ${noClose fooBar", meSkipOnMissingParams.expand("${answer} ${noClose fooBar"));
+    assertNull(meFailOnMissingParams.expand("${noClose"));
+    assertNull(meFailOnMissingParams.expand("${answer} ${noClose"));
+    assertNull(meFailOnMissingParams.expand("${answer} ${noClose fooBar"));
+
+    assertEquals("${${b}}", meSkipOnMissingParams.expand("${${b}}"));
+    assertNull(meFailOnMissingParams.expand("${${b}}"));
+
+    // Does not register as a syntax failure, although may subjectively look like it.
+    //   Consequently, the expression is replaced with nothing in default mode.
+    //   It'd be nice if there was a mode to leave un-resolved macros as-is when they don't resolve.
+    assertEquals("preamble ", meSkipOnMissingParams.expand("preamble ${exp${bad}"));
+    assertNull(meFailOnMissingParams.expand("preamble ${exp${bad}"));
+  }
 }