aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2016-11-30 14:04:12 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2016-11-30 14:04:12 +0100
commita66fbbffe4a12a21014ba08aa80edd31a0a6e5d9 (patch)
tree19bf53ba720972f6f6df6fdb7abfc10da7055e94
parentf98def2ccae99b34c9131067d8ec4cdb20d52d7d (diff)
downloadsonarqube-a66fbbffe4a12a21014ba08aa80edd31a0a6e5d9.tar.gz
sonarqube-a66fbbffe4a12a21014ba08aa80edd31a0a6e5d9.zip
SONAR-8436 escape param "tags" of WS api/rules/tags
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java16
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java17
4 files changed, 45 insertions, 2 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
index 9c74c0e3a4a..523b9a1f88e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
+import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.elasticsearch.action.search.SearchScrollRequestBuilder;
@@ -43,6 +44,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms;
public class EsUtils {
public static final int SCROLL_TIME_IN_MINUTES = 3;
+ private static final Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
private EsUtils() {
// only static methods
@@ -94,6 +96,15 @@ public class EsUtils {
return new DocScrollIterator<>(esClient, scrollId, docConverter);
}
+ /**
+ * Escapes regexp special characters so that text can be forwarded from end-user input
+ * to Elasticsearch regexp query (for instance attributes "include" and "exclude" of
+ * term aggregations.
+ */
+ public static String escapeSpecialRegexChars(String str) {
+ return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
+ }
+
private static class DocScrollIterator<D extends BaseDoc> implements Iterator<D> {
private final EsClient esClient;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
index 4f2994f0691..0cd7a2f152c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
@@ -69,6 +69,7 @@ import org.sonar.server.es.StickyFacetBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.simpleQueryStringQuery;
import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES;
+import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
import static org.sonar.server.es.EsUtils.scrollIds;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY;
@@ -477,7 +478,7 @@ public class RuleIndex extends BaseIndex {
.size(size)
.minDocCount(1);
if (query != null) {
- termsAggregation.include(".*" + query + ".*");
+ termsAggregation.include(".*" + escapeSpecialRegexChars(query) + ".*");
}
SearchRequestBuilder request = getClient()
.prepareSearch(INDEX)
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
index 25c6f83b403..95d81be899c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java
@@ -33,6 +33,7 @@ import org.sonar.test.TestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
public class EsUtilsTest {
@@ -74,4 +75,19 @@ public class EsUtilsTest {
assertThat(EsUtils.parseDateTime("2017-07-14T04:40:00.000+02:00").getTime()).isEqualTo(1_500_000_000_000L);
assertThat(EsUtils.parseDateTime(null)).isNull();
}
+
+ @Test
+ public void test_escapeSpecialRegexChars() {
+ assertThat(escapeSpecialRegexChars("")).isEqualTo("");
+ assertThat(escapeSpecialRegexChars("foo")).isEqualTo("foo");
+ assertThat(escapeSpecialRegexChars("FOO")).isEqualTo("FOO");
+ assertThat(escapeSpecialRegexChars("foo++")).isEqualTo("foo\\+\\+");
+ assertThat(escapeSpecialRegexChars("foo[]")).isEqualTo("foo\\[\\]");
+ assertThat(escapeSpecialRegexChars(".*")).isEqualTo("\\.\\*");
+ assertThat(escapeSpecialRegexChars("foo\\d")).isEqualTo("foo\\\\d");
+ assertThat(escapeSpecialRegexChars("^")).isEqualTo("\\^");
+ assertThat(escapeSpecialRegexChars("$")).isEqualTo("\\$");
+ assertThat(escapeSpecialRegexChars("|")).isEqualTo("\\|");
+ assertThat(escapeSpecialRegexChars("a bit of | $ .* ^ everything")).isEqualTo("a bit of \\| \\$ \\.\\* \\^ everything");
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java
index e6fc5e610fa..bf0a7d289e0 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java
@@ -77,17 +77,32 @@ public class RuleServiceMediumTest {
@Test
public void listTags_returns_tags_filtered_by_name() {
- insertRule(RuleKey.of("javascript", "S001"), newHashSet("tag1"), newHashSet("sys1", "sys2"));
+ insertRule(RuleKey.of("javascript", "S001"), newHashSet("tag1", "misra++"), newHashSet("sys1", "sys2"));
insertRule(RuleKey.of("java", "S001"), newHashSet("tag2"), newHashSet());
assertThat(service.listTags("missing", 10)).isEmpty();
+ assertThat(service.listTags("", 10)).containsOnly("tag1", "misra++", "tag2", "sys1", "sys2");
assertThat(service.listTags("tag", 10)).containsOnly("tag1", "tag2");
assertThat(service.listTags("sys", 10)).containsOnly("sys1", "sys2");
+ assertThat(service.listTags("misra", 10)).containsOnly("misra++");
+ assertThat(service.listTags("misra+", 10)).containsOnly("misra++");
+ assertThat(service.listTags("++", 10)).containsOnly("misra++");
// LIMITATION: case sensitive
assertThat(service.listTags("TAG", 10)).isEmpty();
assertThat(service.listTags("TAg", 10)).isEmpty();
assertThat(service.listTags("MISSing", 10)).isEmpty();
+
+ assertThat(service.listTags("misra-", 10)).isEmpty();
+ }
+
+ @Test
+ public void listTags_returns_empty_results_if_filter_contains_regexp_special_characters() {
+ insertRule(RuleKey.of("javascript", "S001"), newHashSet("misra++"), newHashSet("sys1", "sys2"));
+
+ assertThat(service.listTags("mis[", 10)).isEmpty();
+ assertThat(service.listTags("mis\\d", 10)).isEmpty();
+ assertThat(service.listTags(".*", 10)).isEmpty();
}
@Test(expected = UnauthorizedException.class)