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.OwaspAsvsVersion;
import org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version;
import org.sonar.api.server.rule.RulesDefinition.PciDssVersion;
import org.sonar.api.utils.DateUtils;
import org.sonar.server.permission.index.AuthorizationDoc;
import org.sonar.server.permission.index.WebAuthorizationTypeSupport;
import org.sonar.server.security.SecurityStandards;
+import org.sonar.server.security.SecurityStandards.OwaspAsvs;
import org.sonar.server.security.SecurityStandards.PciDss;
import org.sonar.server.security.SecurityStandards.SQCategory;
import org.sonar.server.user.UserSession;
SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
Arrays.stream(PciDss.values())
.forEach(pciDss -> request.aggregation(
- newPciDssSecurityReportSubAggregations(
- AggregationBuilders.filter(pciDss.category(), boolQuery().filter(prefixQuery(version.prefix(), pciDss.category() + "."))), version)));
- return searchPciDss(request, version.label());
+ newSecurityReportSubAggregations(
+ AggregationBuilders.filter(pciDss.category(), boolQuery().filter(prefixQuery(version.prefix(), pciDss.category() + "."))), version.prefix())));
+ return searchWithDistribution(request, version.label());
+ }
+
+ public List<SecurityStandardCategoryStatistics> getOwaspAsvsReport(String projectUuid, boolean isViewOrApp, OwaspAsvsVersion version) {
+ SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
+ Arrays.stream(OwaspAsvs.values())
+ .forEach(owaspAsvs -> request.aggregation(
+ newSecurityReportSubAggregations(
+ AggregationBuilders.filter(owaspAsvs.category(), boolQuery().filter(prefixQuery(version.prefix(), owaspAsvs.category() + "."))), version.prefix())));
+ return searchWithDistribution(request, version.label());
}
public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe, OwaspTop10Version version) {
return search(request, includeCwe, version.label());
}
- private List<SecurityStandardCategoryStatistics> searchPciDss(SearchSourceBuilder sourceBuilder, String version) {
+ private List<SecurityStandardCategoryStatistics> searchWithDistribution(SearchSourceBuilder sourceBuilder, String version) {
SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
.source(sourceBuilder);
SearchResponse response = client.search(request);
return response.getAggregations().asList().stream()
- .map(c -> processPciDssSecurityReportIssueSearchResults((ParsedFilter) c, version))
+ .map(c -> processSecurityReportIssueSearchResultsWithDistribution((ParsedFilter) c, version))
.collect(MoreCollectors.toList());
}
.collect(MoreCollectors.toList());
}
- private static SecurityStandardCategoryStatistics processPciDssSecurityReportIssueSearchResults(ParsedFilter categoryFilter, String version) {
+ private static SecurityStandardCategoryStatistics processSecurityReportIssueSearchResultsWithDistribution(ParsedFilter categoryFilter, String version) {
Stream<? extends Terms.Bucket> stream = ((ParsedStringTerms) categoryFilter.getAggregations().get(AGG_DISTRIBUTION)).getBuckets().stream();
var children = stream.filter(categoryBucket -> StringUtils.startsWith(categoryBucket.getKeyAsString(), categoryFilter.getName() + "."))
.map(categoryBucket -> processSecurityReportCategorySearchResults(categoryBucket, categoryBucket.getKeyAsString(), null, null)).collect(toList());
reviewedSecurityHotspots, securityReviewRating, children, version);
}
- private static AggregationBuilder newPciDssSecurityReportSubAggregations(AggregationBuilder categoriesAggs, PciDssVersion version) {
+ private static AggregationBuilder newSecurityReportSubAggregations(AggregationBuilder categoriesAggs, String securityStandardVersionPrefix) {
AggregationBuilder aggregationBuilder = addSecurityReportIssueCountAggregations(categoriesAggs);
final TermsAggregationBuilder distributionAggregation = AggregationBuilders.terms(AGG_DISTRIBUTION)
- .field(version.prefix())
+ .field(securityStandardVersionPrefix)
// 100 should be enough to display all the requirements per category. If not, the UI will be broken anyway
.size(MAX_FACET_SIZE);
categoriesAggs.subAggregation(addSecurityReportIssueCountAggregations(distributionAggregation));
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.db.component.ComponentDto;
import org.sonar.server.view.index.ViewDoc;
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.server.rule.RulesDefinition.OwaspAsvsVersion;
+import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion;
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;
assertThat(pciDss32Report.get(11).getChildren()).isEmpty();
}
+ @Test
+ public void getOwaspAsvs40Report_aggregation() {
+ List<SecurityStandardCategoryStatistics> owaspAsvsReport = indexIssuesAndAssertOwaspAsvsReport();
+
+ assertThat(owaspAsvsReport)
+ .isNotEmpty();
+
+ assertThat(owaspAsvsReport.get(0).getChildren()).hasSize(2);
+ assertThat(owaspAsvsReport.get(1).getChildren()).isEmpty();
+ 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(2);
+ assertThat(owaspAsvsReport.get(6).getChildren()).isEmpty();
+ assertThat(owaspAsvsReport.get(7).getChildren()).hasSize(1);
+ assertThat(owaspAsvsReport.get(8).getChildren()).isEmpty();
+ assertThat(owaspAsvsReport.get(9).getChildren()).hasSize(1);
+ assertThat(owaspAsvsReport.get(10).getChildren()).isEmpty();
+ assertThat(owaspAsvsReport.get(11).getChildren()).isEmpty();
+ assertThat(owaspAsvsReport.get(12).getChildren()).isEmpty();
+ assertThat(owaspAsvsReport.get(13).getChildren()).isEmpty();
+ }
+
@Test
public void getOwaspTop10Report_aggregation_with_cwe() {
List<SecurityStandardCategoryStatistics> owaspTop10Report = indexIssuesAndAssertOwaspReport(true);
.setResolution(Issue.RESOLUTION_FIXED),
newDoc("notpcidsshotspot", project).setPciDss32(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
- List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, RulesDefinition.PciDssVersion.V3_2).stream()
+ List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(project.uuid(), false, PciDssVersion.V3_2).stream()
.sorted(comparing(s -> parseInt(s.getCategory())))
.collect(toList());
assertThat(pciDssReport)
return pciDssReport;
}
+ private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspAsvsReport() {
+ ComponentDto project = newPrivateProjectDto();
+ indexIssues(
+ newDoc("openvul1", project).setOwaspAsvs40(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+ .setSeverity(Severity.MAJOR),
+ newDoc("openvul2", project).setOwaspAsvs40(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+ .setSeverity(Severity.MINOR),
+ newDoc("openvul3", project).setOwaspAsvs40(asList("10.1.2", "6.5")).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("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
+ .setStatus(Issue.STATUS_TO_REVIEW),
+ newDoc("toreviewhotspot2", project).setOwaspAsvs40(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+ newDoc("reviewedHotspot", project).setOwaspAsvs40(asList("3.1.1", "8.6")).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));
+
+ List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(project.uuid(), false, OwaspAsvsVersion.V4_0).stream()
+ .sorted(comparing(s -> parseInt(s.getCategory())))
+ .collect(toList());
+ assertThat(owaspAsvsReport)
+ .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
+ SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+ SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
+ .containsExactlyInAnyOrder(
+ tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 5),
+ tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* reviewedHotspot */, 4),
+ tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("6", 2L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
+ tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("8", 0L, OptionalInt.empty(), 0L, 1L /* reviewedHotspot */, 1),
+ tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("10", 1L, OptionalInt.of(2), 0L, 0L, 1),
+ tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
+
+ return owaspAsvsReport;
+ }
+
private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwasp2021Report(boolean includeCwe) {
ComponentDto project = newPrivateProjectDto();
indexIssues(
indexView(portfolio1.uuid(), singletonList(project1.uuid()));
indexView(portfolio2.uuid(), singletonList(project2.uuid()));
- List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, RulesDefinition.PciDssVersion.V3_2).stream()
+ List<SecurityStandardCategoryStatistics> pciDssReport = underTest.getPciDssReport(portfolio1.uuid(), true, PciDssVersion.V3_2).stream()
.sorted(comparing(s -> parseInt(s.getCategory())))
.collect(toList());
assertThat(pciDssReport)
tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1));
}
+ @Test
+ public void getOwaspAsvsReport_aggregation_on_portfolio() {
+ ComponentDto portfolio1 = db.components().insertPrivateApplication();
+ ComponentDto portfolio2 = db.components().insertPrivateApplication();
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ indexIssues(
+ newDoc("openvul1", project1).setOwaspAsvs40(asList("1.2.0", "3.4.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
+ .setSeverity(Severity.MAJOR),
+ newDoc("openvul2", project2).setOwaspAsvs40(asList("3.3.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+ .setSeverity(Severity.MINOR),
+ newDoc("openvul3", project1).setOwaspAsvs40(asList("10.1.2", "6.5")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
+ .setSeverity(Severity.MINOR),
+ newDoc("notowaspvul", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
+ newDoc("toreviewhotspot1", project2).setOwaspAsvs40(asList("1.3.0", "3.3.2")).setType(RuleType.SECURITY_HOTSPOT)
+ .setStatus(Issue.STATUS_TO_REVIEW),
+ newDoc("toreviewhotspot2", project1).setOwaspAsvs40(asList("3.5.6", "6.4.5")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+ newDoc("reviewedHotspot", project2).setOwaspAsvs40(asList("3.1.1", "8.6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED)
+ .setResolution(Issue.RESOLUTION_FIXED),
+ newDoc("notowasphotspot", project1).setOwaspAsvs40(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
+
+ indexView(portfolio1.uuid(), singletonList(project1.uuid()));
+ indexView(portfolio2.uuid(), singletonList(project2.uuid()));
+
+ List<SecurityStandardCategoryStatistics> owaspAsvsReport = underTest.getOwaspAsvsReport(portfolio1.uuid(), true, OwaspAsvsVersion.V4_0).stream()
+ .sorted(comparing(s -> parseInt(s.getCategory())))
+ .collect(toList());
+ assertThat(owaspAsvsReport)
+ .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
+ SecurityStandardCategoryStatistics::getVulnerabilityRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+ SecurityStandardCategoryStatistics::getReviewedSecurityHotspots, SecurityStandardCategoryStatistics::getSecurityReviewRating)
+ .containsExactlyInAnyOrder(
+ tuple("1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 1),
+ tuple("2", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("3", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot2 */, 0L, 5),
+ tuple("4", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("5", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("6", 1L /* openvul3 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 5),
+ tuple("7", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("8", 0L, OptionalInt.empty(), 0L, 0L /* reviewedHotspot */, 1),
+ tuple("9", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("10", 1L /* openvul3 */, OptionalInt.of(2), 0L, 0L, 1),
+ tuple("11", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("12", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("13", 0L, OptionalInt.empty(), 0L, 0L, 1),
+ tuple("14", 0L, OptionalInt.empty(), 0L, 0L, 1));
+ }
+
@Test
public void getCWETop25Report_aggregation() {
ComponentDto project = newPrivateProjectDto();