From 3e515e1d04424e962ef4d798c2820013e5facfae Mon Sep 17 00:00:00 2001 From: =?utf8?q?L=C3=A9o=20Geoffroy?= Date: Fri, 19 Jul 2024 15:34:16 +0200 Subject: [PATCH] SONAR-22542 Add security report support for STIG --- .../server/security/SecurityStandards.java | 55 ++++++++++++++ .../sonar/server/issue/index/IssueIndex.java | 11 +++ .../index/IssueIndexSecurityReportsTest.java | 72 +++++++++++++++++++ .../ws/client/issue/IssuesWsParameters.java | 1 + 4 files changed, 139 insertions(+) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java index 7058a1a3063..b5c9be8a409 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java @@ -215,6 +215,61 @@ public final class SecurityStandards { } } + public enum StigSupportedRequirement { + V222612("V-222612"), + V222578("V-222578"), + V222577("V-222577"), + V222609("V-222609"), + V222608("V-222608"), + V222602("V-222602"), + V222607("V-222607"), + V222604("V-222604"), + V222550("V-222550"), + V222596("V-222596"), + V222620("V-222620"), + V222542("V-222542"), + V222642("V-222642"), + V222567("V-222567"), + V222618("V-222618"), + V222610("V-222610"), + V222579("V-222579"), + V222615("V-222615"), + V222575("V-222575"), + V222576("V-222576"), + V222562("V-222562"), + V222563("V-222563"), + V222603("V-222603"), + V222606("V-222606"), + V222605("V-222605"), + V222391("V-222391"), + V222588("V-222588"), + V222582("V-222582"), + V222519("V-222519"), + V222599("V-222599"), + V222593("V-222593"), + V222597("V-222597"), + V222594("V-222594"), + V222397("V-222397"), + V222534("V-222534"), + V222641("V-222641"), + V222598("V-222598"), + V254803("V-254803"), + V222667("V-222667"), + V222653("V-222653"), + V222649("V-222649"); + + private final String requirement; + + StigSupportedRequirement(String requirement) { + + this.requirement = requirement; + } + + public String getRequirement() { + return requirement; + } + } + public enum PciDss { R1("1"), R2("2"), R3("3"), R4("4"), R5("5"), R6("6"), R7("7"), R8("8"), R9("9"), R10("10"), R11("11"), R12("12"); 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 5e4f67e5deb..61f39ec36d0 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 @@ -97,6 +97,7 @@ import org.sonar.server.permission.index.WebAuthorizationTypeSupport; import org.sonar.server.security.SecurityStandards; import org.sonar.server.security.SecurityStandards.PciDss; import org.sonar.server.security.SecurityStandards.SQCategory; +import org.sonar.server.security.SecurityStandards.StigSupportedRequirement; import org.sonar.server.user.UserSession; import org.sonar.server.view.index.ViewIndexDefinition; import org.springframework.util.CollectionUtils; @@ -1322,6 +1323,16 @@ public class IssueIndex { return search(request, includeCwe, version.label()); } + public List getStig(String projectUuid, boolean isViewOrApp, RulesDefinition.StigVersion stigVersion) { + SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp); + Arrays.stream(StigSupportedRequirement.values()) + .forEach(stigSupportedRequirement -> request.aggregation( + newSecurityReportSubAggregations( + AggregationBuilders.filter(stigSupportedRequirement.getRequirement(), boolQuery().filter(termQuery(stigVersion.prefix(), stigSupportedRequirement.getRequirement()))), + false, null))); + return search(request, false, stigVersion.label()); + } + private List searchWithLevelDistribution(SearchSourceBuilder sourceBuilder, String version, @Nullable String level) { return getSearchResponse(sourceBuilder) .getAggregations().asList().stream() 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 59b3abc0dc3..38b17ff5f3e 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 @@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test; 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.StigVersion; import org.sonar.db.component.ComponentDto; import org.sonar.server.view.index.ViewDoc; @@ -45,6 +46,8 @@ import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; import static org.sonar.api.server.rule.RulesDefinition.PciDssVersion; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.server.issue.IssueDocTesting.newDocForProject; +import static org.sonar.server.security.SecurityStandards.StigSupportedRequirement.V222391; +import static org.sonar.server.security.SecurityStandards.StigSupportedRequirement.V222397; import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD; class IssueIndexSecurityReportsTest extends IssueIndexTestCommon { @@ -729,6 +732,75 @@ class IssueIndexSecurityReportsTest extends IssueIndexTestCommon { assertThat(findRuleInCweByYear(cwe2023, "999")).isNull(); } + @Test + void getStigAsdV5R3_whenRequestingReportOnApplication_ShouldAggregateBasedOnStigRequirement() { + ComponentDto application = db.components().insertPrivateApplication().getMainBranchComponent(); + ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent(); + ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent(); + + indexIssues( + newDocForProject("openvul1", project1).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDocForProject("openvul2", project2).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR), + newDocForProject("toreviewhotspot", project1).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT) + .setStatus(Issue.STATUS_TO_REVIEW), + + newDocForProject("unknown", project2).setStigAsdV5R3(List.of("V-999999")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED) + .setSeverity(Severity.MINOR)); + + indexView(application.uuid(), asList(project1.uuid(), project2.uuid())); + + Map statisticsToMap = underTest.getStig(application.uuid(), true, StigVersion.ASD_V5R3) + .stream().collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, e -> e)); + + assertThat(statisticsToMap).hasSize(41) + .hasEntrySatisfying(V222391.getRequirement(), stat -> { + assertThat(stat.getVulnerabilities()).isEqualTo(2); + assertThat(stat.getToReviewSecurityHotspots()).isZero(); + assertThat(stat.getReviewedSecurityHotspots()).isZero(); + }) + .hasEntrySatisfying(V222397.getRequirement(), stat -> { + assertThat(stat.getVulnerabilities()).isZero(); + assertThat(stat.getToReviewSecurityHotspots()).isEqualTo(1); + assertThat(stat.getReviewedSecurityHotspots()).isZero(); + }); + } + + @Test + void getStigAsdV5R3_whenRequestingReportOnProject_ShouldAggregateBasedOnStigRequirement() { + ComponentDto branch = newPrivateProjectDto(); + indexIssues( + newDocForProject("openvul", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDocForProject("openvul2", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN) + .setSeverity(Severity.MAJOR), + newDocForProject("notopenvul", branch).setStigAsdV5R3(List.of(V222391.getRequirement())).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED) + .setResolution(Issue.RESOLUTION_FIXED) + .setSeverity(Severity.BLOCKER), + newDocForProject("toreviewhotspot", branch).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT) + .setStatus(Issue.STATUS_TO_REVIEW), + newDocForProject("reviewedHostpot", branch).setStigAsdV5R3(List.of(V222397.getRequirement())).setType(RuleType.SECURITY_HOTSPOT) + .setStatus(Issue.STATUS_REVIEWED).setResolution(Issue.RESOLUTION_FIXED)); + + Map statisticsToMap = underTest.getStig(branch.uuid(), false, StigVersion.ASD_V5R3) + .stream().collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, e -> e)); + + assertThat(statisticsToMap).hasSize(41) + .hasEntrySatisfying(V222391.getRequirement(), stat -> { + assertThat(stat.getVulnerabilities()).isEqualTo(2); + assertThat(stat.getToReviewSecurityHotspots()).isZero(); + assertThat(stat.getReviewedSecurityHotspots()).isZero(); + assertThat(stat.getVulnerabilityRating()).as("MAJOR = C").isEqualTo(OptionalInt.of(3)); + }) + .hasEntrySatisfying(V222397.getRequirement(), stat -> { + assertThat(stat.getVulnerabilities()).isZero(); + assertThat(stat.getToReviewSecurityHotspots()).isEqualTo(1); + assertThat(stat.getReviewedSecurityHotspots()).isEqualTo(1); + assertThat(stat.getSecurityReviewRating()).as("50% of hotspots are reviewed, so rating is C").isEqualTo(3); + }); + } + private SecurityStandardCategoryStatistics findRuleInCweByYear(SecurityStandardCategoryStatistics statistics, String cweId) { return statistics.getChildren().stream().filter(stat -> stat.getCategory().equals(cweId)).findAny().orElse(null); } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java index 29d6d06cccc..aa9d6305f92 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java @@ -95,6 +95,7 @@ public class IssuesWsParameters { public static final String PARAM_OWASP_TOP_10 = "owaspTop10"; public static final String PARAM_OWASP_TOP_10_2021 = "owaspTop10-2021"; public static final String PARAM_STIG_ASD_V5R3 = "stig-ASD_V5R3"; + public static final String PARAM_STIG = "stig"; @Deprecated public static final String PARAM_SANS_TOP_25 = "sansTop25"; public static final String PARAM_CWE_TOP_25 = "cweTop25"; -- 2.39.5