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;
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
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;
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;
.size(size)
.minDocCount(1);
if (query != null) {
- termsAggregation.include(".*" + query + ".*");
+ termsAggregation.include(".*" + escapeSpecialRegexChars(query) + ".*");
}
SearchRequestBuilder request = getClient()
.prepareSearch(INDEX)
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 {
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");
+ }
}
@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)