From 20cc332ffff7701ca9a4ac73186fa2adb62bee72 Mon Sep 17 00:00:00 2001 From: Dimitris Kavvathas Date: Fri, 22 Jul 2022 10:24:21 +0200 Subject: [PATCH] SONAR-17061 Add PCI DSS parameter and facet in 'api/issues/search' API endpoint --- .../org/sonar/server/issue/SearchRequest.java | 22 ++ .../sonar/server/issue/SearchRequestTest.java | 6 +- .../sonar/server/issue/index/IssueIndex.java | 55 +++- .../server/issue/index/IssueQueryFactory.java | 6 +- .../issue/index/IssueIndexDebtTest.java | 36 +-- .../issue/index/IssueIndexFacetsTest.java | 69 ++--- .../issue/index/IssueIndexFiltersTest.java | 56 +---- .../IssueIndexProjectStatisticsTest.java | 31 +-- .../IssueIndexSecurityCategoriesTest.java | 140 +++++++++++ .../index/IssueIndexSecurityHotspotsTest.java | 49 +--- .../index/IssueIndexSecurityReportsTest.java | 34 +-- .../issue/index/IssueIndexSortTest.java | 56 +---- .../server/issue/index/IssueIndexTest.java | 59 +---- .../issue/index/IssueIndexTestCommon.java | 89 +++++++ .../sonar/server/issue/ws/SearchAction.java | 22 +- .../server/issue/ws/SearchActionTest.java | 238 +++++++++++++++++- 16 files changed, 607 insertions(+), 361 deletions(-) create mode 100644 server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityCategoriesTest.java create mode 100644 server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTestCommon.java 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 bc1273d762e..108e8577661 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 @@ -61,6 +61,8 @@ public class SearchRequest { private List statuses; private List tags; private Set types; + private List pciDss32; + private List pciDss40; private List owaspTop10; private List owaspTop10For2021; private List sansTop25; @@ -368,6 +370,26 @@ public class SearchRequest { return this; } + @CheckForNull + public List getPciDss32() { + return pciDss32; + } + + public SearchRequest setPciDss32(@Nullable List pciDss32) { + this.pciDss32 = pciDss32; + return this; + } + + @CheckForNull + public List getPciDss40() { + return pciDss40; + } + + public SearchRequest setPciDss40(@Nullable List pciDss40) { + this.pciDss40 = pciDss40; + return this; + } + @CheckForNull public List 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 61136ff96e6..774c12d6092 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 @@ -49,7 +49,9 @@ public class SearchRequestTest { .setSort("CREATION_DATE") .setAsc(true) .setInNewCodePeriod(true) - .setOwaspTop10For2021(asList("a2", "a3")); + .setOwaspTop10For2021(asList("a2", "a3")) + .setPciDss32(asList("1", "4")) + .setPciDss40(asList("3", "5")); assertThat(underTest.getIssues()).containsOnlyOnce("anIssueKey"); assertThat(underTest.getSeverities()).containsExactly("MAJOR", "MINOR"); @@ -71,6 +73,8 @@ public class SearchRequestTest { assertThat(underTest.getAsc()).isTrue(); assertThat(underTest.getInNewCodePeriod()).isTrue(); assertThat(underTest.getOwaspTop10For2021()).containsExactly("a2", "a3"); + assertThat(underTest.getPciDss32()).containsExactly("1", "4"); + assertThat(underTest.getPciDss40()).containsExactly("3", "5"); } @Test 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 d9c53eed857..2ed15da00f5 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 @@ -455,8 +455,8 @@ public class IssueIndex { filters.addFilter(FIELD_ISSUE_STATUS, STATUSES.getFilterScope(), createTermsFilter(FIELD_ISSUE_STATUS, query.statuses())); // security category - addSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_32, PCI_DSS_32, query.pciDss32(), filters); - addSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_40, PCI_DSS_40, query.pciDss40(), filters); + addPciDssSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_32, PCI_DSS_32, query.pciDss32(), filters); + addPciDssSecurityCategoryFilter(FIELD_ISSUE_PCI_DSS_40, PCI_DSS_40, query.pciDss40(), 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); @@ -485,6 +485,49 @@ public class IssueIndex { } } + /** + *

Builds the Elasticsearch boolean query to filter the PCI DSS categories.

+ * + *

The PCI DSS security report handles all the subcategories as one level. This means that subcategory 1.1 doesn't include the issues from 1.1.1. + * Taking this into account, the search filter follows the same logic and uses prefix matching for top-level categories and exact matching for subcategories

+ * + *

Example

+ *

List of PCI DSS categories in issues: {1.5.8, 1.5.9, 1.6.7} + *

    + *
  • Search: {1}, returns {1.5.8, 1.5.9, 1.6.7}
  • + *
  • Search: {1.5.8}, returns {1.5.8}
  • + *
  • Search: {1.5}, returns {}
  • + *
+ *

+ * + * @param fieldName The PCI DSS version, e.g. pciDss-3.2 + * @param facet The facet used for the filter + * @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 values, AllFilters allFilters) { + if (values.isEmpty()) { + return; + } + + BoolQueryBuilder boolQueryBuilder = boolQuery() + // ensures that at least one "should" query is matched. Without it, "should" queries are optional, when a "must" is also present. + .minimumShouldMatch(1) + // 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); + + allFilters.addFilter( + fieldName, + facet.getFilterScope(), + boolQueryBuilder); + } + + private static QueryBuilder choosePciDssQuery(String fieldName, String value) { + return value.contains(".") ? createTermFilter(fieldName, value) : createPrefixFilter(fieldName, value + "."); + } + private static void addSeverityFilter(IssueQuery query, AllFilters allFilters) { QueryBuilder severityFieldFilter = createTermsFilter(FIELD_ISSUE_SEVERITY, query.severities()); if (severityFieldFilter != null) { @@ -615,6 +658,10 @@ public class IssueIndex { return value == null ? null : termQuery(field, value); } + private static QueryBuilder createPrefixFilter(String field, String value) { + return prefixQuery(field, value); + } + private void configureSorting(IssueQuery query, SearchSourceBuilder esRequest) { createSortBuilders(query).forEach(esRequest::sort); } @@ -715,6 +762,8 @@ public class IssueIndex { addFacetIfNeeded(options, aggregationHelper, esRequest, TAGS, query.tags().toArray()); addFacetIfNeeded(options, aggregationHelper, esRequest, TYPES, query.types().toArray()); + addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_32, PCI_DSS_32, options, aggregationHelper, esRequest, query.pciDss32().toArray()); + addSecurityCategoryFacetIfNeeded(PARAM_PCI_DSS_40, PCI_DSS_40, options, aggregationHelper, esRequest, query.pciDss40().toArray()); addSecurityCategoryFacetIfNeeded(PARAM_OWASP_TOP_10, OWASP_TOP_10, options, aggregationHelper, esRequest, query.owaspTop10().toArray()); addSecurityCategoryFacetIfNeeded(PARAM_OWASP_TOP_10_2021, OWASP_TOP_10_2021, options, aggregationHelper, esRequest, query.owaspTop10For2021().toArray()); addSecurityCategoryFacetIfNeeded(PARAM_SANS_TOP_25, SANS_TOP_25, options, aggregationHelper, esRequest, query.sansTop25().toArray()); @@ -1003,7 +1052,7 @@ public class IssueIndex { requestBuilder.source(sourceBuilder); SearchResponse response = client.search(requestBuilder); return response.getAggregations().asList().stream() - .map(x -> (ParsedFilter) x) + .map(ParsedFilter.class::cast) .flatMap(projectBucket -> ((ParsedStringTerms) projectBucket.getAggregations().get("branchUuid")).getBuckets().stream() .flatMap(branchBucket -> { long count = ((ParsedValueCount) branchBucket.getAggregations().get(AGG_COUNT)).getValue(); 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 989936ff93b..ef43b30c23a 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 @@ -133,6 +133,8 @@ public class IssueQueryFactory { .languages(request.getLanguages()) .tags(request.getTags()) .types(request.getTypes()) + .pciDss32(request.getPciDss32()) + .pciDss40(request.getPciDss40()) .owaspTop10(request.getOwaspTop10()) .owaspTop10For2021(request.getOwaspTop10For2021()) .sansTop25(request.getSansTop25()) @@ -199,8 +201,8 @@ public class IssueQueryFactory { if (!QUALIFIERS_WITHOUT_LEAK_PERIOD.contains(component.qualifier()) && request.getPullRequest() == null) { Optional snapshot = getLastAnalysis(dbSession, component); if (!snapshot.isEmpty() && isLastAnalysisFromReAnalyzedReferenceBranch(dbSession, snapshot.get())) { - builder.newCodeOnReference(true); - return; + builder.newCodeOnReference(true); + return; } // if last analysis has no period date, then no issue should be considered new. Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(snapshot); diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java index 0a595df01e9..94225742656 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java @@ -20,54 +20,25 @@ package org.sonar.server.issue.index; import java.util.Map; -import java.util.TimeZone; -import org.junit.Rule; import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; -import org.sonar.server.es.EsTester; import org.sonar.server.es.Facets; import org.sonar.server.es.SearchOptions; import org.sonar.server.issue.IssueDocTesting; import org.sonar.server.issue.index.IssueQuery.Builder; -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.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; import static org.sonar.api.issue.Issue.STATUS_CLOSED; import static org.sonar.api.issue.Issue.STATUS_OPEN; -import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT; -public class IssueIndexDebtTest { - - private final System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(TimeZone.getTimeZone("GMT-01:00")); - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - @Rule - public DbTester db = DbTester.create(system2); - - private final AsyncIssueIndexing asyncIssueIndexing = mock(AsyncIssueIndexing.class); - private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), asyncIssueIndexing); - private final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - private final IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexDebtTest extends IssueIndexTestCommon { @Test public void facets_on_projects() { @@ -248,11 +219,6 @@ public class IssueIndexDebtTest { return new SearchOptions().addFacets("createdAt"); } - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } - private Facets search(String additionalFacet) { return new Facets(underTest.search(newQueryBuilder().build(), new SearchOptions().addFacets(singletonList(additionalFacet))), system2.getDefaultTimeZone().toZoneId()); } 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 ac1062dde0a..20d86b48c40 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 @@ -23,30 +23,17 @@ import java.time.ZoneId; import java.util.Collections; import java.util.Date; import java.util.Map; -import java.util.TimeZone; import org.elasticsearch.action.search.SearchResponse; -import org.junit.Rule; import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.rules.RuleType; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.rule.RuleDto; -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.security.SecurityStandards.SQCategory; -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 java.util.stream.IntStream.rangeClosed; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -59,7 +46,6 @@ import static org.sonar.api.issue.Issue.STATUS_CONFIRMED; import static org.sonar.api.issue.Issue.STATUS_OPEN; import static org.sonar.api.issue.Issue.STATUS_REOPENED; import static org.sonar.api.issue.Issue.STATUS_RESOLVED; -import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.rule.Severity.BLOCKER; import static org.sonar.api.rule.Severity.CRITICAL; import static org.sonar.api.rule.Severity.INFO; @@ -67,6 +53,8 @@ import static org.sonar.api.rule.Severity.MAJOR; import static org.sonar.api.rule.Severity.MINOR; import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017; import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; +import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion.V3_2; +import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion.V4_0; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; @@ -74,21 +62,7 @@ import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexFacetsTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - private final TimeZone defaultTimezone = getTimeZone("GMT-01:00"); - private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(defaultTimezone); - @Rule - public DbTester db = DbTester.create(system2); - - private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), null); - private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - - private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexFacetsTest extends IssueIndexTestCommon { @Test public void facet_on_projectUuids() { @@ -187,6 +161,38 @@ public class IssueIndexFacetsTest { entry("89", 1L)); } + @Test + public void facets_on_pciDss32() { + ComponentDto project = newPrivateProjectDto(); + ComponentDto file = newFileDto(project, null); + + indexIssues( + newDoc("I1", file).setType(RuleType.VULNERABILITY).setPciDss32(asList("1", "2")), + newDoc("I2", file).setType(RuleType.VULNERABILITY).setPciDss32(singletonList("3")), + newDoc("I3", file)); + + assertThatFacetHasOnly(IssueQuery.builder(), V3_2.prefix(), + entry("1", 1L), + entry("2", 1L), + entry("3", 1L)); + } + + @Test + public void facets_on_pciDss40() { + ComponentDto project = newPrivateProjectDto(); + ComponentDto file = newFileDto(project, null); + + indexIssues( + newDoc("I1", file).setType(RuleType.VULNERABILITY).setPciDss40(asList("1", "2")), + newDoc("I2", file).setType(RuleType.VULNERABILITY).setPciDss40(singletonList("3")), + newDoc("I3", file)); + + assertThatFacetHasOnly(IssueQuery.builder(), V4_0.prefix(), + entry("1", 1L), + entry("2", 1L), + entry("3", 1L)); + } + @Test public void facets_on_owaspTop10() { ComponentDto project = newPrivateProjectDto(); @@ -637,11 +643,6 @@ public class IssueIndexFacetsTest { return new SearchOptions().addFacets("createdAt"); } - 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 assertThatFacetHasExactly(IssueQuery.Builder query, String facet, Map.Entry... expectedEntries) { SearchResponse result = underTest.search(query.build(), new SearchOptions().addFacets(singletonList(facet))); diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java index a41d651ba9a..8312ff90cf4 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java @@ -20,43 +20,26 @@ package org.sonar.server.issue.index; import com.google.common.collect.ImmutableMap; -import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import org.assertj.core.api.Fail; -import org.elasticsearch.search.SearchHit; -import org.junit.Rule; import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.rule.RuleDto; -import org.sonar.server.es.EsTester; 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.security.SecurityStandards.SQCategory; -import org.sonar.server.tester.UserSessionRule; import org.sonar.server.view.index.ViewDoc; -import org.sonar.server.view.index.ViewIndexer; import static java.util.Arrays.asList; -import static java.util.Arrays.stream; import static java.util.Collections.emptyList; 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.assertThatThrownBy; import static org.sonar.api.resources.Qualifiers.APP; -import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.utils.DateUtils.addDays; import static org.sonar.api.utils.DateUtils.parseDate; import static org.sonar.api.utils.DateUtils.parseDateTime; @@ -66,20 +49,7 @@ import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexFiltersTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - private final System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00")); - @Rule - public DbTester db = DbTester.create(system2); - - private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), null); - private final ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client()); - private final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - private final IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexFiltersTest extends IssueIndexTestCommon { @Test public void filter_by_keys() { @@ -844,31 +814,7 @@ public class IssueIndexFiltersTest { assertThatSearchReturnsOnly(IssueQuery.builder().sonarsourceSecurity(singletonList("buffer-overflow")), "I1"); } - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } - private void indexView(String viewUuid, List projects) { viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects)); } - - /** - * Execute the search request and return the document ids of results. - */ - private List searchAndReturnKeys(IssueQuery.Builder query) { - return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); - } - - private void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) { - List keys = searchAndReturnKeys(query); - assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys); - } - - private void assertThatSearchReturnsEmpty(IssueQuery.Builder query) { - List keys = searchAndReturnKeys(query); - assertThat(keys).isEmpty(); - } } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java index 07279338865..59739bf78be 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java @@ -21,44 +21,22 @@ package org.sonar.server.issue.index; import java.util.Date; import java.util.List; -import org.junit.Rule; import org.junit.Test; import org.sonar.api.issue.Issue; -import org.sonar.api.utils.System2; import org.sonar.db.component.ComponentDto; -import org.sonar.server.es.EsTester; -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.emptyList; import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.Mockito.mock; -import static org.sonar.api.resources.Qualifiers.PROJECT; +import static org.sonar.db.component.ComponentTesting.newBranchComponent; import static org.sonar.db.component.ComponentTesting.newBranchDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; -import static org.sonar.db.component.ComponentTesting.newBranchComponent; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexProjectStatisticsTest { - - private System2 system2 = mock(System2.class); - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - - private IssueIndexer issueIndexer = new IssueIndexer(es.client(), null, new IssueIteratorFactory(null), null); - private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - - private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexProjectStatisticsTest extends IssueIndexTestCommon { @Test public void searchProjectStatistics_returns_empty_list_if_no_input() { @@ -249,9 +227,4 @@ public class IssueIndexProjectStatisticsTest { tuple(2L, branch.uuid(), from + 2L), tuple(1L, project.uuid(), from + 1L)); } - - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityCategoriesTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityCategoriesTest.java new file mode 100644 index 00000000000..4477c766de2 --- /dev/null +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityCategoriesTest.java @@ -0,0 +1,140 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.List; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.RuleType; +import org.sonar.db.component.ComponentDto; + +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.server.issue.IssueDocTesting.newDoc; + +public class IssueIndexSecurityCategoriesTest extends IssueIndexTestCommon { + + @Test + public void searchSinglePciDss32Category() { + ComponentDto project = newPrivateProjectDto(); + + indexIssues( + newDoc("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDoc("openvul2", project).setPciDss32(asList("3.3.2", "1.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR) + ); + + assertThatSearchReturnsOnly(queryPciDss32("1"), "openvul1", "openvul2"); + assertThatSearchReturnsOnly(queryPciDss32("1.2.0"), "openvul1"); + assertThatSearchReturnsEmpty(queryPciDss32("1.2")); + } + + @Test + public void searchMultiplePciDss32Categories() { + ComponentDto project = newPrivateProjectDto(); + + indexIssues( + newDoc("openvul1", project).setPciDss32(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDoc("openvul2", project).setPciDss32(asList("3.3.2", "2.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR), + newDoc("openvul3", project).setPciDss32(asList("4.1", "5.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR) + ); + + assertThatSearchReturnsOnly(queryPciDss32("1", "4"), "openvul1", "openvul3"); + assertThatSearchReturnsOnly(queryPciDss32("1.2.0", "5.4"), "openvul1", "openvul3"); + assertThatSearchReturnsEmpty(queryPciDss32("6", "7", "8", "9", "10", "11", "12")); + } + + @Test + public void searchSinglePciDss40Category() { + ComponentDto project = newPrivateProjectDto(); + + indexIssues( + newDoc("openvul1", project).setPciDss40(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDoc("openvul2", project).setPciDss40(asList("3.3.2", "1.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR) + ); + + assertThatSearchReturnsOnly(queryPciDss40("1"), "openvul1", "openvul2"); + assertThatSearchReturnsOnly(queryPciDss40("1.2.0"), "openvul1"); + assertThatSearchReturnsEmpty(queryPciDss40("1.2")); + } + + @Test + public void searchMultiplePciDss40Categories() { + ComponentDto project = newPrivateProjectDto(); + + indexIssues( + newDoc("openvul1", project).setPciDss40(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDoc("openvul2", project).setPciDss40(asList("3.3.2", "2.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR), + newDoc("openvul3", project).setPciDss40(asList("4.1", "5.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR) + ); + + assertThatSearchReturnsOnly(queryPciDss40("1", "4"), "openvul1", "openvul3"); + assertThatSearchReturnsOnly(queryPciDss40("1.2.0", "5.4"), "openvul1", "openvul3"); + assertThatSearchReturnsEmpty(queryPciDss40("6", "7", "8", "9", "10", "11", "12")); + } + + @Test + public void searchMixedPciDssCategories() { + ComponentDto project = newPrivateProjectDto(); + + indexIssues( + newDoc("openvul1", project).setPciDss40(asList("1.2.0", "3.4.5")).setPciDss32(List.of("2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDoc("openvul2", project).setPciDss40(asList("3.3.2", "2.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR), + newDoc("openvul3", project).setPciDss32(asList("4.1", "5.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR) + ); + + assertThatSearchReturnsOnly(queryPciDss40("1", "4"), "openvul1"); + assertThatSearchReturnsOnly(queryPciDss40("1.2.0", "5.4"), "openvul1"); + assertThatSearchReturnsEmpty(queryPciDss40("6", "7", "8", "9", "10", "11", "12")); + + assertThatSearchReturnsOnly(queryPciDss32("3", "2.1"), "openvul1"); + assertThatSearchReturnsOnly(queryPciDss32("1", "2"), "openvul1"); + assertThatSearchReturnsOnly(queryPciDss32("4", "3"), "openvul3"); + assertThatSearchReturnsEmpty(queryPciDss32("1", "3", "6", "7", "8", "9", "10", "11", "12")); + + } + + private IssueQuery.Builder queryPciDss32(String... values) { + return IssueQuery.builder() + .pciDss32(stream(values).collect(toList())) + .types(List.of("CODE_SMELL", "BUG", "VULNERABILITY")); + } + + private IssueQuery.Builder queryPciDss40(String... values) { + return IssueQuery.builder() + .pciDss40(stream(values).collect(toList())) + .types(List.of("CODE_SMELL", "BUG", "VULNERABILITY")); + } +} diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityHotspotsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityHotspotsTest.java index ee9d8d94c80..951397edc6d 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityHotspotsTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityHotspotsTest.java @@ -19,31 +19,18 @@ */ 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.sonar.api.impl.utils.TestSystem2; import org.sonar.api.rule.Severity; -import org.sonar.api.utils.System2; -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; @@ -58,20 +45,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexSecurityHotspotsTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - 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()), null); - private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - - private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexSecurityHotspotsTest extends IssueIndexTestCommon { @Test public void filter_by_security_hotspots_type() { @@ -129,11 +103,6 @@ public class IssueIndexSecurityHotspotsTest { 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... expectedEntries) { SearchResponse result = underTest.search(query.build(), new SearchOptions().addFacets(singletonList(facet))); @@ -142,20 +111,4 @@ public class IssueIndexSecurityHotspotsTest { assertThat(facets.get(facet)).containsOnly(expectedEntries); } - private void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) { - List keys = searchAndReturnKeys(query); - assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys); - } - - private void assertThatSearchReturnsEmpty(IssueQuery.Builder query) { - List keys = searchAndReturnKeys(query); - assertThat(keys).isEmpty(); - } - - private List searchAndReturnKeys(IssueQuery.Builder query) { - return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); - } - } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java index 82bf992f1b8..8a7952c6e1c 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java @@ -23,34 +23,21 @@ import java.util.List; import java.util.Map; import java.util.OptionalInt; import java.util.stream.Collectors; -import org.junit.Rule; import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.server.es.EsTester; -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 org.sonar.server.view.index.ViewDoc; -import org.sonar.server.view.index.ViewIndexer; import static java.lang.Integer.parseInt; import static java.util.Arrays.asList; -import static java.util.Arrays.stream; import static java.util.Collections.singletonList; import static java.util.Comparator.comparing; -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.tuple; -import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017; import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; @@ -60,21 +47,7 @@ import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEF import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE; import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD; -public class IssueIndexSecurityReportsTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - 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()), null); - private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client()); - private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - - private IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon { @Test public void getOwaspTop10Report_dont_count_vulnerabilities_from_other_projects() { @@ -675,11 +648,6 @@ public class IssueIndexSecurityReportsTest { return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null); } - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } - private void indexView(String viewUuid, List projects) { viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects)); } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSortTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSortTest.java index 200a0010f3e..3fb1906468b 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSortTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSortTest.java @@ -19,52 +19,19 @@ */ package org.sonar.server.issue.index; -import java.util.Arrays; -import java.util.List; -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.sonar.api.impl.utils.TestSystem2; import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.server.es.EsTester; 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.TimeZone.getTimeZone; -import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.sonar.api.resources.Qualifiers.PROJECT; + import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexSortTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - private final System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00")); - @Rule - public DbTester db = DbTester.create(system2); - - private final AsyncIssueIndexing asyncIssueIndexing = mock(AsyncIssueIndexing.class); - private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), asyncIssueIndexing); - private final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - private final IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexSortTest extends IssueIndexTestCommon { @Test public void sort_by_status() { @@ -209,23 +176,4 @@ public class IssueIndexSortTest { assertThatSearchReturnsOnly(IssueQuery.builder(), "F3_1", "F1_2", "F1_1", "F1_3", "F2_1", "F2_2", "F2_3", "F3_2"); } - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } - - /** - * Execute the search request and return the document ids of results. - */ - private List searchAndReturnKeys(IssueQuery.Builder query) { - return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); - } - - private void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) { - List keys = searchAndReturnKeys(query); - assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys); - } - } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java index 3b887d76704..e62a4b63528 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java @@ -22,46 +22,29 @@ package org.sonar.server.issue.index; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.TotalHits.Relation; import org.assertj.core.groups.Tuple; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.SearchHit; -import org.junit.Rule; import org.junit.Test; -import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.issue.Issue; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; -import org.sonar.server.es.EsTester; 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.rule.index.RuleIndexer; -import org.sonar.server.tester.UserSessionRule; import static com.google.common.collect.ImmutableSortedSet.of; import static java.util.Arrays.asList; -import static java.util.Arrays.stream; import static java.util.Collections.emptyList; 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.assertj.core.api.Assertions.tuple; -import static org.mockito.Mockito.mock; import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; -import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.rules.RuleType.BUG; import static org.sonar.api.rules.RuleType.CODE_SMELL; import static org.sonar.api.rules.RuleType.VULNERABILITY; @@ -71,22 +54,7 @@ import static org.sonar.db.user.GroupTesting.newGroupDto; import static org.sonar.db.user.UserTesting.newUserDto; import static org.sonar.server.issue.IssueDocTesting.newDoc; -public class IssueIndexTest { - - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - private final System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00")); - @Rule - public DbTester db = DbTester.create(system2); - - private final AsyncIssueIndexing asyncIssueIndexing = mock(AsyncIssueIndexing.class); - private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), asyncIssueIndexing); - private final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient()); - private final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); - - private final IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); +public class IssueIndexTest extends IssueIndexTestCommon { @Test public void paging() { @@ -387,32 +355,7 @@ public class IssueIndexTest { return IssueQuery.builder().projectUuids(singletonList(projectUuid)).resolved(false).build(); } - private void indexIssues(IssueDoc... issues) { - issueIndexer.index(asList(issues).iterator()); - authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); - } - private void indexIssue(IssueDoc issue) { issueIndexer.index(Iterators.singletonIterator(issue)); } - - /** - * Execute the search request and return the document ids of results. - */ - private List searchAndReturnKeys(IssueQuery.Builder query) { - return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits()) - .map(SearchHit::getId) - .collect(Collectors.toList()); - } - - private void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) { - List keys = searchAndReturnKeys(query); - assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys); - } - - private void assertThatSearchReturnsEmpty(IssueQuery.Builder query) { - List keys = searchAndReturnKeys(query); - assertThat(keys).isEmpty(); - } - } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTestCommon.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTestCommon.java new file mode 100644 index 00000000000..ca097cc2e21 --- /dev/null +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexTestCommon.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.stream.Collectors; +import org.elasticsearch.search.SearchHit; +import org.junit.Rule; +import org.sonar.api.impl.utils.TestSystem2; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; +import org.sonar.server.es.EsTester; +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.rule.index.RuleIndexer; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.view.index.ViewIndexer; + +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static java.util.TimeZone.getTimeZone; +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.sonar.api.resources.Qualifiers.PROJECT; + +public class IssueIndexTestCommon { + + @Rule + public EsTester es = EsTester.create(); + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + protected final System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00")); + @Rule + public DbTester db = DbTester.create(system2); + + private final AsyncIssueIndexing asyncIssueIndexing = mock(AsyncIssueIndexing.class); + protected final IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), asyncIssueIndexing); + protected final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient()); + protected final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, issueIndexer); + protected final ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client()); + + protected final IssueIndex underTest = new IssueIndex(es.client(), system2, userSessionRule, new WebAuthorizationTypeSupport(userSessionRule)); + + /** + * Execute the search request and return the document ids of results. + */ + protected List searchAndReturnKeys(IssueQuery.Builder query) { + return Arrays.stream(underTest.search(query.build(), new SearchOptions()).getHits().getHits()) + .map(SearchHit::getId) + .collect(Collectors.toList()); + } + + protected void assertThatSearchReturnsOnly(IssueQuery.Builder query, String... expectedIssueKeys) { + List keys = searchAndReturnKeys(query); + assertThat(keys).containsExactlyInAnyOrder(expectedIssueKeys); + } + + protected void assertThatSearchReturnsEmpty(IssueQuery.Builder query) { + List keys = searchAndReturnKeys(query); + assertThat(keys).isEmpty(); + } + + protected void indexIssues(IssueDoc... issues) { + issueIndexer.index(asList(issues).iterator()); + authorizationIndexer.allow(stream(issues).map(issue -> new IndexPermissions(issue.projectUuid(), PROJECT).allowAnyone()).collect(toList())); + } + +} 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 462affd3087..9a825efd39a 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 @@ -112,6 +112,8 @@ 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_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; +import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PCI_DSS_40; import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST; import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS; @@ -147,6 +149,8 @@ public class SearchAction implements IssuesWsAction { PARAM_LANGUAGES, PARAM_TAGS, PARAM_TYPES, + PARAM_PCI_DSS_32, + PARAM_PCI_DSS_40, PARAM_OWASP_TOP_10, PARAM_OWASP_TOP_10_2021, PARAM_SANS_TOP_25, @@ -189,6 +193,8 @@ public class SearchAction implements IssuesWsAction { + "
When issue indexation is in progress returns 503 service unavailable HTTP code.") .setSince("3.6") .setChangelog( + new Change("9.6", "Added facets 'pciDss-3.2' and 'pciDss-4.0"), + new Change("9.6", "Added parameters 'pciDss-3.2' and 'pciDss-4.0"), new Change("9.6", "Response field 'ruleDescriptionContextKey' added"), new Change("9.6", "New possible value for 'additionalFields' parameter: 'ruleDescriptionContextKey'"), new Change("9.6", "Facet 'moduleUuids' is dropped."), @@ -261,6 +267,14 @@ public class SearchAction implements IssuesWsAction { .setSince("5.5") .setPossibleValues(ALL_RULE_TYPES_EXCEPT_SECURITY_HOTSPOTS) .setExampleValue(format("%s,%s", RuleType.CODE_SMELL, RuleType.BUG)); + action.createParam(PARAM_PCI_DSS_32) + .setDescription("Comma-separated list of PCI DSS v3.2 categories.") + .setSince("9.6") + .setExampleValue("4,6.5.8,10.1"); + action.createParam(PARAM_PCI_DSS_40) + .setDescription("Comma-separated list of PCI DSS v4.0 categories.") + .setSince("9.6") + .setExampleValue("4,6.5.8,10.1"); action.createParam(PARAM_OWASP_TOP_10) .setDescription("Comma-separated list of OWASP Top 10 2017 lowercase categories.") .setSince("7.3") @@ -466,6 +480,8 @@ public class SearchAction implements IssuesWsAction { setTypesFacet(facets); + addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_32, request.getPciDss32()); + addMandatoryValuesToFacet(facets, PARAM_PCI_DSS_40, request.getPciDss40()); 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()); @@ -485,9 +501,7 @@ public class SearchAction implements IssuesWsAction { Map buckets = facets.get(facetName); if (buckets != null && mandatoryValues != null) { for (String mandatoryValue : mandatoryValues) { - if (!buckets.containsKey(mandatoryValue)) { - buckets.put(mandatoryValue, 0L); - } + buckets.putIfAbsent(mandatoryValue, 0L); } } } @@ -542,6 +556,8 @@ public class SearchAction implements IssuesWsAction { .setStatuses(request.paramAsStrings(PARAM_STATUSES)) .setTags(request.paramAsStrings(PARAM_TAGS)) .setTypes(allRuleTypesExceptHotspotsIfEmpty(request.paramAsStrings(PARAM_TYPES))) + .setPciDss32(request.paramAsStrings(PARAM_PCI_DSS_32)) + .setPciDss40(request.paramAsStrings(PARAM_PCI_DSS_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 0d1d86a81e2..73573a7d074 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 @@ -214,8 +214,7 @@ public class SearchActionTest { .getIssuesList() .get(0) .getActions() - .getActionsList() - ).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN)); + .getActionsList()).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN)); response = ws.newRequest() .setParam(PARAM_ADDITIONAL_FIELDS, "actions") @@ -227,8 +226,7 @@ public class SearchActionTest { .getIssuesList() .get(0) .getActions() - .getActionsList() - ).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY)); + .getActionsList()).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY)); } @Test @@ -1048,6 +1046,234 @@ public class SearchActionTest { .containsExactly("82fd47d4-b650-4037-80bc-7b112bd4eac3", "82fd47d4-b650-4037-80bc-7b112bd4eac1", "82fd47d4-b650-4037-80bc-7b112bd4eac2"); } + @Test + public void only_vulnerabilities_are_returned_by_pciDss32() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + Consumer ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "pciDss-3.2:10.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + Consumer 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("pciDss-3.2", "10") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey()); + + result = ws.newRequest() + .setParam("pciDss-3.2", "10.1") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey()); + } + + @Test + public void multiple_categories_pciDss32() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + + // Rule 1 + Consumer ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "pciDss-3.2:10.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + Consumer 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)); + + // Rule 2 + ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("pciDss-4.0:6.5.3", "pciDss-3.2:1.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + hotspotRule = db.rules().insertHotspotRule(ruleConsumer); + db.issues().insertHotspot(hotspotRule, project, file, issueConsumer); + issueRule = db.rules().insertIssueRule(ruleConsumer); + IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + + // Rule 3 + ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("pciDss-4.0:6.5.3", "pciDss-3.2:2.3", "pciDss-3.2:10.1.2")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + hotspotRule = db.rules().insertHotspotRule(ruleConsumer); + db.issues().insertHotspot(hotspotRule, project, file, issueConsumer); + issueRule = db.rules().insertIssueRule(ruleConsumer); + IssueDto issueDto5 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + IssueDto issueDto6 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + + indexPermissionsAndIssues(); + + SearchWsResponse result = ws.newRequest() + .setParam("pciDss-3.2", "1,10") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(), issueDto6.getKey()); + + result = ws.newRequest() + .setParam("pciDss-3.2", "1") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto3.getKey(), issueDto4.getKey()); + + result = ws.newRequest() + .setParam("pciDss-3.2", "1,10,4") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(), issueDto6.getKey()); + + result = ws.newRequest() + .setParam("pciDss-3.2", "4") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + + result = ws.newRequest() + .setParam("pciDss-3.2", "4,7,12") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + + result = ws.newRequest() + .setParam("pciDss-3.2", "10.1") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey()); + } + + @Test + public void only_vulnerabilities_are_returned_by_pciDss40() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + Consumer ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-4.0:6.5.3", "pciDss-4.0:10.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + Consumer 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("pciDss-4.0", "10,6,5") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey()); + + result = ws.newRequest() + .setParam("pciDss-4.0", "10.1,6.5,5.5") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey()); + } + + @Test + public void multiple_categories_pciDss40() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + + // Rule 1 + Consumer ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-4.0:6.5.3", "pciDss-4.0:10.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + Consumer 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)); + + // Rule 2 + ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("pciDss-4.0:6.5.3", "pciDss-4.0:1.1")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + hotspotRule = db.rules().insertHotspotRule(ruleConsumer); + db.issues().insertHotspot(hotspotRule, project, file, issueConsumer); + issueRule = db.rules().insertIssueRule(ruleConsumer); + IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + + // Rule 3 + ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto + .setSecurityStandards(Sets.newHashSet("pciDss-3.2:6.5.3", "pciDss-4.0:2.3")) + .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql")); + hotspotRule = db.rules().insertHotspotRule(ruleConsumer); + db.issues().insertHotspot(hotspotRule, project, file, issueConsumer); + issueRule = db.rules().insertIssueRule(ruleConsumer); + IssueDto issueDto5 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + IssueDto issueDto6 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY)); + + indexPermissionsAndIssues(); + + SearchWsResponse result = ws.newRequest() + .setParam("pciDss-4.0", "1,10") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey()); + + result = ws.newRequest() + .setParam("pciDss-4.0", "1") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto3.getKey(), issueDto4.getKey()); + + result = ws.newRequest() + .setParam("pciDss-4.0", "1,10,4") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()) + .extracting(Issue::getKey) + .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey()); + + result = ws.newRequest() + .setParam("pciDss-4.0", "4") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + + result = ws.newRequest() + .setParam("pciDss-4.0", "4,7,12") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + } + @Test public void only_vulnerabilities_are_returned_by_cwe() { ComponentDto project = db.components().insertPublicProject(); @@ -1457,8 +1683,8 @@ 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", "owaspTop10", "owaspTop10-2021", "sansTop25", - "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod"); + "p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspTop10", + "owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod"); WebService.Param branch = def.param(PARAM_BRANCH); assertThat(branch.isInternal()).isFalse(); -- 2.39.5