Browse Source

SONAR-11886 Ignore Hotspots in Issues facets / filters

* SONAR-11886 Ignore Hotspots in Issues facets / filters
* SONAR-11886 Do not return severity on Security Hotspots
tags/7.8
Julien Lancelot 5 years ago
parent
commit
ff6994bef0

+ 44
- 18
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java View File

@@ -53,11 +53,11 @@ import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuil
import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds;
import org.elasticsearch.search.aggregations.bucket.terms.IncludeExclude;
import org.elasticsearch.search.aggregations.bucket.terms.InternalTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.IncludeExclude;
import org.elasticsearch.search.aggregations.metrics.max.InternalMax;
import org.elasticsearch.search.aggregations.metrics.min.Min;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
@@ -95,6 +95,8 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.server.es.BaseDoc.epochMillisToEpochSeconds;
import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
@@ -356,20 +358,29 @@ public class IssueIndex {
filters.put(FIELD_ISSUE_RULE_ID, createTermsFilter(
FIELD_ISSUE_RULE_ID,
query.rules().stream().map(RuleDefinitionDto::getId).collect(Collectors.toList())));
filters.put(FIELD_ISSUE_SEVERITY, createTermsFilter(FIELD_ISSUE_SEVERITY, query.severities()));
filters.put(FIELD_ISSUE_STATUS, createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
filters.put(FIELD_ISSUE_ORGANIZATION_UUID, createTermFilter(FIELD_ISSUE_ORGANIZATION_UUID, query.organizationUuid()));
filters.put(FIELD_ISSUE_OWASP_TOP_10, createTermsFilter(FIELD_ISSUE_OWASP_TOP_10, query.owaspTop10()));
filters.put(FIELD_ISSUE_SANS_TOP_25, createTermsFilter(FIELD_ISSUE_SANS_TOP_25, query.sansTop25()));
filters.put(FIELD_ISSUE_CWE, createTermsFilter(FIELD_ISSUE_CWE, query.cwe()));
addSeverityFilter(query, filters);

addComponentRelatedFilters(query, filters);

addDatesFilter(filters, query);
addCreatedAfterByProjectsFilter(filters, query);
return filters;
}

private static void addSeverityFilter(IssueQuery query, Map<String, QueryBuilder> filters) {
QueryBuilder severityFieldFilter = createTermsFilter(FIELD_ISSUE_SEVERITY, query.severities());
if (severityFieldFilter != null) {
filters.put(FIELD_ISSUE_SEVERITY, boolQuery()
.must(severityFieldFilter)
// Ignore severity of Security HotSpots
.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())));
}
}

private static void addComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
addCommonComponentRelatedFilters(query, filters);
if (query.viewUuids().isEmpty()) {
@@ -452,6 +463,19 @@ public class IssueIndex {
return FACET_MODE_EFFORT.equals(query.facetMode());
}

private static AggregationBuilder createSeverityFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
String fieldName = SEVERITIES.getFieldName();
String facetName = SEVERITIES.getName();
StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, filters, queryBuilder);
BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName)
// Ignore severity of Security HotSpots
.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()));
FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, SEVERITIES.getSize());
return AggregationBuilders
.global(facetName)
.subAggregation(facetTopAggregation);
}

private static AggregationBuilder createAssigneesFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
String fieldName = ASSIGNEES.getFieldName();
String facetName = ASSIGNEES.getName();
@@ -460,11 +484,11 @@ public class IssueIndex {
Map<String, QueryBuilder> assigneeFilters = Maps.newHashMap(filters);
assigneeFilters.remove(IS_ASSIGNED_FILTER);
assigneeFilters.remove(fieldName);
StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, assigneeFilters, queryBuilder);
BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, ASSIGNEES.getSize());
StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, assigneeFilters, queryBuilder);
BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName);
FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, ASSIGNEES.getSize());
if (!query.assignees().isEmpty()) {
facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t, query.assignees().toArray());
facetTopAggregation = stickyFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t, query.assignees().toArray());
}

// Add missing facet for unassigned issues
@@ -486,10 +510,10 @@ public class IssueIndex {
Map<String, QueryBuilder> resolutionFilters = Maps.newHashMap(filters);
resolutionFilters.remove("__isResolved");
resolutionFilters.remove(fieldName);
StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, resolutionFilters, esQuery);
BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, RESOLUTIONS.getSize());
facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t);
StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, resolutionFilters, esQuery);
BoolQueryBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(fieldName);
FilterAggregationBuilder facetTopAggregation = stickyFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, RESOLUTIONS.getSize());
facetTopAggregation = stickyFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t);

// Add missing facet for unresolved issues
facetTopAggregation.subAggregation(
@@ -570,7 +594,6 @@ public class IssueIndex {
private void configureStickyFacets(IssueQuery query, SearchOptions options, Map<String, QueryBuilder> filters, QueryBuilder esQuery, SearchRequestBuilder esSearch) {
if (!options.getFacets().isEmpty()) {
StickyFacetBuilder stickyFacetBuilder = newStickyFacetBuilder(query, filters, esQuery);
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, SEVERITIES);
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, STATUSES);
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, PROJECT_UUIDS, query.projectUuids().toArray());
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, MODULE_UUIDS, query.moduleUuids().toArray());
@@ -585,6 +608,9 @@ public class IssueIndex {
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, OWASP_TOP_10, query.owaspTop10().toArray());
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, SANS_TOP_25, query.sansTop25().toArray());
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, CWE, query.cwe().toArray());
if (options.getFacets().contains(PARAM_SEVERITIES)) {
esSearch.addAggregation(createSeverityFacet(query, filters, esQuery));
}
if (options.getFacets().contains(PARAM_RESOLUTIONS)) {
esSearch.addAggregation(createResolutionFacet(query, filters, esQuery));
}
@@ -756,7 +782,7 @@ public class IssueIndex {
boolQuery()
.mustNot(existsQuery(FIELD_ISSUE_RESOLUTION))
.filter(termQuery(FIELD_ISSUE_ASSIGNEE_UUID, assigneeUuid))
.mustNot(termQuery(FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name())))
.mustNot(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name())))
.setSize(0);
IntStream.range(0, projectUuids.size()).forEach(i -> {
String projectUuid = projectUuids.get(i);
@@ -890,25 +916,25 @@ public class IssueIndex {
return categoryAggs
.subAggregation(
AggregationBuilders.filter("vulnerabilities", boolQuery()
.filter(termQuery(FIELD_ISSUE_TYPE, RuleType.VULNERABILITY.name()))
.filter(termQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name()))
.mustNot(existsQuery(FIELD_ISSUE_RESOLUTION)))
.subAggregation(
AggregationBuilders.terms("severity").field(FIELD_ISSUE_SEVERITY)
.subAggregation(
AggregationBuilders.count(COUNT).field(FIELD_ISSUE_KEY))))
.subAggregation(AggregationBuilders.filter("openSecurityHotspots", boolQuery()
.filter(termQuery(FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name()))
.filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
.mustNot(existsQuery(FIELD_ISSUE_RESOLUTION)))
.subAggregation(
AggregationBuilders.count(COUNT).field(FIELD_ISSUE_KEY)))
.subAggregation(AggregationBuilders.filter("toReviewSecurityHotspots", boolQuery()
.filter(termQuery(FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name()))
.filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
.filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_RESOLVED))
.filter(termQuery(FIELD_ISSUE_RESOLUTION, Issue.RESOLUTION_FIXED)))
.subAggregation(
AggregationBuilders.count(COUNT).field(FIELD_ISSUE_KEY)))
.subAggregation(AggregationBuilders.filter("wontFixSecurityHotspots", boolQuery()
.filter(termQuery(FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name()))
.filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
.filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_RESOLVED))
.filter(termQuery(FIELD_ISSUE_RESOLUTION, Issue.RESOLUTION_WONT_FIX)))
.subAggregation(
@@ -931,7 +957,7 @@ public class IssueIndex {
return client.prepareSearch(TYPE_ISSUE.getMainType())
.setQuery(
componentFilter
.filter(termsQuery(FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name(), RuleType.VULNERABILITY.name()))
.filter(termsQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name(), VULNERABILITY.name()))
.mustNot(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_CLOSED)))
.setSize(0);
}

+ 5
- 2
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
@@ -162,7 +163,7 @@ public class SearchResponseFormat {

private void formatIssue(Issue.Builder issueBuilder, IssueDto dto, SearchResponseData data) {
issueBuilder.setKey(dto.getKey());
ofNullable(dto.getType()).map(Common.RuleType::forNumber).ifPresent(issueBuilder::setType);
issueBuilder.setType(Common.RuleType.forNumber(dto.getType()));

ComponentDto component = data.getComponentByUuid(dto.getComponentUuid());
issueBuilder.setOrganization(data.getOrganizationKey(component.getOrganizationUuid()));
@@ -182,7 +183,9 @@ public class SearchResponseFormat {
issueBuilder.setExternalRuleEngine(engineNameFrom(dto.getRuleKey()));
}
issueBuilder.setFromHotspot(dto.isFromHotspot());
issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity()));
if (dto.getType() != RuleType.SECURITY_HOTSPOT.getDbConstant()) {
issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity()));
}
ofNullable(data.getUserByUuid(dto.getAssigneeUuid())).ifPresent(assignee -> issueBuilder.setAssignee(assignee.getLogin()));
ofNullable(emptyToNull(dto.getResolution())).ifPresent(issueBuilder::setResolution);
issueBuilder.setStatus(dto.getStatus());

+ 0
- 25
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java View File

@@ -23,10 +23,8 @@ import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.assertj.core.api.Fail;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.junit.Rule;
import org.junit.Test;
@@ -40,7 +38,6 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.permission.index.IndexPermissions;
import org.sonar.server.permission.index.PermissionIndexerTester;
@@ -56,7 +53,6 @@ import static java.util.Collections.singletonList;
import static java.util.TimeZone.getTimeZone;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.PROJECT;
@@ -471,19 +467,6 @@ public class IssueIndexFiltersTest {
assertThatSearchReturnsEmpty(IssueQuery.builder().severities(singletonList(Severity.BLOCKER)));
}

@Test
public void facets_on_severities() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setSeverity(Severity.INFO),
newDoc("I2", file).setSeverity(Severity.INFO),
newDoc("I3", file).setSeverity(Severity.MAJOR));

assertThatFacetHasOnly(IssueQuery.builder(), "severities", entry("INFO", 2L), entry("MAJOR", 1L));
}

@Test
public void filter_by_statuses() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
@@ -789,12 +772,4 @@ public class IssueIndexFiltersTest {
List<String> keys = searchAndReturnKeys(query);
assertThat(keys).isEmpty();
}

@SafeVarargs
private final void assertThatFacetHasOnly(IssueQuery.Builder query, String facet, Map.Entry<String, Long>... expectedEntries) {
SearchResponse result = underTest.search(query.build(), new SearchOptions().addFacets(singletonList(facet)));
Facets facets = new Facets(result, system2.getDefaultTimeZone());
assertThat(facets.getNames()).containsOnly(facet, "effort");
assertThat(facets.get(facet)).containsOnly(expectedEntries);
}
}

+ 166
- 0
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityHotspotsTest.java View File

@@ -0,0 +1,166 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.issue.index;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.permission.index.IndexPermissions;
import org.sonar.server.permission.index.PermissionIndexerTester;
import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.tester.UserSessionRule;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Collections.singletonList;
import static java.util.TimeZone.getTimeZone;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.rule.Severity.INFO;
import static org.sonar.api.rule.Severity.MAJOR;
import static org.sonar.api.rules.RuleType.BUG;
import static org.sonar.api.rules.RuleType.CODE_SMELL;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonar.server.issue.IssueDocTesting.newDoc;

public class IssueIndexSecurityHotspotsTest {

@Rule
public EsTester es = EsTester.create();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
@Rule
public ExpectedException expectedException = none();
private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00"));
@Rule
public DbTester db = DbTester.create(system2);

private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer);

private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule));

@Test
public void filter_by_security_hotspots_type() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setType(BUG),
newDoc("I2", file).setType(CODE_SMELL),
newDoc("I3", file).setType(VULNERABILITY),
newDoc("I4", file).setType(VULNERABILITY),
newDoc("I5", file).setType(SECURITY_HOTSPOT),
newDoc("I6", file).setType(SECURITY_HOTSPOT));

assertThatSearchReturnsOnly(IssueQuery.builder().types(singletonList(SECURITY_HOTSPOT.name())), "I5", "I6");
assertThatSearchReturnsOnly(IssueQuery.builder().types(asList(SECURITY_HOTSPOT.name(), VULNERABILITY.name())), "I3", "I4", "I5", "I6");
assertThatSearchReturnsOnly(IssueQuery.builder(), "I1", "I2", "I3", "I4", "I5", "I6");
}

@Test
public void filter_by_severities_ignore_hotspots() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setSeverity(Severity.INFO).setType(BUG),
newDoc("I2", file).setSeverity(Severity.MAJOR).setType(CODE_SMELL),
newDoc("I3", file).setSeverity(Severity.MAJOR).setType(VULNERABILITY),
newDoc("I4", file).setSeverity(Severity.CRITICAL).setType(VULNERABILITY),
// This entry should be ignored
newDoc("I5", file).setSeverity(Severity.MAJOR).setType(SECURITY_HOTSPOT));

assertThatSearchReturnsOnly(IssueQuery.builder().severities(asList(Severity.INFO, Severity.MAJOR)), "I1", "I2", "I3");
assertThatSearchReturnsOnly(IssueQuery.builder().severities(asList(Severity.INFO, Severity.MAJOR)).types(singletonList(VULNERABILITY.name())), "I3");
assertThatSearchReturnsEmpty(IssueQuery.builder().severities(singletonList(Severity.MAJOR)).types(singletonList(SECURITY_HOTSPOT.name())));
}

@Test
public void facet_on_severities_ignore_hotspots() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setSeverity(INFO).setType(BUG),
newDoc("I2", file).setSeverity(INFO).setType(CODE_SMELL),
newDoc("I3", file).setSeverity(INFO).setType(VULNERABILITY),
newDoc("I4", file).setSeverity(MAJOR).setType(VULNERABILITY),
// These 2 entries should be ignored
newDoc("I5", file).setSeverity(INFO).setType(SECURITY_HOTSPOT),
newDoc("I6", file).setSeverity(MAJOR).setType(SECURITY_HOTSPOT));

assertThatFacetHasOnly(IssueQuery.builder(), "severities", entry("INFO", 3L), entry("MAJOR", 1L));
assertThatFacetHasOnly(IssueQuery.builder().types(singletonList(VULNERABILITY.name())), "severities", entry("INFO", 1L), entry("MAJOR", 1L));
assertThatFacetHasOnly(IssueQuery.builder().types(asList(BUG.name(), CODE_SMELL.name(), VULNERABILITY.name())), "severities", entry("INFO", 3L), entry("MAJOR", 1L));
assertThatFacetHasOnly(IssueQuery.builder().types(singletonList(SECURITY_HOTSPOT.name())), "severities");
}

private void indexIssues(IssueDoc... issues) {
issueIndexer.index(asList(issues).iterator());
authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList()));
}

@SafeVarargs
private final void assertThatFacetHasOnly(IssueQuery.Builder query, String facet, Map.Entry<String, Long>... expectedEntries) {
SearchResponse result = underTest.search(query.build(), new SearchOptions().addFacets(singletonList(facet)));
Facets facets = new Facets(result, system2.getDefaultTimeZone());
assertThat(facets.getNames()).containsOnly(facet, "effort");
assertThat(facets.get(facet)).containsOnly(expectedEntries);
}

private void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) {
List<String> keys = searchAndReturnKeys(query);
assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys);
}

private void assertThatSearchReturnsEmpty(IssueQuery.Builder query) {
List<String> keys = searchAndReturnKeys(query);
assertThat(keys).isEmpty();
}

private List<String> searchAndReturnKeys(IssueQuery.Builder query) {
return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits())
.map(SearchHit::getId)
.collect(Collectors.toList());
}

}

+ 0
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java View File

@@ -48,7 +48,6 @@ import org.sonar.server.permission.index.PermissionIndexerTester;
import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.rule.index.RuleIndexer;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.view.index.ViewIndexer;

import static com.google.common.collect.ImmutableSortedSet.of;
import static java.util.Arrays.asList;
@@ -86,7 +85,6 @@ public class IssueIndexTest {
public DbTester db = DbTester.create(system2);

private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client());
private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer);


+ 46
- 0
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java View File

@@ -720,6 +720,52 @@ public class SearchActionTest {
.containsExactlyInAnyOrder(Common.RuleType.BUG, Common.RuleType.SECURITY_HOTSPOT);
}

@Test
public void security_hotspot_are_ignored_when_filtering_by_severities() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = db.rules().insert();
db.issues().insert(rule, project, file, i -> i.setType(RuleType.BUG).setSeverity(Severity.MAJOR.name()));
db.issues().insert(rule, project, file, i -> i.setType(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR.name()));
db.issues().insert(rule, project, file, i -> i.setType(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR.name()));
db.issues().insert(rule, project, file, i -> i.setType(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR.name()));
indexPermissions();
indexIssues();

SearchWsResponse result = ws.newRequest()
.setParam("severities", Severity.MAJOR.name())
.setParam(FACETS, "severities")
.executeProtobuf(SearchWsResponse.class);

assertThat(result.getIssuesList())
.extracting(Issue::getType)
.containsExactlyInAnyOrder(Common.RuleType.BUG, Common.RuleType.VULNERABILITY, Common.RuleType.CODE_SMELL);
assertThat(result.getFacets().getFacets(0).getValuesList())
.extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
.containsExactlyInAnyOrder(tuple("MAJOR", 3L), tuple("INFO", 0L), tuple("MINOR", 0L), tuple("CRITICAL", 0L), tuple("BLOCKER", 0L));
}

@Test
public void do_not_return_severity_on_security_hotspots() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = db.rules().insert();
db.issues().insert(rule, project, file, i -> i.setType(RuleType.BUG).setSeverity(Severity.MAJOR.name()));
db.issues().insert(rule, project, file, i -> i.setType(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR.name()));
indexPermissions();
indexIssues();

SearchWsResponse result = ws.newRequest()
.setParam("types", String.format("%s,%s", RuleType.BUG, RuleType.SECURITY_HOTSPOT))
.executeProtobuf(SearchWsResponse.class);

assertThat(result.getIssuesList())
.extracting(Issue::getType, Issue::hasSeverity)
.containsExactlyInAnyOrder(
tuple(Common.RuleType.BUG, true),
tuple(Common.RuleType.SECURITY_HOTSPOT, false));
}

@Test
public void return_total_effort() {
UserDto john = db.users().insertUser();

Loading…
Cancel
Save