From: alain <108417558+alain-kermis-sonarsource@users.noreply.github.com> Date: Fri, 12 Aug 2022 13:31:54 +0000 (+0200) Subject: SONAR-17150 Fix SSF-39 X-Git-Tag: 9.7.0.61563~384 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=11e154e4beca7ddc55bfc6f8af068756f6316707;p=sonarqube.git SONAR-17150 Fix SSF-39 --- diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/FilterParser.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/FilterParser.java index a8a3b495145..ab83c401bbe 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/FilterParser.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/FilterParser.java @@ -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 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 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 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 diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/FilterParserTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/FilterParserTest.java index 1751f2621d0..6afca881faf 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/FilterParserTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ws/FilterParserTest.java @@ -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 = FilterParser.parse("languages IN (java, python, )"); + + assertThat(criterion) + .extracting(Criterion::getKey, Criterion::getOperator, Criterion::getValues, Criterion::getValue) + .containsOnly( + tuple("languages", IN, asList("java", "python", ""), null)); + } + + @Test + public void parse_filter_having_value_containing_non_alphanumeric_characters() { + List 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 = 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 = FilterParser.parse("isFavorite");