]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22542 Add security report support for STIG
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Fri, 19 Jul 2024 13:34:16 +0000 (15:34 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 23 Jul 2024 20:02:45 +0000 (20:02 +0000)
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java
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
sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java

index 7058a1a30636ec614a852276fd220bede186a49c..b5c9be8a409dc958e7ca9e055308aa91a664c451 100644 (file)
@@ -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");
 
index 5e4f67e5debb291b85913eb9fa6a2026a7b836fc..61f39ec36d0454726f83307be823b360cdd1c15d 100644 (file)
@@ -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<SecurityStandardCategoryStatistics> 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<SecurityStandardCategoryStatistics> searchWithLevelDistribution(SearchSourceBuilder sourceBuilder, String version, @Nullable String level) {
     return getSearchResponse(sourceBuilder)
       .getAggregations().asList().stream()
index 59b3abc0dc3a746fd0d7810a2e8b6d27b39ac5d4..38b17ff5f3e650551badef237ab985f8e6ea87c6 100644 (file)
@@ -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<String, SecurityStandardCategoryStatistics> 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<String, SecurityStandardCategoryStatistics> 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);
   }
index 29d6d06ccccd6a70805ef0f8d9f8dfeac3431c59..aa9d6305f92bdd18c615c7206b6a17e4cd69ca62 100644 (file)
@@ -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";