aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorssjenka <ssjenka@ops-slave-centos7-1.internal.sonarsource.com>2016-11-30 13:52:52 +0100
committerssjenka <ssjenka@ops-slave-centos7-1.internal.sonarsource.com>2016-11-30 13:52:52 +0100
commit3c646719ac84403fab2ed7060db25dedcf29eb33 (patch)
tree641b0727f2a99d636c99b41bfe92533f7da856bf /server
parent1511b9d96b1690428bd794d13209183e56e26f15 (diff)
parent5eedd7e06021b78f49522f3d296bc9cd3939c415 (diff)
downloadsonarqube-3c646719ac84403fab2ed7060db25dedcf29eb33.tar.gz
sonarqube-3c646719ac84403fab2ed7060db25dedcf29eb33.zip
Automatic merge from branch-6.2
* origin/branch-6.2: SONAR-8437 fix listing of issue tags and authors SONAR-8437 fix facet "assigned_to_me" of api/issues/search SONAR-8437 escape selected value in ES facet of WS api/issues SONAR-8436 escape param "tags" of WS api/rules/tags Slight performance improvement in api/rules/tags Fix Quality flaws related to org.sonar.server.rule
Diffstat (limited to 'server')
-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/es/StickyFacetBuilder.java14
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java31
-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/issue/IssueServiceMediumTest.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java90
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java11
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assignedToMe_facet_must_escape_login_of_authenticated_user.json23
11 files changed, 191 insertions, 55 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 25770e0d97e..85f65400722 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.bulk.BulkRequestBuilder;
@@ -47,6 +48,7 @@ import static java.lang.String.format;
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
@@ -107,6 +109,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/es/StickyFacetBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/es/StickyFacetBuilder.java
index 4c0070d08ac..11519a92055 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/StickyFacetBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/StickyFacetBuilder.java
@@ -19,8 +19,11 @@
*/
package org.sonar.server.es;
-import com.google.common.base.Joiner;
+import java.util.Arrays;
import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.lang.ArrayUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
@@ -40,7 +43,7 @@ public class StickyFacetBuilder {
private static final int FACET_DEFAULT_MIN_DOC_COUNT = 1;
private static final int FACET_DEFAULT_SIZE = 10;
private static final Order FACET_DEFAULT_ORDER = Terms.Order.count(false);
- private static final Joiner PIPE_JOINER = Joiner.on('|');
+ private static final Collector<CharSequence, ?, String> PIPE_JOINER = Collectors.joining("|");
private final QueryBuilder query;
private final Map<String, QueryBuilder> filters;
@@ -107,9 +110,14 @@ public class StickyFacetBuilder {
public FilterAggregationBuilder addSelectedItemsToFacet(String fieldName, String facetName, FilterAggregationBuilder facetTopAggregation, Object... selected) {
if (selected.length > 0) {
+ String includes = Arrays.stream(selected)
+ .filter(Objects::nonNull)
+ .map(s -> EsUtils.escapeSpecialRegexChars(s.toString()))
+ .collect(PIPE_JOINER);
+
TermsBuilder selectedTerms = AggregationBuilders.terms(facetName + "_selected")
.field(fieldName)
- .include(PIPE_JOINER.join(selected));
+ .include(includes);
if (subAggregation != null) {
selectedTerms = selectedTerms.subAggregation(subAggregation);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index c914d9ad63a..318461fa5e1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -82,11 +82,13 @@ import org.sonar.server.user.UserSession;
import org.sonar.server.view.index.ViewIndexDefinition;
import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
+import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.AUTHORS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.CREATED_AT;
@@ -209,7 +211,7 @@ public class IssueIndex extends BaseIndex {
public IssueDoc getByKey(String key) {
IssueDoc value = getNullableByKey(key);
if (value == null) {
- throw new NotFoundException(String.format("Issue with key '%s' does not exist", key));
+ throw new NotFoundException(format("Issue with key '%s' does not exist", key));
}
return value;
}
@@ -567,7 +569,9 @@ public class IssueIndex extends BaseIndex {
FilterAggregationBuilder facetTopAggregation = AggregationBuilders
.filter(facetName + "__filter")
.filter(facetFilter)
- .subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders.terms(facetName + "__terms").field(fieldName).include(login)));
+ .subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders.terms(facetName + "__terms")
+ .field(fieldName)
+ .include(escapeSpecialRegexChars(login))));
builder.addAggregation(
AggregationBuilders.global(facetName)
@@ -619,16 +623,15 @@ public class IssueIndex extends BaseIndex {
.size(maxNumberOfTags)
.order(Terms.Order.term(true))
.minDocCount(1L);
- if (textQuery != null) {
- issueTags.include(String.format(SUBSTRING_MATCH_REGEXP, textQuery));
- }
TermsBuilder ruleTags = AggregationBuilders.terms(tagsOnRulesSubAggregation)
.field(RuleIndexDefinition.FIELD_RULE_ALL_TAGS)
.size(maxNumberOfTags)
.order(Terms.Order.term(true))
.minDocCount(1L);
if (textQuery != null) {
- ruleTags.include(String.format(SUBSTRING_MATCH_REGEXP, textQuery));
+ String escapedTextQuery = escapeSpecialRegexChars(textQuery);
+ issueTags.include(format(SUBSTRING_MATCH_REGEXP, escapedTextQuery));
+ ruleTags.include(format(SUBSTRING_MATCH_REGEXP, escapedTextQuery));
}
SearchResponse searchResponse = requestBuilder.addAggregation(topAggreg.subAggregation(issueTags).subAggregation(ruleTags)).get();
@@ -667,7 +670,7 @@ public class IssueIndex extends BaseIndex {
.order(termsOrder)
.minDocCount(1L);
if (textQuery != null) {
- aggreg.include(String.format(SUBSTRING_MATCH_REGEXP, textQuery));
+ aggreg.include(format(SUBSTRING_MATCH_REGEXP, escapeSpecialRegexChars(textQuery)));
}
SearchResponse searchResponse = requestBuilder.addAggregation(aggreg).get();
@@ -701,7 +704,7 @@ public class IssueIndex extends BaseIndex {
filter.must(termsQuery(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, component.uuid()));
break;
default:
- throw new IllegalStateException(String.format("Component of scope '%s' is not allowed", component.scope()));
+ throw new IllegalStateException(format("Component of scope '%s' is not allowed", component.scope()));
}
SearchRequestBuilder requestBuilder = getClient()
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java
index 581045bf5a1..48e70374318 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java
@@ -77,7 +77,8 @@ public class RuleService {
}
private void checkPermission() {
- userSession.checkLoggedIn();
- userSession.checkPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+ userSession
+ .checkLoggedIn()
+ .checkPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
}
}
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 d7fe053024f..e42af16478c 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
@@ -64,9 +64,11 @@ import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.StickyFacetBuilder;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
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;
@@ -168,7 +170,7 @@ public class RuleIndex extends BaseIndex {
// No contextual query case
String queryText = query.getQueryText();
if (StringUtils.isEmpty(queryText)) {
- return QueryBuilders.matchAllQuery();
+ return matchAllQuery();
}
// Build RuleBased contextual query
@@ -179,8 +181,7 @@ public class RuleIndex extends BaseIndex {
qb.should(simpleQueryStringQuery(query.getQueryText())
.field(FIELD_RULE_NAME + "." + SEARCH_WORDS_SUFFIX, 20f)
.field(FIELD_RULE_HTML_DESCRIPTION, 3f)
- .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
- ).boost(20f);
+ .defaultOperator(SimpleQueryStringBuilder.Operator.AND)).boost(20f);
// Match and partial Match queries
// Search by key uses the "sortable" sub-field as it requires to be case-insensitive (lower-case filtering)
@@ -295,7 +296,7 @@ public class RuleIndex extends BaseIndex {
if (childrenFilter.hasClauses()) {
childQuery = childrenFilter;
} else {
- childQuery = QueryBuilders.matchAllQuery();
+ childQuery = matchAllQuery();
}
/** Implementation of activation query */
@@ -469,31 +470,29 @@ public class RuleIndex extends BaseIndex {
}
public Set<String> terms(String fields, @Nullable String query, int size) {
- Set<String> tags = new HashSet<>();
- String key = "_ref";
+ String aggregationKey = "_ref";
- TermsBuilder terms = AggregationBuilders.terms(key)
+ TermsBuilder termsAggregation = AggregationBuilders.terms(aggregationKey)
.field(fields)
.size(size)
.minDocCount(1);
if (query != null) {
- terms.include(".*" + query + ".*");
+ termsAggregation.include(".*" + escapeSpecialRegexChars(query) + ".*");
}
SearchRequestBuilder request = getClient()
.prepareSearch(INDEX)
- .setQuery(QueryBuilders.matchAllQuery())
- .addAggregation(terms);
+ .setQuery(matchAllQuery())
+ .setSize(0)
+ .addAggregation(termsAggregation);
SearchResponse esResponse = request.get();
- Terms aggregation = esResponse.getAggregations().get(key);
-
+ Set<String> terms = new HashSet<>();
+ Terms aggregation = esResponse.getAggregations().get(aggregationKey);
if (aggregation != null) {
- for (Terms.Bucket value : aggregation.getBuckets()) {
- tags.add(value.getKeyAsString());
- }
+ aggregation.getBuckets().forEach(value -> terms.add(value.getKeyAsString()));
}
- return tags;
+ return terms;
}
private enum ToRuleKey implements Function<String, RuleKey> {
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/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
index f0b605833d7..ff940104b98 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
@@ -245,6 +245,7 @@ public class IssueServiceMediumTest {
assertThat(service.listTags("sys", 5)).containsOnly("systag1", "systag2");
assertThat(service.listTags(null, 1)).containsOnly("bug");
assertThat(service.listTags(null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug", "systag1", "systag2", "tag1", "tag2");
+ assertThat(service.listTags("invalidRegexp[", 5)).isEmpty();
}
@Test
@@ -303,7 +304,7 @@ public class IssueServiceMediumTest {
}
@Test
- public void list_authors() {
+ public void test_listAuthors() {
RuleDto rule = newRule();
ComponentDto project = newProject();
ComponentDto file = newFile(project);
@@ -319,6 +320,16 @@ public class IssueServiceMediumTest {
assertThat(service.listAuthors(null, Integer.MAX_VALUE)).containsExactly("anakin@skywalker.name", "luke.skywalker", "luke@skywalker.name");
}
+ @Test
+ public void listAuthors_escapes_regexp_special_characters() {
+ saveIssue(IssueTesting.newDto(newRule(), newFile(newProject()), newProject()).setAuthorLogin("name++"));
+
+ assertThat(service.listAuthors("invalidRegexp[", 5)).isEmpty();
+ assertThat(service.listAuthors("nam+", 5)).isEmpty();
+ assertThat(service.listAuthors("name+", 5)).containsExactly("name++");
+ assertThat(service.listAuthors(".*", 5)).isEmpty();
+ }
+
private RuleDto newRule() {
return newRule(RuleTesting.newXooX1());
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
index a852f54abbb..8cf65711c2f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
@@ -387,6 +387,19 @@ public class SearchActionMediumTest {
}
@Test
+ public void assignedToMe_facet_must_escape_login_of_authenticated_user() throws Exception {
+ // login looks like an invalid regexp
+ userSessionRule.login("foo[");
+
+ // should not fail
+ wsTester.newGetRequest(API_ENDPOINT, SEARCH_ACTION)
+ .setParam(WebService.Param.FACETS, "assigned_to_me")
+ .execute()
+ .assertJson(this.getClass(), "assignedToMe_facet_must_escape_login_of_authenticated_user.json");
+
+ }
+
+ @Test
public void filter_by_assigned_to_me() throws Exception {
db.userDao().insert(session, new UserDto().setLogin("john").setName("John").setEmail("john@email.com"));
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 79f86211988..80440da8f90 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
@@ -19,26 +19,26 @@
*/
package org.sonar.server.rule;
-import com.google.common.collect.Sets;
-import java.util.Collections;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.rule.RuleDao;
import org.sonar.db.rule.RuleTesting;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.rule.index.RuleIndexDefinition;
import org.sonar.server.rule.index.RuleIndexer;
import org.sonar.server.tester.ServerTester;
import org.sonar.server.tester.UserSessionRule;
+import static com.google.common.collect.Sets.newHashSet;
import static org.assertj.core.api.Assertions.assertThat;
public class RuleServiceMediumTest {
@@ -46,14 +46,16 @@ public class RuleServiceMediumTest {
@ClassRule
public static ServerTester tester = new ServerTester().withEsIndexes();
- @org.junit.Rule
+ @Rule
public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester);
- RuleDao dao = tester.get(RuleDao.class);
- RuleIndex index = tester.get(RuleIndex.class);
- RuleService service = tester.get(RuleService.class);
- DbSession dbSession;
- RuleIndexer ruleIndexer;
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private RuleDao dao = tester.get(RuleDao.class);
+ private RuleService service = tester.get(RuleService.class);
+ private DbSession dbSession;
+ private RuleIndexer ruleIndexer;
@Before
public void before() {
@@ -68,30 +70,68 @@ public class RuleServiceMediumTest {
}
@Test
- public void list_tags() {
+ public void listTags_returns_all_tags() {
// insert db
- RuleKey key1 = RuleKey.of("javascript", "S001");
- RuleKey key2 = RuleKey.of("java", "S001");
- dao.insert(dbSession,
- RuleTesting.newDto(key1).setTags(Sets.newHashSet("tag1")).setSystemTags(Sets.newHashSet("sys1", "sys2")));
- dao.insert(dbSession,
- RuleTesting.newDto(key2).setTags(Sets.newHashSet("tag2")).setSystemTags(Collections.<String>emptySet()));
- dbSession.commit();
- ruleIndexer.index();
+ insertRule(RuleKey.of("javascript", "S001"), newHashSet("tag1"), newHashSet("sys1", "sys2"));
+ insertRule(RuleKey.of("java", "S001"), newHashSet("tag2"), newHashSet());
// all tags, including system
Set<String> tags = service.listTags();
assertThat(tags).containsOnly("tag1", "tag2", "sys1", "sys2");
+ }
- // verify in es
- tags = index.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS);
- assertThat(tags).containsOnly("tag1", "tag2", "sys1", "sys2");
+ @Test
+ public void listTags_returns_tags_filtered_by_name() {
+ 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(expected = UnauthorizedException.class)
- public void do_not_delete_if_not_granted() {
- userSessionRule.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+ @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
+ public void delete_throws_UnauthorizedException_if_not_logged_in() {
+ expectedException.expect(UnauthorizedException.class);
+ expectedException.expectMessage("Authentication is required");
service.delete(RuleKey.of("java", "S001"));
}
+
+ @Test
+ public void delete_throws_ForbiddenException_if_not_administrator() {
+ userSessionRule.login().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ service.delete(RuleKey.of("java", "S001"));
+ }
+
+ private void insertRule(RuleKey key, Set<String> tags, Set<String> systemTags) {
+ dao.insert(dbSession,
+ RuleTesting.newDto(key).setTags(tags).setSystemTags(systemTags));
+ dbSession.commit();
+ ruleIndexer.index();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
index 4d44e20c9e8..dd4f79d4918 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
@@ -253,6 +253,17 @@ public class RuleIndexTest {
}
@Test
+ public void tags_facet_supports_selected_value_with_regexp_special_characters() {
+ indexRules(newDoc(RuleKey.of("java", "S001")).setAllTags(singleton("misra++")));
+
+ RuleQuery query = new RuleQuery().setTags(singletonList("misra["));
+ SearchOptions options = new SearchOptions().addFacets(RuleIndex.FACET_TAGS);
+
+ // do not fail
+ assertThat(index.search(query, options).getTotal()).isEqualTo(0);
+ }
+
+ @Test
public void search_by_types() {
indexRules(
newDoc(RULE_KEY_1).setType(CODE_SMELL),
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assignedToMe_facet_must_escape_login_of_authenticated_user.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assignedToMe_facet_must_escape_login_of_authenticated_user.json
new file mode 100644
index 00000000000..45df34e8f53
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/assignedToMe_facet_must_escape_login_of_authenticated_user.json
@@ -0,0 +1,23 @@
+{
+ "total": 0,
+ "p": 1,
+ "ps": 100,
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 100,
+ "total": 0
+ },
+ "issues": [],
+ "components": [],
+ "facets": [
+ {
+ "property": "assigned_to_me",
+ "values": [
+ {
+ "val": "foo[",
+ "count": 0
+ }
+ ]
+ }
+ ]
+}