]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10978 Fix support of branches and applications for security reports
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 11 Jul 2018 11:14:07 +0000 (13:14 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 17 Jul 2018 18:21:25 +0000 (20:21 +0200)
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
server/sonar-server/src/main/java/org/sonar/server/securityreport/ws/ShowAction.java
server/sonar-server/src/test/java/org/sonar/server/securityreport/ws/ShowActionTest.java

index a9794254c7733fb3a9aad91ee91ada9bbaa1bcb0..48bbecfb3c3a1f6dabe999d24e7f11101ecaf471 100644 (file)
@@ -798,8 +798,8 @@ public class IssueIndex {
       .collect(MoreCollectors.toList(branchUuids.size()));
   }
 
-  public List<SecurityStandardCategoryStatistics> getSansTop25Report(String projectUuid, boolean includeCwe) {
-    SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid);
+  public List<SecurityStandardCategoryStatistics> getSansTop25Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
+    SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
     Stream.of(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES).forEach(sansCategory -> {
       AggregationBuilder sansCategoryAggs = AggregationBuilders
         .filter(sansCategory, boolQuery()
@@ -809,8 +809,8 @@ public class IssueIndex {
     return processSecurityReportSearchResults(request, includeCwe);
   }
 
-  public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean includeCwe) {
-    SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid);
+  public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
+    SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
     Stream.concat(IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i), Stream.of(UNKNOWN_STANDARD)).forEach(owaspCategory -> {
       AggregationBuilder owaspCategoryAggs = AggregationBuilders
         .filter(owaspCategory, boolQuery()
@@ -898,11 +898,21 @@ public class IssueIndex {
           AggregationBuilders.count("count").field(IssueIndexDefinition.FIELD_ISSUE_KEY)));
   }
 
-  private SearchRequestBuilder prepareNonClosedVulnerabilitiesAndHotspotSearch(String projectUuid) {
+  private SearchRequestBuilder prepareNonClosedVulnerabilitiesAndHotspotSearch(String projectUuid, boolean isViewOrApp) {
+    BoolQueryBuilder componentFilter = boolQuery();
+    if (isViewOrApp) {
+      componentFilter.filter(QueryBuilders.termsLookupQuery(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID,
+        new TermsLookup(
+          ViewIndexDefinition.INDEX_TYPE_VIEW.getIndex(),
+          ViewIndexDefinition.INDEX_TYPE_VIEW.getType(),
+          projectUuid,
+          ViewIndexDefinition.FIELD_PROJECTS)));
+    } else {
+      componentFilter.filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, projectUuid));
+    }
     return client.prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
       .setQuery(
-        boolQuery()
-          .filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectUuid))
+        componentFilter
           .filter(termsQuery(IssueIndexDefinition.FIELD_ISSUE_TYPE, RuleType.SECURITY_HOTSPOT.name(), RuleType.VULNERABILITY.name()))
           .mustNot(termQuery(IssueIndexDefinition.FIELD_ISSUE_STATUS, Issue.STATUS_CLOSED)))
       .setSize(0);
index ea07a6587bf4de3b5dc04d8e5ac355c5952e20ff..eddde5f4622b44752aaa982fb29c30439bbfc020 100644 (file)
@@ -1459,7 +1459,7 @@ public class IssueIndexTest {
       newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
       newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
         SecurityStandardCategoryStatistics::getVulnerabiliyRating)
@@ -1477,7 +1477,7 @@ public class IssueIndexTest {
       newDoc("notopenvul", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
         .setSeverity(Severity.BLOCKER));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
         SecurityStandardCategoryStatistics::getVulnerabiliyRating)
@@ -1493,7 +1493,7 @@ public class IssueIndexTest {
       // Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
       newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
         SecurityStandardCategoryStatistics::getVulnerabiliyRating)
@@ -1510,7 +1510,7 @@ public class IssueIndexTest {
       newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN),
       newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
       .contains(
@@ -1526,7 +1526,7 @@ public class IssueIndexTest {
       newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
         .setResolution(Issue.RESOLUTION_FIXED));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
       .contains(
@@ -1583,7 +1583,7 @@ public class IssueIndexTest {
         .setResolution(Issue.RESOLUTION_WONT_FIX),
       newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
 
-    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), includeCwe);
+    List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
         SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
@@ -1627,7 +1627,7 @@ public class IssueIndexTest {
         .setResolution(Issue.RESOLUTION_WONT_FIX),
       newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
 
-    List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false);
+    List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
     assertThat(sansTop25Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
         SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
@@ -1641,6 +1641,49 @@ public class IssueIndexTest {
     assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
   }
 
+  @Test
+  public void test_getSansTop25Report_aggregation_on_portfolio() {
+    ComponentDto portfolio1 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+    ComponentDto portfolio2 = db.components().insertPrivateApplication(db.getDefaultOrganization());
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ComponentDto project2 = db.components().insertPrivateProject();
+
+    indexIssues(
+      newDoc("openvul1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+        .setSeverity(Severity.MAJOR),
+      newDoc("openvul2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+        .setSeverity(Severity.MINOR),
+      newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
+        .setResolution(Issue.RESOLUTION_FIXED)
+        .setSeverity(Severity.BLOCKER),
+      newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+        .setSeverity(Severity.CRITICAL),
+      newDoc("openhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
+        .setStatus(Issue.STATUS_OPEN),
+      newDoc("openhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
+        .setStatus(Issue.STATUS_REOPENED),
+      newDoc("toReviewHotspot", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
+        .setResolution(Issue.RESOLUTION_FIXED),
+      newDoc("WFHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
+        .setResolution(Issue.RESOLUTION_WONT_FIX),
+      newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+
+    indexView(portfolio1.uuid(), singletonList(project1.uuid()));
+    indexView(portfolio2.uuid(), singletonList(project2.uuid()));
+
+    List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
+    assertThat(sansTop25Report)
+      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
+        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
+        SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+      .containsExactlyInAnyOrder(
+        tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* openhotspot1 */, 0L, 0L),
+        tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* openhotspot1 */, 1L /* toReviewHotspot */, 0L),
+        tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 0L));
+
+    assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
+  }
+
   private void addIssues(ComponentDto component, int bugs, int vulnerabilities, int codeSmelles) {
     List<IssueDoc> issues = new ArrayList<>();
     IntStream.range(0, bugs).forEach(b -> issues.add(newDoc(component).setType(BUG).setResolution(null)));
index 5f5ea1303754f1b07e09963c9f793dc7992d670b..280e9100227de1eeea98873f06f5703ddb883949 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.securityreport.ws;
 
 import java.util.List;
 import java.util.function.Function;
+import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -72,7 +73,7 @@ public class ShowAction implements SecurityReportsWsAction {
       .setInternal(true);
 
     action.createParam(PARAM_PROJECT)
-      .setDescription("Project key")
+      .setDescription("Project, view or application key")
       .setRequired(true);
     action.createParam(PARAM_BRANCH)
       .setDescription("Branch name")
@@ -95,18 +96,32 @@ public class ShowAction implements SecurityReportsWsAction {
       projectDto = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, request.param(PARAM_BRANCH), null);
     }
     userSession.checkComponentPermission(USER, projectDto);
+    String qualifier = projectDto.qualifier();
+    boolean isViewOrApp;
+    switch (qualifier) {
+      case Qualifiers.VIEW:
+      case Qualifiers.SUBVIEW:
+      case Qualifiers.APP:
+        isViewOrApp = true;
+        break;
+      case Qualifiers.PROJECT:
+        isViewOrApp = false;
+        break;
+      default:
+        throw new IllegalArgumentException("Unsupported component type " + qualifier);
+    }
     String standard = request.mandatoryParam(PARAM_STANDARD);
     boolean includeCwe = request.mandatoryParamAsBoolean(PARAM_INCLUDE_DISTRIBUTION);
     switch (standard) {
       case PARAM_OWASP_TOP_10:
-        List<SecurityStandardCategoryStatistics> owaspCategories = issueIndex.getOwaspTop10Report(projectDto.uuid(), includeCwe)
+        List<SecurityStandardCategoryStatistics> owaspCategories = issueIndex.getOwaspTop10Report(projectDto.uuid(), isViewOrApp, includeCwe)
           .stream()
           .sorted(comparing(ShowAction::index))
           .collect(toList());
         writeResponse(request, response, owaspCategories);
         break;
       case PARAM_SANS_TOP_25:
-        writeResponse(request, response, issueIndex.getSansTop25Report(projectDto.uuid(), includeCwe));
+        writeResponse(request, response, issueIndex.getSansTop25Report(projectDto.uuid(), isViewOrApp, includeCwe));
         break;
       default:
         throw new IllegalArgumentException("Unsupported standard: '" + standard + "'");
index 9cd3054a64f670e1d4edf2402b506d3968fdbb0b..d87519555eeefdecbe7fd7db99b8af3337cc8110 100644 (file)
@@ -207,7 +207,6 @@ public class ShowActionTest {
 
   @Test
   public void sans_with_cwe() {
-
     ComponentDto project = insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "PROJECT_ID").setDbKey("PROJECT_KEY"));
     userSessionRule.addProjectPermission(UserRole.USER, project);
     indexPermissions();
@@ -244,6 +243,49 @@ public class ShowActionTest {
       .isSimilarTo(this.getClass().getResource("ShowActionTest/sansWithCwe.json"));
   }
 
+  @Test
+  public void sans_with_cwe_for_branches() {
+    ComponentDto project1 = db.components().insertPrivateProject(p -> p.setDbKey("prj1"));
+    ComponentDto project1Branch1 = db.components().insertProjectBranch(project1);
+    ComponentDto fileOnProject1Branch1 = db.components().insertComponent(newFileDto(project1Branch1));
+    ComponentDto project2 = db.components().insertPrivateProject(p -> p.setDbKey("prj2"));
+
+    userSessionRule.addProjectPermission(UserRole.USER, project1);
+    userSessionRule.addProjectPermission(UserRole.USER, project2);
+    indexPermissions();
+    RuleDefinitionDto rule = newRule();
+    IssueDto issue1 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+      .setStatus("OPEN")
+      .setSeverity("MAJOR")
+      .setType(RuleType.VULNERABILITY);
+    IssueDto issue2 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+      .setStatus("OPEN")
+      .setSeverity("MAJOR")
+      .setType(RuleType.SECURITY_HOTSPOT);
+    IssueDto issue3 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+      .setStatus(Issue.STATUS_RESOLVED)
+      .setResolution(Issue.RESOLUTION_FIXED)
+      .setSeverity("MAJOR")
+      .setType(RuleType.SECURITY_HOTSPOT);
+    IssueDto issue4 = newIssue(rule, project1Branch1, fileOnProject1Branch1)
+      .setStatus(Issue.STATUS_RESOLVED)
+      .setResolution(Issue.RESOLUTION_WONT_FIX)
+      .setSeverity("MAJOR")
+      .setType(RuleType.SECURITY_HOTSPOT);
+    dbClient.issueDao().insert(session, issue1, issue2, issue3, issue4);
+    session.commit();
+    indexIssues();
+
+    assertJson(ws.newRequest()
+      .setParam("standard", "sansTop25")
+      .setParam("project", project1Branch1.getKey())
+      .setParam("branch", project1Branch1.getBranch())
+      .setParam("includeDistribution", "true")
+      .execute().getInput())
+      .withStrictArrayOrder()
+      .isSimilarTo(this.getClass().getResource("ShowActionTest/sansWithCwe.json"));
+  }
+
 
   private RuleDefinitionDto newRule() {
     RuleDefinitionDto rule = RuleTesting.newRule()