aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZipeng WU <zipeng.wu@sonarsource.com>2022-09-30 10:11:08 +0200
committerPhilippe Perrin <philippe.perrin@sonarsource.com>2022-10-07 12:13:56 +0200
commit1942fe88ebad22640e16b21a1896eca094f7a6f7 (patch)
tree4f2c06814bb70bb610420887acc070ad75e9a507
parentc4184ce3148131b61d3fc51f7a816e3cf9e88540 (diff)
downloadsonarqube-1942fe88ebad22640e16b21a1896eca094f7a6f7.tar.gz
sonarqube-1942fe88ebad22640e16b21a1896eca094f7a6f7.zip
SONAR-17393 Add OWASP ASVS filtering to issues search API call
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java11
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java2
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java39
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java1
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java35
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java8
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java35
7 files changed, 102 insertions, 29 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
index 108e8577661..8852d1f2e41 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
@@ -64,6 +64,7 @@ public class SearchRequest {
private List<String> pciDss32;
private List<String> pciDss40;
private List<String> owaspTop10;
+ private List<String> owaspAsvs40;
private List<String> owaspTop10For2021;
private List<String> sansTop25;
private List<String> sonarsourceSecurity;
@@ -391,6 +392,16 @@ public class SearchRequest {
}
@CheckForNull
+ public List<String> getOwaspAsvs40() {
+ return owaspAsvs40;
+ }
+
+ public SearchRequest setOwaspAsvs40(@Nullable List<String> owaspAsvs40) {
+ this.owaspAsvs40 = owaspAsvs40;
+ return this;
+ }
+
+ @CheckForNull
public List<String> getOwaspTop10() {
return owaspTop10;
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
index 774c12d6092..adaeb8a4fcb 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
@@ -50,6 +50,7 @@ public class SearchRequestTest {
.setAsc(true)
.setInNewCodePeriod(true)
.setOwaspTop10For2021(asList("a2", "a3"))
+ .setOwaspAsvs40(asList("1.1.1", "4.2.2"))
.setPciDss32(asList("1", "4"))
.setPciDss40(asList("3", "5"));
@@ -73,6 +74,7 @@ public class SearchRequestTest {
assertThat(underTest.getAsc()).isTrue();
assertThat(underTest.getInNewCodePeriod()).isTrue();
assertThat(underTest.getOwaspTop10For2021()).containsExactly("a2", "a3");
+ assertThat(underTest.getOwaspAsvs40()).containsExactly("1.1.1", "4.2.2");
assertThat(underTest.getPciDss32()).containsExactly("1", "4");
assertThat(underTest.getPciDss40()).containsExactly("3", "5");
}
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index bfabfb5af6a..093324796ad 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -461,8 +461,9 @@ public class IssueIndex {
filters.addFilter(FIELD_ISSUE_STATUS, STATUSES.getFilterScope(), createTermsFilter(FIELD_ISSUE_STATUS, query.statuses()));
// security category
- addPciDssSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_32, PCI_DSS_32, query.pciDss32(), filters);
- addPciDssSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_40, PCI_DSS_40, query.pciDss40(), filters);
+ addSecurityCategoryPrefixFilter(FIELD_ISSUE_PCI_DSS_32, PCI_DSS_32, query.pciDss32(), filters);
+ addSecurityCategoryPrefixFilter(FIELD_ISSUE_PCI_DSS_40, PCI_DSS_40, query.pciDss40(), filters);
+ addSecurityCategoryPrefixFilter(FIELD_ISSUE_OWASP_ASVS_40, OWASP_ASVS_40, query.owaspAsvs40(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_OWASP_TOP_10, OWASP_TOP_10, query.owaspTop10(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_OWASP_TOP_10_2021, OWASP_TOP_10_2021, query.owaspTop10For2021(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_SANS_TOP_25, SANS_TOP_25, query.sansTop25(), filters);
@@ -511,7 +512,7 @@ public class IssueIndex {
* @param values The PCI DSS categories to search for
* @param allFilters Object that holds all the filters for the Elastic search call
*/
- private static void addPciDssSecurityCategoryFilter(String fieldName, Facet facet, Collection<String> values, AllFilters allFilters) {
+ private static void addSecurityCategoryPrefixFilter(String fieldName, Facet facet, Collection<String> values, AllFilters allFilters) {
if (values.isEmpty()) {
return;
}
@@ -522,7 +523,7 @@ public class IssueIndex {
// the field type must be vulnerability or security hotspot
.must(termsQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()));
// for top level categories a prefix query is added, while for subcategories a term query is used for exact matching
- values.stream().map(v -> choosePciDssQuery(fieldName, v)).forEach(boolQueryBuilder::should);
+ values.stream().map(v -> choosePrefixQuery(fieldName, v)).forEach(boolQueryBuilder::should);
allFilters.addFilter(
fieldName,
@@ -530,7 +531,7 @@ public class IssueIndex {
boolQueryBuilder);
}
- private static QueryBuilder choosePciDssQuery(String fieldName, String value) {
+ private static QueryBuilder choosePrefixQuery(String fieldName, String value) {
return value.contains(".") ? createTermFilter(fieldName, value) : createPrefixFilter(fieldName, value + ".");
}
@@ -626,11 +627,11 @@ public class IssueIndex {
private static RequestFiltersComputer newFilterComputer(SearchOptions options, AllFilters allFilters) {
Collection<String> facetNames = options.getFacets();
Set<TopAggregationDefinition<?>> facets = Stream.concat(
- Stream.of(EFFORT_TOP_AGGREGATION),
- facetNames.stream()
- .map(FACETS_BY_NAME::get)
- .filter(Objects::nonNull)
- .map(Facet::getTopAggregationDef))
+ Stream.of(EFFORT_TOP_AGGREGATION),
+ facetNames.stream()
+ .map(FACETS_BY_NAME::get)
+ .filter(Objects::nonNull)
+ .map(Facet::getTopAggregationDef))
.collect(MoreCollectors.toSet(facetNames.size()));
return new RequestFiltersComputer(allFilters, facets);
@@ -835,11 +836,11 @@ public class IssueIndex {
RESOLUTIONS.getName(), RESOLUTIONS.getTopAggregationDef(), RESOLUTIONS.getNumberOfTerms(),
NO_EXTRA_FILTER,
t ->
- // add aggregation of type "missing" to return count of unresolved issues in the facet
- t.subAggregation(
- addEffortAggregationIfNeeded(query, AggregationBuilders
- .missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING)
- .field(RESOLUTIONS.getFieldName()))));
+ // add aggregation of type "missing" to return count of unresolved issues in the facet
+ t.subAggregation(
+ addEffortAggregationIfNeeded(query, AggregationBuilders
+ .missing(RESOLUTIONS.getName() + FACET_SUFFIX_MISSING)
+ .field(RESOLUTIONS.getFieldName()))));
esRequest.aggregation(aggregation);
}
@@ -959,10 +960,10 @@ public class IssueIndex {
ASSIGNED_TO_ME.getNumberOfTerms(),
NO_EXTRA_FILTER,
t ->
- // add sub-aggregation to return issue count for current user
- aggregationHelper.getSubAggregationHelper()
- .buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTopAggregationDef(), new String[] {uuid})
- .ifPresent(t::subAggregation));
+ // add sub-aggregation to return issue count for current user
+ aggregationHelper.getSubAggregationHelper()
+ .buildSelectedItemsAggregation(ASSIGNED_TO_ME.getName(), ASSIGNED_TO_ME.getTopAggregationDef(), new String[] {uuid})
+ .ifPresent(t::subAggregation));
esRequest.aggregation(aggregation);
}
}
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
index f6c2773b806..ed9e96a6a77 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
@@ -139,6 +139,7 @@ public class IssueQueryFactory {
.types(request.getTypes())
.pciDss32(request.getPciDss32())
.pciDss40(request.getPciDss40())
+ .owaspAsvs40(request.getOwaspAsvs40())
.owaspTop10(request.getOwaspTop10())
.owaspTop10For2021(request.getOwaspTop10For2021())
.sansTop25(request.getSansTop25())
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
index 20d86b48c40..91061c3adf8 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.elasticsearch.action.search.SearchResponse;
import org.junit.Test;
import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.rule.RulesDefinition.OwaspAsvsVersion;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.es.Facets;
@@ -194,6 +195,22 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
}
@Test
+ public void facets_on_owaspAsvs40() {
+ ComponentDto project = newPrivateProjectDto();
+ ComponentDto file = newFileDto(project, null);
+
+ indexIssues(
+ newDoc("I1", file).setType(RuleType.VULNERABILITY).setOwaspAsvs40(asList("1", "2")),
+ newDoc("I2", file).setType(RuleType.VULNERABILITY).setOwaspAsvs40(singletonList("3")),
+ newDoc("I3", file));
+
+ assertThatFacetHasOnly(IssueQuery.builder(), OwaspAsvsVersion.V4_0.prefix(),
+ entry("1", 1L),
+ entry("2", 1L),
+ entry("3", 1L));
+ }
+
+ @Test
public void facets_on_owaspTop10() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project, null);
@@ -521,8 +538,8 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
SearchOptions options = fixtureForCreatedAtFacet();
SearchResponse result = underTest.search(IssueQuery.builder()
- .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
- .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
+ .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+ .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
options);
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
assertThat(createdAt).containsOnly(
@@ -537,8 +554,8 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
SearchOptions options = fixtureForCreatedAtFacet();
SearchResponse result = underTest.search(IssueQuery.builder()
- .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
- .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
+ .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+ .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
options);
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
assertThat(createdAt).containsOnly(
@@ -555,8 +572,8 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
SearchOptions options = fixtureForCreatedAtFacet();
SearchResponse result = underTest.search(IssueQuery.builder()
- .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
- .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+ .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
+ .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
options);
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
assertThat(createdAt).containsOnly(
@@ -573,8 +590,8 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
SearchOptions options = fixtureForCreatedAtFacet();
SearchResponse result = underTest.search(IssueQuery.builder()
- .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
- .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
+ .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
+ .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
options);
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
assertThat(createdAt).containsOnly(
@@ -606,7 +623,7 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
SearchOptions searchOptions = fixtureForCreatedAtFacet();
SearchResponse result = underTest.search(IssueQuery.builder()
- .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+ .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
searchOptions);
Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
assertThat(createdAt).containsOnly(
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index 36aa53d81cb..e2f508b8bb8 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -110,6 +110,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IN_NEW_CODE
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ON_COMPONENT_ONLY;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_ASVS_40;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10_2021;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PCI_DSS_32;
@@ -151,6 +152,7 @@ public class SearchAction implements IssuesWsAction {
PARAM_TYPES,
PARAM_PCI_DSS_32,
PARAM_PCI_DSS_40,
+ PARAM_OWASP_ASVS_40,
PARAM_OWASP_TOP_10,
PARAM_OWASP_TOP_10_2021,
PARAM_SANS_TOP_25,
@@ -277,6 +279,10 @@ public class SearchAction implements IssuesWsAction {
.setDescription("Comma-separated list of PCI DSS v4.0 categories.")
.setSince("9.6")
.setExampleValue("4,6.5.8,10.1");
+ action.createParam(PARAM_OWASP_ASVS_40)
+ .setDescription("Comma-separated list of OWASP ASVS v4.0 categories.")
+ .setSince("9.7")
+ .setExampleValue("6,10.1.1");
action.createParam(PARAM_OWASP_TOP_10)
.setDescription("Comma-separated list of OWASP Top 10 2017 lowercase categories.")
.setSince("7.3")
@@ -484,6 +490,7 @@ public class SearchAction implements IssuesWsAction {
addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_32, request.getPciDss32());
addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_40, request.getPciDss40());
+ addMandatoryValuesToFacet(facets, PARAM_OWASP_ASVS_40, request.getOwaspAsvs40());
addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10, request.getOwaspTop10());
addMandatoryValuesToFacet(facets, PARAM_OWASP_TOP_10_2021, request.getOwaspTop10For2021());
addMandatoryValuesToFacet(facets, PARAM_SANS_TOP_25, request.getSansTop25());
@@ -560,6 +567,7 @@ public class SearchAction implements IssuesWsAction {
.setTypes(allRuleTypesExceptHotspotsIfEmpty(request.paramAsStrings(PARAM_TYPES)))
.setPciDss32(request.paramAsStrings(PARAM_PCI_DSS_32))
.setPciDss40(request.paramAsStrings(PARAM_PCI_DSS_40))
+ .setOwaspAsvs40(request.paramAsStrings(PARAM_OWASP_ASVS_40))
.setOwaspTop10(request.paramAsStrings(PARAM_OWASP_TOP_10))
.setOwaspTop10For2021(request.paramAsStrings(PARAM_OWASP_TOP_10_2021))
.setSansTop25(request.paramAsStrings(PARAM_SANS_TOP_25))
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
index 293ea18a699..5f8f3e082bd 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
@@ -1056,6 +1056,39 @@ public class SearchActionTest {
}
@Test
+ public void only_vulnerabilities_are_returned_by_owaspAsvs40() {
+ ComponentDto project = db.components().insertPublicProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
+ Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
+ .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "owaspAsvs-4.0:12.3.1"))
+ .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+ Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+ RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
+ db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
+ RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
+ IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+ IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+ IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
+ indexPermissionsAndIssues();
+
+ SearchWsResponse result = ws.newRequest()
+ .setParam("owaspAsvs-4.0", "12.3.1")
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey());
+
+ result = ws.newRequest()
+ .setParam("owaspAsvs-4.0", "12")
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey());
+ }
+
+ @Test
public void only_vulnerabilities_are_returned_by_pciDss32() {
ComponentDto project = db.components().insertPublicProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
@@ -1692,7 +1725,7 @@ public class SearchActionTest {
assertThat(def.params()).extracting("key").containsExactlyInAnyOrder(
"additionalFields", "asc", "assigned", "assignees", "author", "componentKeys", "branch", "pullRequest", "createdAfter", "createdAt",
"createdBefore", "createdInLast", "directories", "facets", "files", "issues", "scopes", "languages", "onComponentOnly",
- "p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspTop10",
+ "p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspAsvs-4.0", "owaspTop10",
"owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod");
WebService.Param branch = def.param(PARAM_BRANCH);