diff options
4 files changed, 52 insertions, 25 deletions
diff --git a/build.gradle b/build.gradle index af234ba5c33..11095a9e5cf 100644 --- a/build.gradle +++ b/build.gradle @@ -515,7 +515,7 @@ subprojects { dependency('org.sonarsource.orchestrator:sonar-orchestrator-junit5:5.6.2.2625') { exclude 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' } - dependency 'com.sonarsource.pdfreport:security-report-pdf-generation:2.0.0.208' + dependency 'com.sonarsource.pdfreport:security-report-pdf-generation:2.0.0.229' dependency 'com.sonarsource.fixsuggestions:ai-suggestions-shared:1.0.0.1312' dependency 'org.sonarsource.update-center:sonar-update-center-common:1.35.0.2835' dependency 'org.sonarsource.classloader:sonar-classloader:1.1.0.1059' diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java index 484b03f1918..903fe411831 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java @@ -20,6 +20,7 @@ package org.sonar.server.issue.index; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import javax.annotation.Nullable; @@ -38,9 +39,11 @@ public class SecurityStandardCategoryStatistics { private boolean hasMoreRules; private final Optional<String> version; private Optional<String> level = Optional.empty(); + private final Map<String, Long> severityDistribution; public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long toReviewSecurityHotspots, - long reviewedSecurityHotspots, Integer securityReviewRating, @Nullable List<SecurityStandardCategoryStatistics> children, @Nullable String version) { + long reviewedSecurityHotspots, Integer securityReviewRating, @Nullable List<SecurityStandardCategoryStatistics> children, @Nullable String version, + Map<String, Long> severityDistribution) { this.category = category; this.vulnerabilities = vulnerabilities; this.vulnerabilityRating = vulnerabiliyRating; @@ -50,6 +53,7 @@ public class SecurityStandardCategoryStatistics { this.children = children; this.version = Optional.ofNullable(version); this.hasMoreRules = false; + this.severityDistribution = severityDistribution; } public SecurityStandardCategoryStatistics withModifiedVulnerabilities( @@ -71,7 +75,8 @@ public class SecurityStandardCategoryStatistics { this.getReviewedSecurityHotspots(), this.getSecurityReviewRating(), this.getChildren(), - this.getVersion().orElse(null)); + this.getVersion().orElse(null), + this.getSeverityDistribution()); } public String getCategory() { @@ -140,4 +145,8 @@ public class SecurityStandardCategoryStatistics { public void setHasMoreRules(boolean hasMoreRules) { this.hasMoreRules = hasMoreRules; } + + public Map<String, Long> getSeverityDistribution() { + return severityDistribution; + } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/SecurityStandardCategoryStatisticsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/SecurityStandardCategoryStatisticsTest.java index 36638caf954..a23c45cd248 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/SecurityStandardCategoryStatisticsTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/SecurityStandardCategoryStatisticsTest.java @@ -20,6 +20,7 @@ package org.sonar.server.issue.index; import java.util.ArrayList; +import java.util.Map; import java.util.OptionalInt; import org.junit.Test; @@ -33,7 +34,7 @@ public class SecurityStandardCategoryStatisticsTest { public void hasMoreRules_default_false() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 0, empty(), 0, - 0, 5, null, null + 0, 5, null, null, Map.of() ); assertThat(standardCategoryStatistics.hasMoreRules()).isFalse(); } @@ -42,7 +43,7 @@ public class SecurityStandardCategoryStatisticsTest { public void hasMoreRules_is_updatable() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 0, empty(), 0, - 0, 5, null, null + 0, 5, null, null, Map.of() ); standardCategoryStatistics.setHasMoreRules(true); assertThat(standardCategoryStatistics.hasMoreRules()).isTrue(); @@ -52,7 +53,7 @@ public class SecurityStandardCategoryStatisticsTest { public void test_getters() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 1, empty(), 0, - 0, 5, new ArrayList<>(), "version" + 0, 5, new ArrayList<>(), "version", Map.of() ).setLevel("1"); standardCategoryStatistics.setActiveRules(3); @@ -71,13 +72,14 @@ public class SecurityStandardCategoryStatisticsTest { assertThat(standardCategoryStatistics.getVersion().get()).contains("version"); assertThat(standardCategoryStatistics.getLevel().get()).contains("1"); assertThat(standardCategoryStatistics.hasMoreRules()).isFalse(); + assertThat(standardCategoryStatistics.getSeverityDistribution()).isEmpty(); } @Test public void withModifiedVulnerabilities() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 1, empty(), 0, - 0, 5, null, null + 0, 5, null, null, Map.of() ); SecurityStandardCategoryStatistics modified = standardCategoryStatistics.withModifiedVulnerabilities(2, 3); @@ -91,7 +93,7 @@ public class SecurityStandardCategoryStatisticsTest { public void withModifiedVulnerabilities_noNewRating() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 1, OptionalInt.of(1), 0, - 0, 5, null, null + 0, 5, null, null, Map.of() ); SecurityStandardCategoryStatistics modified = standardCategoryStatistics.withModifiedVulnerabilities(2, null); @@ -105,7 +107,7 @@ public class SecurityStandardCategoryStatisticsTest { public void withModifiedVulnerabilities_usesLowestRating() { SecurityStandardCategoryStatistics standardCategoryStatistics = new SecurityStandardCategoryStatistics( "cat", 1, OptionalInt.of(5), 0, - 0, 5, null, null + 0, 5, null, null, Map.of() ); SecurityStandardCategoryStatistics modified = standardCategoryStatistics.withModifiedVulnerabilities(2, 3); 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 fd408c6c26a..9235bc57b1a 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 @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -54,6 +55,7 @@ import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.HasAggregations; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator; import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter; @@ -1292,7 +1294,7 @@ public class IssueIndex { } private static SecurityStandardCategoryStatistics emptyCweStatistics(String rule) { - return new SecurityStandardCategoryStatistics(rule, 0, OptionalInt.of(1), 0, 0, 1, null, null); + return new SecurityStandardCategoryStatistics(rule, 0, OptionalInt.of(1), 0, 0, 1, null, null, Map.of()); } public List<SecurityStandardCategoryStatistics> getSonarSourceReport(String projectUuid, boolean isViewOrApp, boolean includeCwe) { @@ -1448,10 +1450,11 @@ public class IssueIndex { Aggregation severitiesAggregations = ((ParsedFilter) categoryBucket.getAggregations().get(AGG_VULNERABILITIES)).getAggregations().get(AGG_SEVERITIES); - CountAndRating countAndRating = getCountAndRating(severitiesAggregations); - long vulnerabilities = countAndRating.getCount(); + SeverityAggregationDetails severityAggregationDetails = getSeverityDetails(severitiesAggregations); + long vulnerabilities = severityAggregationDetails.getCount(); // Worst severity having at least one issue - OptionalInt severityRating = countAndRating.getRating(); + OptionalInt severityRating = severityAggregationDetails.getRating(); + Map<String, Long> severityDistribution = severityAggregationDetails.getDistribution(); long toReviewSecurityHotspots = ((ParsedValueCount) ((ParsedFilter) categoryBucket.getAggregations().get(AGG_TO_REVIEW_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT)) .getValue(); @@ -1462,32 +1465,39 @@ public class IssueIndex { Integer securityReviewRating = computeRating(percent.orElse(null)).getIndex(); return new SecurityStandardCategoryStatistics(categoryName, vulnerabilities, severityRating, toReviewSecurityHotspots, - reviewedSecurityHotspots, securityReviewRating, children, version); + reviewedSecurityHotspots, securityReviewRating, children, version, severityDistribution); } - private CountAndRating getCountAndRating(Aggregation severitiesAggregations) { + private SeverityAggregationDetails getSeverityDetails(Aggregation severitiesAggregations) { + List<? extends Terms.Bucket> severityBuckets; + long vulnerabilities; + OptionalInt severityRating; if (isMQRMode()) { - List<? extends Terms.Bucket> severityBuckets = + severityBuckets = ((ParsedStringTerms) ((ParsedFilter) ((ParsedNested) severitiesAggregations).getAggregations().get(ISSUES_WITH_SECURITY_IMPACT)).getAggregations().get(AGG_IMPACT_SEVERITIES)).getBuckets(); - long vulnerabilities = + vulnerabilities = severityBuckets.stream().mapToLong(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue()).sum(); // Worst severity having at least one issue - OptionalInt severityRating = severityBuckets.stream() + severityRating = severityBuckets.stream() .filter(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue() != 0) .mapToInt(b -> org.sonar.api.issue.impact.Severity.valueOf(b.getKeyAsString()).ordinal() + 1) .max(); - return new CountAndRating(vulnerabilities, severityRating); } else { - List<? extends Terms.Bucket> severityBuckets = ((ParsedStringTerms) severitiesAggregations).getBuckets(); - long vulnerabilities = + severityBuckets = ((ParsedStringTerms) severitiesAggregations).getBuckets(); + vulnerabilities = severityBuckets.stream().mapToLong(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue()).sum(); // Worst severity having at least one issue - OptionalInt severityRating = severityBuckets.stream() + severityRating = severityBuckets.stream() .filter(b -> ((ParsedValueCount) b.getAggregations().get(AGG_COUNT)).getValue() != 0) .mapToInt(b -> Severity.ALL.indexOf(b.getKeyAsString()) + 1) .max(); - return new CountAndRating(vulnerabilities, severityRating); } + Map<String, Long> severityDistribution = severityBuckets.stream() + .collect(Collectors.toMap( + e -> e.getKeyAsString().toLowerCase(Locale.US), + MultiBucketsAggregation.Bucket::getDocCount + )); + return new SeverityAggregationDetails(vulnerabilities, severityRating, severityDistribution); } private AggregationBuilder newSecurityReportSubAggregations(AggregationBuilder categoriesAggs, String securityStandardVersionPrefix) { @@ -1578,13 +1588,15 @@ public class IssueIndex { } - private static class CountAndRating { + private static class SeverityAggregationDetails { private long count; private OptionalInt rating; + private Map<String, Long> distribution; - public CountAndRating(long count, OptionalInt rating) { + public SeverityAggregationDetails(long count, OptionalInt rating, Map<String, Long> distribution) { this.count = count; this.rating = rating; + this.distribution = distribution; } public long getCount() { @@ -1594,5 +1606,9 @@ public class IssueIndex { public OptionalInt getRating() { return rating; } + + public Map<String, Long> getDistribution() { + return distribution; + } } } |