]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17150 Fix SSF-39
authoralain <108417558+alain-kermis-sonarsource@users.noreply.github.com>
Fri, 12 Aug 2022 13:31:54 +0000 (15:31 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 12 Aug 2022 20:02:57 +0000 (20:02 +0000)
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/FilterParser.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/FilterParserTest.java

index a8a3b4951458c81a07f1c655b5375545dfce3257..ab83c401bbe180080bdd61da6ecb81d3036d102a 100644 (file)
@@ -22,8 +22,10 @@ package org.sonar.server.component.ws;
 import com.google.common.base.Splitter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -36,12 +38,11 @@ import static java.util.Objects.requireNonNull;
 public class FilterParser {
 
   private static final String DOUBLE_QUOTES = "\"";
-
   private static final Splitter CRITERIA_SPLITTER = Splitter.on(Pattern.compile(" and ", Pattern.CASE_INSENSITIVE)).trimResults().omitEmptyStrings();
   private static final Splitter IN_VALUES_SPLITTER = Splitter.on(",").trimResults().omitEmptyStrings();
-
-  private static final Pattern PATTERN = Pattern.compile("(\\w+)\\s*([<>]?[=]?)\\s*(.*)", Pattern.CASE_INSENSITIVE);
-  private static final Pattern PATTERN_HAVING_VALUES = Pattern.compile("(\\w+)\\s+(in)\\s+\\((.*)\\)", Pattern.CASE_INSENSITIVE);
+  private static final Pattern PATTERN_WITH_COMPARISON_OPERATOR = Pattern.compile("(\\w+)\\s*+(<=?|>=?|=)\\s*+([^<>=]*+)", Pattern.CASE_INSENSITIVE);
+  private static final Pattern PATTERN_WITHOUT_OPERATOR = Pattern.compile("(\\w+)\\s*+", Pattern.CASE_INSENSITIVE);
+  private static final Pattern PATTERN_WITH_IN_OPERATOR = Pattern.compile("(\\w+)\\s+(in)\\s+\\(([^()]*)\\)", Pattern.CASE_INSENSITIVE);
 
   private FilterParser() {
     // Only static methods
@@ -55,25 +56,34 @@ public class FilterParser {
 
   private static Criterion parseCriterion(String rawCriterion) {
     try {
-      Criterion criterion = tryParsingCriterionHavingValues(rawCriterion);
-      if (criterion != null) {
-        return criterion;
-      }
-      criterion = tryParsingCriterionNotHavingValues(rawCriterion);
-      if (criterion != null) {
-        return criterion;
-      }
-      throw new IllegalArgumentException("Criterion is invalid");
+      return Stream.of(
+          tryParsingCriterionWithoutOperator(rawCriterion),
+          tryParsingCriterionWithInOperator(rawCriterion),
+          tryParsingCriterionWithComparisonOperator(rawCriterion)
+        )
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .findFirst()
+        .orElseThrow(() -> new IllegalArgumentException("Criterion is invalid"));
     } catch (Exception e) {
       throw new IllegalArgumentException(String.format("Cannot parse '%s' : %s", rawCriterion, e.getMessage()), e);
     }
   }
 
-  @CheckForNull
-  private static Criterion tryParsingCriterionNotHavingValues(String criterion) {
-    Matcher matcher = PATTERN.matcher(criterion);
-    if (!matcher.find()) {
-      return null;
+  private static Optional<Criterion> tryParsingCriterionWithoutOperator(String criterion) {
+    Matcher matcher = PATTERN_WITHOUT_OPERATOR.matcher(criterion);
+    if (!matcher.matches()) {
+      return Optional.empty();
+    }
+    Criterion.Builder builder = new Criterion.Builder();
+    builder.setKey(matcher.group(1));
+    return Optional.of(builder.build());
+  }
+
+  private static Optional<Criterion> tryParsingCriterionWithComparisonOperator(String criterion) {
+    Matcher matcher = PATTERN_WITH_COMPARISON_OPERATOR.matcher(criterion);
+    if (!matcher.matches()) {
+      return Optional.empty();
     }
     Criterion.Builder builder = new Criterion.Builder();
     builder.setKey(matcher.group(1));
@@ -83,20 +93,19 @@ public class FilterParser {
       builder.setOperator(ProjectMeasuresQuery.Operator.getByValue(operatorValue));
       builder.setValue(sanitizeValue(value));
     }
-    return builder.build();
+    return Optional.of(builder.build());
   }
 
-  @CheckForNull
-  private static Criterion tryParsingCriterionHavingValues(String criterion) {
-    Matcher matcher = PATTERN_HAVING_VALUES.matcher(criterion);
-    if (!matcher.find()) {
-      return null;
+  private static Optional<Criterion> tryParsingCriterionWithInOperator(String criterion) {
+    Matcher matcher = PATTERN_WITH_IN_OPERATOR.matcher(criterion);
+    if (!matcher.matches()) {
+      return Optional.empty();
     }
     Criterion.Builder builder = new Criterion.Builder();
     builder.setKey(matcher.group(1));
     builder.setOperator(ProjectMeasuresQuery.Operator.IN);
     builder.setValues(IN_VALUES_SPLITTER.splitToList(matcher.group(3)));
-    return builder.build();
+    return Optional.of(builder.build());
   }
 
   @CheckForNull
index 1751f2621d0ec2bd2c3f09385f630f150b8dda7d..6afca881fafe4a1e3298786838d8225809860c23 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.component.ws;
 
+import java.util.Collections;
 import java.util.List;
 import org.junit.Test;
 import org.sonar.server.component.ws.FilterParser.Criterion;
@@ -77,6 +78,36 @@ public class FilterParserTest {
         tuple("ncloc", IN, asList("80", "90"), null));
   }
 
+  @Test
+  public void parse_filter_having_value_containing_operator_characters() {
+    List<Criterion> criterion = FilterParser.parse("languages IN (java, python, <null>)");
+
+    assertThat(criterion)
+      .extracting(Criterion::getKey, Criterion::getOperator, Criterion::getValues, Criterion::getValue)
+      .containsOnly(
+        tuple("languages", IN, asList("java", "python", "<null>"), null));
+  }
+
+  @Test
+  public void parse_filter_having_value_containing_non_alphanumeric_characters() {
+    List<Criterion> criterion = FilterParser.parse("q = \"+*ç%&/()\"");
+
+    assertThat(criterion)
+      .extracting(Criterion::getKey, Criterion::getOperator, Criterion::getValue)
+      .containsOnly(
+        tuple("q", EQ, "+*ç%&/()"));
+  }
+
+  @Test
+  public void parse_filter_having_in_empty_list() {
+    List<Criterion> criterion = FilterParser.parse("languages IN ()");
+
+    assertThat(criterion)
+      .extracting(Criterion::getKey, Criterion::getOperator, Criterion::getValues, Criterion::getValue)
+      .containsOnly(
+        tuple("languages", IN, Collections.emptyList(), null));
+  }
+
   @Test
   public void parse_filter_having_only_key() {
     List<Criterion> criterion = FilterParser.parse("isFavorite");