]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17404 group Owasp ASVS issues by level in security report PDF
authorMatteo Mara <matteo.mara@sonarsource.com>
Fri, 7 Oct 2022 09:35:29 +0000 (11:35 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 10 Oct 2022 20:03:09 +0000 (20:03 +0000)
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java

index 7cffd0d083aa9b6d8a0b5fe566d50a76d4284e31..84af82f9e5ac047fbd11c6aeb105cf3951dc4178 100644 (file)
@@ -1174,6 +1174,17 @@ public class IssueIndex {
     return searchWithDistribution(request, version.label(), level);
   }
 
+  public List<SecurityStandardCategoryStatistics> getOwaspAsvsReportGroupedByLevel(String projectUuid, boolean isViewOrApp, RulesDefinition.OwaspAsvsVersion version, int level) {
+    SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
+    request.aggregation(
+      newSecurityReportSubAggregations(
+        AggregationBuilders.filter(
+          "l" + level,
+          boolQuery().filter(termsQuery(version.prefix(), SecurityStandards.OWASP_ASVS_REQUIREMENTS_BY_LEVEL.get(version).get(level)))),
+        version.prefix()));
+    return searchWithLevelDistribution(request, version.label(), Integer.toString(level));
+  }
+
   public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe, OwaspTop10Version version) {
     SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
     IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i)
@@ -1185,24 +1196,33 @@ public class IssueIndex {
     return search(request, includeCwe, version.label());
   }
 
+  private List<SecurityStandardCategoryStatistics> searchWithLevelDistribution(SearchSourceBuilder sourceBuilder, String version, @Nullable String level) {
+    return getSearchResponse(sourceBuilder)
+      .getAggregations().asList().stream()
+      .map(c -> processSecurityReportIssueSearchResultsWithLevelDistribution((ParsedFilter) c, version, level))
+      .collect(MoreCollectors.toList());
+  }
+
   private List<SecurityStandardCategoryStatistics> searchWithDistribution(SearchSourceBuilder sourceBuilder, String version, @Nullable Integer level) {
-    SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
-      .source(sourceBuilder);
-    SearchResponse response = client.search(request);
-    return response.getAggregations().asList().stream()
+    return getSearchResponse(sourceBuilder)
+      .getAggregations().asList().stream()
       .map(c -> processSecurityReportIssueSearchResultsWithDistribution((ParsedFilter) c, version, level))
       .collect(MoreCollectors.toList());
   }
 
   private List<SecurityStandardCategoryStatistics> search(SearchSourceBuilder sourceBuilder, boolean includeDistribution, @Nullable String version) {
-    SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
-      .source(sourceBuilder);
-    SearchResponse response = client.search(request);
-    return response.getAggregations().asList().stream()
+    return getSearchResponse(sourceBuilder)
+      .getAggregations().asList().stream()
       .map(c -> processSecurityReportIssueSearchResults((ParsedFilter) c, includeDistribution, version))
       .collect(MoreCollectors.toList());
   }
 
+  private SearchResponse getSearchResponse(SearchSourceBuilder sourceBuilder) {
+    SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
+      .source(sourceBuilder);
+    return client.search(request);
+  }
+
   private static SecurityStandardCategoryStatistics processSecurityReportIssueSearchResultsWithDistribution(ParsedFilter categoryFilter, String version, @Nullable Integer level) {
     var list = ((ParsedStringTerms) categoryFilter.getAggregations().get(AGG_DISTRIBUTION)).getBuckets();
     List<SecurityStandardCategoryStatistics> children = list.stream()
@@ -1214,6 +1234,16 @@ public class IssueIndex {
     return processSecurityReportCategorySearchResults(categoryFilter, categoryFilter.getName(), children, version);
   }
 
+  private static SecurityStandardCategoryStatistics processSecurityReportIssueSearchResultsWithLevelDistribution(ParsedFilter categoryFilter, String version, String level) {
+    var list = ((ParsedStringTerms) categoryFilter.getAggregations().get(AGG_DISTRIBUTION)).getBuckets();
+    List<SecurityStandardCategoryStatistics> children = list.stream()
+      .filter(categoryBucket -> OWASP_ASVS_40_REQUIREMENTS_BY_LEVEL.get(Integer.parseInt(level)).contains(categoryBucket.getKeyAsString()))
+      .map(categoryBucket -> processSecurityReportCategorySearchResults(categoryBucket, categoryBucket.getKeyAsString(), null, null))
+      .collect(toList());
+
+    return processSecurityReportCategorySearchResults(categoryFilter, categoryFilter.getName(), children, version);
+  }
+
   private static SecurityStandardCategoryStatistics processSecurityReportIssueSearchResults(ParsedFilter categoryBucket, boolean includeDistribution, String version) {
     List<SecurityStandardCategoryStatistics> children = new ArrayList<>();
     if (includeDistribution) {
index 44ddc775f4af0c4f474dbf1879abf3ac3e259782..baeaccf0d79110adf17c2a2da3b8628ce02db7da 100644 (file)
  */
 package org.sonar.server.issue.index;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.OptionalInt;
 import java.util.stream.Collectors;
+import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.Severity;
@@ -213,10 +215,10 @@ public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
 
     assertThat(owaspAsvsReport.get(0).getChildren()).isEmpty();
     assertThat(owaspAsvsReport.get(1).getChildren()).hasSize(2);
-    assertThat(owaspAsvsReport.get(2).getChildren()).hasSize(3);
+    assertThat(owaspAsvsReport.get(2).getChildren()).hasSize(4);
     assertThat(owaspAsvsReport.get(3).getChildren()).isEmpty();
     assertThat(owaspAsvsReport.get(4).getChildren()).isEmpty();
-    assertThat(owaspAsvsReport.get(5).getChildren()).hasSize(1);
+    assertThat(owaspAsvsReport.get(5).getChildren()).hasSize(2);
     assertThat(owaspAsvsReport.get(6).getChildren()).hasSize(1);
     assertThat(owaspAsvsReport.get(7).getChildren()).hasSize(1);
     assertThat(owaspAsvsReport.get(8).getChildren()).isEmpty();
@@ -227,6 +229,18 @@ public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
     assertThat(owaspAsvsReport.get(13).getChildren()).isEmpty();
   }
 
+  @Test
+  public void getOwaspAsvs40ReportGroupedByLevel_aggregation() {
+    List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = indexIssuesAndAssertOwaspAsvsReportGroupedByLevel();
+
+    assertThat(owaspAsvsReportGroupedByLevel)
+      .isNotEmpty();
+
+    assertThat(owaspAsvsReportGroupedByLevel.get(0).getChildren()).hasSize(3);
+    assertThat(owaspAsvsReportGroupedByLevel.get(1).getChildren()).hasSize(7);
+    assertThat(owaspAsvsReportGroupedByLevel.get(2).getChildren()).hasSize(11);
+  }
+
   @Test
   public void getOwaspTop10Report_aggregation_with_cwe() {
     List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
@@ -349,23 +363,9 @@ public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
   }
 
   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReport() {
-    ComponentDto project = newPrivateProjectDto();
-    indexIssues(
-      newDoc("openvul1", project).setOwaspAsvs40(asList("2.1.1", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
-        .setSeverity(Severity.MAJOR),
-      newDoc("openvul2", project).setOwaspAsvs40(asList("3.2.2", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
-        .setSeverity(Severity.MINOR),
-      newDoc("openvul3", project).setOwaspAsvs40(asList("10.3.1", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
-        .setSeverity(Severity.MINOR),
-      newDoc("notowaspasvsvul", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
-      newDoc("toreviewhotspot1", project).setOwaspAsvs40(asList("2.1.2", "3.2.2")).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_TO_REVIEW),
-      newDoc("toreviewhotspot2", project).setOwaspAsvs40(asList("3.4.5", "7.1.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
-      newDoc("reviewedHotspot", project).setOwaspAsvs40(asList("3.1.1", "8.3.4")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
-        .setResolution(Issue.RESOLUTION_FIXED),
-      newDoc("notowaspasvshotspot", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
+    ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
 
-    List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0, 1).stream()
+    List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
       .sorted(comparing(s -> parseInt(s.getCategory())))
       .collect(toList());
     assertThat(owaspAsvsReport)
@@ -391,6 +391,52 @@ public class IssueIndexSecurityReportsTest extends IssueIndexTestCommon {
     return owaspAsvsReport;
   }
 
+  private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReportGroupedByLevel() {
+    ComponentDto project = getProjectWithOwaspAsvsIssuesIndexed();
+
+    List<SecurityStandardCategoryStatistics> owaspAsvsReportGroupedByLevel = new ArrayList<>();
+    owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 1).stream()
+      .sorted(comparing(s -> parseInt(s.getCategory())))
+      .collect(toList()));
+    owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 2).stream()
+      .sorted(comparing(s -> parseInt(s.getCategory())))
+      .collect(toList()));
+    owaspAsvsReportGroupedByLevel.addAll(underTest.getOwaspAsvsReportGroupedByLevel(project.uuid(), false, OwaspAsvsVersion.V4_0, 3).stream()
+      .sorted(comparing(s -> parseInt(s.getCategory())))
+      .collect(toList()));
+
+    assertThat(owaspAsvsReportGroupedByLevel)
+      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
+        SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+        SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
+      .containsExactlyInAnyOrder(
+        tuple("l1", 1L /* openvul2 */, OptionalInt.of(2), 1L /* toreviewhotspot2 */, 0L, 5),
+        tuple("l2", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L /* toreviewhotspot1, toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
+        tuple("l3", 3L /* openvul1,openvul2,openvul3 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4));
+
+    return owaspAsvsReportGroupedByLevel;
+  }
+
+  @NotNull
+  private ComponentDto getProjectWithOwaspAsvsIssuesIndexed() {
+    ComponentDto project = newPrivateProjectDto();
+    indexIssues(
+      newDoc("openvul1", project).setOwaspAsvs40(asList("2.4.1", "3.2.4")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+        .setSeverity(Severity.MAJOR),
+      newDoc("openvul2", project).setOwaspAsvs40(asList("3.4.5", "6.2.1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+        .setSeverity(Severity.MINOR),
+      newDoc("openvul3", project).setOwaspAsvs40(asList("10.2.4", "6.2.8")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+        .setSeverity(Severity.MINOR),
+      newDoc("notowaspasvsvul", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
+      newDoc("toreviewhotspot1", project).setOwaspAsvs40(asList("2.2.5", "3.2.4")).setType(RuleType.SECURITY_HOTSPOT)
+        .setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("toreviewhotspot2", project).setOwaspAsvs40(asList("3.6.1", "7.1.1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("reviewedHotspot", project).setOwaspAsvs40(asList("3.3.3", "8.3.7")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
+        .setResolution(Issue.RESOLUTION_FIXED),
+      newDoc("notowaspasvshotspot", project).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
+    return project;
+  }
+
   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
     ComponentDto project = newPrivateProjectDto();
     indexIssues(