]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12026 Add transition "Set as In Review" on Security Hotspots
authorPierre Guillot <50145663+pierre-guillot-sonarsource@users.noreply.github.com>
Wed, 8 May 2019 14:36:58 +0000 (16:36 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 22 May 2019 18:21:14 +0000 (20:21 +0200)
19 files changed:
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowForSecurityHotspotsTest.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json
server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json
sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
sonar-ws/src/main/protobuf/ws-security.proto

index 11a45cb4946a41127e5700885e981ce8918c0d7e..4148b57c12133c652270b2cccc3456ad7f17f71e 100644 (file)
@@ -28,20 +28,20 @@ public class SecurityStandardCategoryStatistics {
   private final String category;
   private final long vulnerabilities;
   private final OptionalInt vulnerabiliyRating;
+  private final long inReviewSecurityHotspots;
   private final long toReviewSecurityHotspots;
-  private final long openSecurityHotspots;
   private final long wontFixSecurityHotspots;
   private final List<SecurityStandardCategoryStatistics> children;
   private long activeRules;
   private long totalRules;
 
-  public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long toReviewSecurityHotspots, long openSecurityHotspots,
-    long wontFixSecurityHotspots, @Nullable List<SecurityStandardCategoryStatistics> children) {
+  public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long inReviewSecurityHotspots, long toReviewSecurityHotspots,
+                                            long wontFixSecurityHotspots, @Nullable List<SecurityStandardCategoryStatistics> children) {
     this.category = category;
     this.vulnerabilities = vulnerabilities;
     this.vulnerabiliyRating = vulnerabiliyRating;
+    this.inReviewSecurityHotspots = inReviewSecurityHotspots;
     this.toReviewSecurityHotspots = toReviewSecurityHotspots;
-    this.openSecurityHotspots = openSecurityHotspots;
     this.wontFixSecurityHotspots = wontFixSecurityHotspots;
     this.children = children;
   }
@@ -58,12 +58,12 @@ public class SecurityStandardCategoryStatistics {
     return vulnerabiliyRating;
   }
 
-  public long getToReviewSecurityHotspots() {
-    return toReviewSecurityHotspots;
+  public long getInReviewSecurityHotspots() {
+    return inReviewSecurityHotspots;
   }
 
-  public long getOpenSecurityHotspots() {
-    return openSecurityHotspots;
+  public long getToReviewSecurityHotspots() {
+    return toReviewSecurityHotspots;
   }
 
   public long getWontFixSecurityHotspots() {
index 1a7cfe32e1bc6dc162fca419d9ce195ab98345dc..eea7b1e28e7f5be458e193bad7280e06cdb0f0e1 100644 (file)
@@ -39,6 +39,7 @@ import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
 import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
 import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_IN_REVIEW;
 import static org.sonar.api.issue.Issue.STATUS_OPEN;
 import static org.sonar.api.issue.Issue.STATUS_REOPENED;
 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
@@ -61,7 +62,7 @@ public class IssueWorkflow implements Startable {
   @Override
   public void start() {
     StateMachine.Builder builder = StateMachine.builder()
-      .states(STATUS_OPEN, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_RESOLVED, STATUS_CLOSED, STATUS_TO_REVIEW);
+      .states(STATUS_OPEN, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_RESOLVED, STATUS_CLOSED, STATUS_TO_REVIEW, STATUS_IN_REVIEW);
     buildManualTransitions(builder);
     buildAutomaticTransitions(builder);
     buildSecurityHotspotTransitions(builder);
@@ -149,6 +150,12 @@ public class IssueWorkflow implements Startable {
 
   private static void buildSecurityHotspotTransitions(StateMachine.Builder builder) {
     builder
+      .transition(Transition.builder(DefaultTransitions.SET_AS_IN_REVIEW)
+        .from(STATUS_TO_REVIEW).to(STATUS_IN_REVIEW)
+        .conditions(new HasType(RuleType.SECURITY_HOTSPOT))
+        .requiredProjectPermission(UserRole.SECURITYHOTSPOT_ADMIN)
+        .build())
+
       .transition(Transition.builder(DefaultTransitions.DETECT)
         .from(STATUS_TO_REVIEW).to(STATUS_OPEN)
         .conditions(new HasType(RuleType.SECURITY_HOTSPOT))
@@ -167,16 +174,6 @@ public class IssueWorkflow implements Startable {
         .functions(new SetType(RuleType.SECURITY_HOTSPOT))
         .requiredProjectPermission(UserRole.SECURITYHOTSPOT_ADMIN)
         .build())
-      .transition(Transition.builder(DefaultTransitions.REQUEST_REVIEW)
-        .from(STATUS_OPEN).to(STATUS_RESOLVED)
-        .conditions(IsManualVulnerability.INSTANCE)
-        .functions(new SetType(RuleType.SECURITY_HOTSPOT), new SetResolution(RESOLUTION_FIXED))
-        .build())
-      .transition(Transition.builder(DefaultTransitions.REQUEST_REVIEW)
-        .from(STATUS_REOPENED).to(STATUS_RESOLVED)
-        .conditions(IsManualVulnerability.INSTANCE)
-        .functions(new SetType(RuleType.SECURITY_HOTSPOT), new SetResolution(RESOLUTION_FIXED))
-        .build())
       .transition(Transition.builder(DefaultTransitions.REJECT)
         .from(STATUS_RESOLVED).to(STATUS_REOPENED)
         .conditions(new HasType(RuleType.SECURITY_HOTSPOT), new HasResolution(RESOLUTION_FIXED))
@@ -236,6 +233,12 @@ public class IssueWorkflow implements Startable {
         .functions(SetClosed.INSTANCE, SetCloseDate.INSTANCE)
         .automatic()
         .build())
+      .transition(Transition.builder(AUTOMATIC_CLOSE_TRANSITION)
+        .from(STATUS_IN_REVIEW).to(STATUS_CLOSED)
+        .conditions(IsBeingClosed.INSTANCE, new HasType(RuleType.SECURITY_HOTSPOT))
+        .functions(SetClosed.INSTANCE, SetCloseDate.INSTANCE)
+        .automatic()
+        .build())
 
       // Reopen issues that are marked as resolved but that are still alive.
       .transition(Transition.builder("automaticreopen")
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowForSecurityHotspotsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowForSecurityHotspotsTest.java
new file mode 100644 (file)
index 0000000..03bb358
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue.workflow;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.issue.DefaultTransitions;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleType;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.server.issue.IssueFieldsSetter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
+import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
+import static org.sonar.api.issue.Issue.STATUS_CLOSED;
+import static org.sonar.api.issue.Issue.STATUS_IN_REVIEW;
+import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
+import static org.sonar.db.rule.RuleTesting.XOO_X1;
+
+@RunWith(DataProviderRunner.class)
+public class IssueWorkflowForSecurityHotspotsTest {
+
+  private static final String[] ALL_STATUSES_LEADING_TO_CLOSED = new String[] {STATUS_TO_REVIEW, STATUS_IN_REVIEW, STATUS_RESOLVED};
+
+  private static final String[] SUPPORTED_RESOLUTIONS_FOR_UNCLOSING = new String[] {RESOLUTION_FIXED, RESOLUTION_REMOVED};
+
+  private IssueFieldsSetter updater = new IssueFieldsSetter();
+
+  private IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater);
+
+  @Test
+  public void list_out_transitions_in_status_to_review() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_TO_REVIEW);
+
+    List<Transition> transitions = underTest.outTransitions(issue);
+
+    assertThat(keys(transitions)).containsOnly("setinreview", "detect", "clear");
+  }
+
+  @Test
+  public void list_out_transitions_in_status_in_review() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_IN_REVIEW);
+
+    List<Transition> transitions = underTest.outTransitions(issue);
+
+    assertThat(keys(transitions)).isEmpty();
+  }
+
+  @Test
+  public void set_as_in_review() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue()
+      .setType(RuleType.SECURITY_HOTSPOT)
+      .setIsFromHotspot(true)
+      .setStatus(STATUS_TO_REVIEW);
+
+    boolean result = underTest.doManualTransition(issue, DefaultTransitions.SET_AS_IN_REVIEW, IssueChangeContext.createUser(new Date(), "USER1"));
+
+    assertThat(result).isTrue();
+    assertThat(issue.getStatus()).isEqualTo(STATUS_IN_REVIEW);
+    assertThat(issue.resolution()).isNull();
+  }
+
+  @Test
+  public void automatically_close_resolved_security_hotspots_in_status_to_review() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue()
+      .setType(RuleType.SECURITY_HOTSPOT)
+      .setResolution(null)
+      .setStatus(STATUS_TO_REVIEW)
+      .setNew(false)
+      .setBeingClosed(true);
+    Date now = new Date();
+
+    underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+
+    assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+    assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+    assertThat(issue.closeDate()).isNotNull();
+    assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+  }
+
+  @Test
+  public void automatically_close_resolved_security_hotspots_in_status_in_review() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue()
+      .setType(RuleType.SECURITY_HOTSPOT)
+      .setResolution(null)
+      .setStatus(STATUS_IN_REVIEW)
+      .setNew(false)
+      .setBeingClosed(true);
+    Date now = new Date();
+
+    underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+
+    assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
+    assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+    assertThat(issue.closeDate()).isNotNull();
+    assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
+  }
+
+  @Test
+  @UseDataProvider("allStatusesLeadingToClosed")
+  public void do_not_automatically_reopen_closed_issues_of_security_hotspots(String previousStatus) {
+    DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
+      .map(resolution -> {
+        DefaultIssue issue = newClosedIssue(resolution);
+        setStatusPreviousToClosed(issue, previousStatus);
+        issue.setType(RuleType.SECURITY_HOTSPOT);
+        return issue;
+      })
+      .toArray(DefaultIssue[]::new);
+    Date now = new Date();
+    underTest.start();
+
+    Arrays.stream(issues).forEach(issue -> {
+      underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+
+      assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+      assertThat(issue.updateDate()).isNull();
+    });
+  }
+
+  @Test
+  public void doAutomaticTransition_does_nothing_on_security_hotspots_in_to_review_status() {
+    DefaultIssue issue = new DefaultIssue()
+      .setKey("ABCDE")
+      .setRuleKey(XOO_X1)
+      .setResolution(null)
+      .setStatus(STATUS_TO_REVIEW);
+
+    underTest.start();
+    underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(new Date()));
+
+    assertThat(issue.status()).isEqualTo(STATUS_TO_REVIEW);
+    assertThat(issue.resolution()).isNull();
+  }
+
+  @Test
+  @UseDataProvider("allStatusesLeadingToClosed")
+  public void do_not_automatically_reopen_closed_issues_of_manual_vulnerability(String previousStatus) {
+    DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
+      .map(resolution -> {
+        DefaultIssue issue = newClosedIssue(resolution);
+        setStatusPreviousToClosed(issue, previousStatus);
+        issue.setIsFromHotspot(true);
+        return issue;
+      })
+      .toArray(DefaultIssue[]::new);
+    Date now = new Date();
+    underTest.start();
+
+    Arrays.stream(issues).forEach(issue -> {
+      underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
+
+      assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
+      assertThat(issue.updateDate()).isNull();
+    });
+  }
+
+  @DataProvider
+  public static Object[][] allStatusesLeadingToClosed() {
+    return Arrays.stream(ALL_STATUSES_LEADING_TO_CLOSED)
+      .map(t -> new Object[] {t})
+      .toArray(Object[][]::new);
+  }
+
+  @Test
+  public void do_not_allow_to_doManualTransition_when_condition_fails() {
+    underTest.start();
+    DefaultIssue issue = new DefaultIssue()
+      .setKey("ABCDE")
+      // Detect is only available on hotspot
+      .setType(RuleType.VULNERABILITY)
+      .setIsFromHotspot(true)
+      .setStatus(STATUS_RESOLVED)
+      .setResolution(RESOLUTION_WONT_FIX)
+      .setRuleKey(XOO_X1);
+
+    assertThat(underTest.doManualTransition(issue, DefaultTransitions.DETECT, IssueChangeContext.createScan(new Date()))).isFalse();
+  }
+
+  private static DefaultIssue newClosedIssue(String resolution) {
+    return new DefaultIssue()
+      .setKey("ABCDE")
+      .setRuleKey(RuleKey.of("js", "S001"))
+      .setResolution(resolution)
+      .setStatus(STATUS_CLOSED)
+      .setNew(false)
+      .setCloseDate(new Date(5_999_999L));
+  }
+
+  private static void setStatusPreviousToClosed(DefaultIssue issue, String previousStatus) {
+    addStatusChange(issue, new Date(), previousStatus, STATUS_CLOSED);
+  }
+
+  private static void addStatusChange(DefaultIssue issue, Date date, String previousStatus, String newStatus) {
+    issue.addChange(new FieldDiffs().setCreationDate(date).setDiff("status", previousStatus, newStatus));
+  }
+
+  private Collection<String> keys(List<Transition> transitions) {
+    return Collections2.transform(transitions, new Function<Transition, String>() {
+      @Override
+      public String apply(@Nullable Transition transition) {
+        return transition.key();
+      }
+    });
+  }
+}
index 4151f7280b6e85fc573f87957796a4484ccaabc8..a8b7c9998d4030bbf6a8f9fa7edf0a8725788ab5 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.collect.Collections2;
 import com.tngtech.java.junit.dataprovider.DataProvider;
 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
 import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
@@ -36,7 +37,6 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.sonar.api.issue.DefaultTransitions;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleType;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.IssueChangeContext;
@@ -52,11 +52,11 @@ import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED;
 import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
 import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_IN_REVIEW;
 import static org.sonar.api.issue.Issue.STATUS_OPEN;
 import static org.sonar.api.issue.Issue.STATUS_REOPENED;
 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
-import static org.sonar.db.rule.RuleTesting.XOO_X1;
 
 @RunWith(DataProviderRunner.class)
 public class IssueWorkflowTest {
@@ -68,7 +68,14 @@ public class IssueWorkflowTest {
   @Test
   public void list_statuses() {
     underTest.start();
-    assertThat(underTest.statusKeys()).containsExactly(STATUS_OPEN, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_RESOLVED, STATUS_CLOSED, STATUS_TO_REVIEW);
+
+    List<String> expectedStatus = new ArrayList<>();
+    // issues statuses
+    expectedStatus.addAll(Arrays.asList(STATUS_OPEN, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_RESOLVED, STATUS_CLOSED));
+    // hostpots statuses
+    expectedStatus.addAll(Arrays.asList(STATUS_TO_REVIEW, STATUS_IN_REVIEW));
+
+    assertThat(underTest.statusKeys()).containsExactlyInAnyOrder(expectedStatus.toArray(new String[]{}));
   }
 
   @Test
@@ -116,16 +123,6 @@ public class IssueWorkflowTest {
     assertThat(transitions).isEmpty();
   }
 
-  @Test
-  public void list_out_transitions_from_security_hotspot_in_status_to_review() {
-    underTest.start();
-    DefaultIssue issue = new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(STATUS_TO_REVIEW);
-
-    List<Transition> transitions = underTest.outTransitions(issue);
-
-    assertThat(keys(transitions)).containsOnly("detect", "clear");
-  }
-
   @Test
   public void fail_if_unknown_status_when_listing_transitions() {
     underTest.start();
@@ -158,27 +155,6 @@ public class IssueWorkflowTest {
     assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
   }
 
-  @Test
-  public void automatically_close_resolved_security_hotspots_in_to_review() {
-    underTest.start();
-    DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      .setType(RuleType.SECURITY_HOTSPOT)
-      .setRuleKey(XOO_X1)
-      .setResolution(null)
-      .setStatus(STATUS_TO_REVIEW)
-      .setNew(false)
-      .setBeingClosed(true);
-    Date now = new Date();
-
-    underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
-
-    assertThat(issue.resolution()).isEqualTo(RESOLUTION_FIXED);
-    assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
-    assertThat(issue.closeDate()).isNotNull();
-    assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(now, Calendar.SECOND));
-  }
-
   @Test
   @UseDataProvider("allStatusesLeadingToClosed")
   public void automatically_reopen_closed_issue_to_its_previous_status_from_changelog(String previousStatus) {
@@ -328,64 +304,6 @@ public class IssueWorkflowTest {
     });
   }
 
-  @Test
-  @UseDataProvider("allStatusesLeadingToClosed")
-  public void do_not_automatically_reopen_closed_issues_of_security_hotspots(String previousStatus) {
-    DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
-      .map(resolution -> {
-        DefaultIssue issue = newClosedIssue(resolution);
-        setStatusPreviousToClosed(issue, previousStatus);
-        issue.setType(RuleType.SECURITY_HOTSPOT);
-        return issue;
-      })
-      .toArray(DefaultIssue[]::new);
-    Date now = new Date();
-    underTest.start();
-
-    Arrays.stream(issues).forEach(issue -> {
-      underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
-
-      assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
-      assertThat(issue.updateDate()).isNull();
-    });
-  }
-
-  @Test
-  public void doAutomaticTransition_does_nothing_on_security_hotspots_in_to_review_status() {
-    DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      .setRuleKey(XOO_X1)
-      .setResolution(null)
-      .setStatus(STATUS_TO_REVIEW);
-
-    underTest.start();
-    underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(new Date()));
-
-    assertThat(issue.status()).isEqualTo(STATUS_TO_REVIEW);
-    assertThat(issue.resolution()).isNull();
-  }
-
-  @Test
-  @UseDataProvider("allStatusesLeadingToClosed")
-  public void do_not_automatically_reopen_closed_issues_of_manual_vulnerability(String previousStatus) {
-    DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING)
-      .map(resolution -> {
-        DefaultIssue issue = newClosedIssue(resolution);
-        setStatusPreviousToClosed(issue, previousStatus);
-        issue.setIsFromHotspot(true);
-        return issue;
-      })
-      .toArray(DefaultIssue[]::new);
-    Date now = new Date();
-    underTest.start();
-
-    Arrays.stream(issues).forEach(issue -> {
-      underTest.doAutomaticTransition(issue, IssueChangeContext.createScan(now));
-
-      assertThat(issue.status()).isEqualTo(STATUS_CLOSED);
-      assertThat(issue.updateDate()).isNull();
-    });
-  }
   private static final String[] ALL_STATUSES_LEADING_TO_CLOSED = new String[] {STATUS_OPEN, STATUS_REOPENED, STATUS_CONFIRMED, STATUS_RESOLVED};
   private static final String[] ALL_RESOLUTIONS_BEFORE_CLOSING = new String[] {
     null,
@@ -511,21 +429,6 @@ public class IssueWorkflowTest {
     assertThat(issue.assignee()).isNull();
   }
 
-  @Test
-  public void do_not_allow_to_doManualTransition_when_condition_fails() {
-    underTest.start();
-    DefaultIssue issue = new DefaultIssue()
-      .setKey("ABCDE")
-      // Detect is only available on hotspot
-      .setType(RuleType.VULNERABILITY)
-      .setIsFromHotspot(true)
-      .setStatus(STATUS_RESOLVED)
-      .setResolution(RESOLUTION_WONT_FIX)
-      .setRuleKey(XOO_X1);
-
-    assertThat(underTest.doManualTransition(issue, DefaultTransitions.DETECT, IssueChangeContext.createScan(new Date()))).isFalse();
-  }
-
   private static DefaultIssue newClosedIssue(String resolution) {
     return new DefaultIssue()
       .setKey("ABCDE")
index 580edfee6aa5be7645c42b176722c10ca9d20f1f..b2949e2721029f457939cb76066e306757dd11b3 100644 (file)
@@ -192,20 +192,21 @@ public class IssueIndex {
   private static final int MAX_FACET_SIZE = 100;
   private static final String AGG_VULNERABILITIES = "vulnerabilities";
   private static final String AGG_SEVERITIES = "severities";
-  private static final String AGG_OPEN_SECURITY_HOTSPOTS = "openSecurityHotspots";
   private static final String AGG_TO_REVIEW_SECURITY_HOTSPOTS = "toReviewSecurityHotspots";
+  private static final String AGG_IN_REVIEW_SECURITY_HOTSPOTS = "inReviewSecurityHotspots";
   private static final String AGG_WONT_FIX_SECURITY_HOTSPOTS = "wontFixSecurityHotspots";
   private static final String AGG_CWES = "cwes";
   private static final BoolQueryBuilder NON_RESOLVED_VULNERABILITIES_FILTER = boolQuery()
     .filter(termQuery(FIELD_ISSUE_TYPE, VULNERABILITY.name()))
     .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
-  private static final BoolQueryBuilder NON_RESOLVED_HOTSTPOS_FILTER = boolQuery()
+  private static final BoolQueryBuilder IN_REVIEW_HOTSPOTS_FILTER = boolQuery()
     .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
+    .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_IN_REVIEW))
     .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
   private static final BoolQueryBuilder TO_REVIEW_HOTSPOTS_FILTER = boolQuery()
     .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
-    .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_RESOLVED))
-    .filter(termQuery(FIELD_ISSUE_RESOLUTION, Issue.RESOLUTION_FIXED));
+    .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_TO_REVIEW))
+    .mustNot(existsQuery(FIELD_ISSUE_RESOLUTION));
   private static final BoolQueryBuilder WONT_FIX_HOTSPOTS_FILTER = boolQuery()
     .filter(termQuery(FIELD_ISSUE_TYPE, SECURITY_HOTSPOT.name()))
     .filter(termQuery(FIELD_ISSUE_STATUS, Issue.STATUS_RESOLVED))
@@ -927,14 +928,14 @@ public class IssueIndex {
       .mapToInt(b -> Severity.ALL.indexOf(b.getKeyAsString()) + 1)
       .max();
 
-    long openSecurityHotspots = ((InternalValueCount) ((InternalFilter) categoryBucket.getAggregations().get(AGG_OPEN_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
-      .getValue();
     long toReviewSecurityHotspots = ((InternalValueCount) ((InternalFilter) categoryBucket.getAggregations().get(AGG_TO_REVIEW_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
       .getValue();
+    long inReviewSecurityHotspots = ((InternalValueCount) ((InternalFilter) categoryBucket.getAggregations().get(AGG_IN_REVIEW_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
+      .getValue();
     long wontFixSecurityHotspots = ((InternalValueCount) ((InternalFilter) categoryBucket.getAggregations().get(AGG_WONT_FIX_SECURITY_HOTSPOTS)).getAggregations().get(AGG_COUNT))
       .getValue();
 
-    return new SecurityStandardCategoryStatistics(categoryName, vulnerabilities, severityRating, toReviewSecurityHotspots, openSecurityHotspots,
+    return new SecurityStandardCategoryStatistics(categoryName, vulnerabilities, severityRating, inReviewSecurityHotspots, toReviewSecurityHotspots,
       wontFixSecurityHotspots, children);
   }
 
@@ -962,10 +963,10 @@ public class IssueIndex {
             AggregationBuilders.terms(AGG_SEVERITIES).field(FIELD_ISSUE_SEVERITY)
               .subAggregation(
                 AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY))))
-      .subAggregation(AggregationBuilders.filter(AGG_OPEN_SECURITY_HOTSPOTS, NON_RESOLVED_HOTSTPOS_FILTER)
+      .subAggregation(AggregationBuilders.filter(AGG_TO_REVIEW_SECURITY_HOTSPOTS, TO_REVIEW_HOTSPOTS_FILTER)
         .subAggregation(
           AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY)))
-      .subAggregation(AggregationBuilders.filter(AGG_TO_REVIEW_SECURITY_HOTSPOTS, TO_REVIEW_HOTSPOTS_FILTER)
+      .subAggregation(AggregationBuilders.filter(AGG_IN_REVIEW_SECURITY_HOTSPOTS, IN_REVIEW_HOTSPOTS_FILTER)
         .subAggregation(
           AggregationBuilders.count(AGG_COUNT).field(FIELD_ISSUE_KEY)))
       .subAggregation(AggregationBuilders.filter(AGG_WONT_FIX_SECURITY_HOTSPOTS, WONT_FIX_HOTSPOTS_FILTER)
@@ -990,8 +991,8 @@ public class IssueIndex {
       .setQuery(
         componentFilter
           .should(NON_RESOLVED_VULNERABILITIES_FILTER)
-          .should(NON_RESOLVED_HOTSTPOS_FILTER)
           .should(TO_REVIEW_HOTSPOTS_FILTER)
+          .should(IN_REVIEW_HOTSPOTS_FILTER)
           .should(WONT_FIX_HOTSPOTS_FILTER)
           .minimumShouldMatch(1))
       .setSize(0);
index dd3cfb81bd99215072461f8d9a0cdb6ab4580f90..972e8f7c38be651a545d667e5384c97e923d64bc 100644 (file)
@@ -38,6 +38,8 @@ import org.sonar.server.issue.IssueUpdater;
 import org.sonar.server.issue.TransitionService;
 import org.sonar.server.user.UserSession;
 
+import static java.lang.String.format;
+import static org.sonar.api.issue.DefaultTransitions.SET_AS_IN_REVIEW;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_DO_TRANSITION;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUE;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TRANSITION;
@@ -68,12 +70,13 @@ public class DoTransitionAction implements IssuesWsAction {
     WebService.NewAction action = controller.createAction(ACTION_DO_TRANSITION)
       .setDescription("Do workflow transition on an issue. Requires authentication and Browse permission on project.<br/>" +
         "The transitions '" + DefaultTransitions.WONT_FIX + "' and '" + DefaultTransitions.FALSE_POSITIVE + "' require the permission 'Administer Issues'.<br/>" +
-        "The transitions involving security hotspots (except '" + DefaultTransitions.REQUEST_REVIEW + "') require the permission 'Administer Security Hotspot'.")
+        "The transitions involving security hotspots require the permission 'Administer Security Hotspot'.")
       .setSince("3.6")
       .setChangelog(
+        new Change("7.8", format("added transition '%s' for security hotspots ", SET_AS_IN_REVIEW)),
+        new Change("7.3", "added transitions for security hotspots"),
         new Change("6.5", "the database ids of the components are removed from the response"),
-        new Change("6.5", "the response field components.uuid is deprecated. Use components.key instead."),
-        new Change("7.3", "added transitions for security hotspots"))
+        new Change("6.5", "the response field components.uuid is deprecated. Use components.key instead."))
       .setHandler(this)
       .setResponseExample(Resources.getResource(this.getClass(), "do_transition-example.json"))
       .setPost(true);
index 0f7b94220e69a500632025c3a779d766c526379e..1e93e6ff96c57eb96a515d54bb260a1ed504cb55 100644 (file)
@@ -56,7 +56,8 @@ public interface QGChangeEventListener {
     RESOLVED_FP,
     RESOLVED_WF,
     RESOLVED_FIXED,
-    TO_REVIEW;
+    TO_REVIEW,
+    IN_REVIEW;
 
     protected static final Set<Status> CLOSED_STATUSES = EnumSet.of(CONFIRMED, RESOLVED_FIXED, RESOLVED_FP, RESOLVED_WF);
   }
index e1100812ca9b541a46d5b1dbfb624f857ad2a83a..ba51aef85fd8289a22ff4dcee2b27ccf9fa3aa10 100644 (file)
@@ -120,6 +120,8 @@ public class QGChangeEventListenersImpl implements QGChangeEventListeners {
           return QGChangeEventListener.Status.REOPENED;
         case Issue.STATUS_TO_REVIEW:
           return QGChangeEventListener.Status.TO_REVIEW;
+        case Issue.STATUS_IN_REVIEW:
+          return QGChangeEventListener.Status.IN_REVIEW;
         case Issue.STATUS_RESOLVED:
           return statusOfResolved(issue);
         default:
index bc00cddd508758b43ef630a14ec4c12a2fb44847..ce04036104c5b03a909fb846bca69a75317c3db4 100644 (file)
@@ -134,12 +134,12 @@ public class IssueIndexSecurityReportsTest {
     ComponentDto project = newPrivateProjectDto(org);
     ComponentDto another = newPrivateProjectDto(org);
     indexIssues(
-      newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN),
-      newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+      newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
 
     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
-      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
+      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
       .contains(
         tuple("a1", 1L /* openhotspot1 */));
   }
@@ -149,13 +149,13 @@ public class IssueIndexSecurityReportsTest {
     OrganizationDto org = newOrganizationDto();
     ComponentDto project = newPrivateProjectDto(org);
     indexIssues(
-      newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN),
+      newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
       newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
         .setResolution(Issue.RESOLUTION_FIXED));
 
     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
     assertThat(owaspTop10Report)
-      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getOpenSecurityHotspots)
+      .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
       .contains(
         tuple("a1", 1L /* openhotspot1 */));
   }
@@ -175,15 +175,15 @@ public class IssueIndexSecurityReportsTest {
       .collect(Collectors.toMap(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getChildren));
 
     assertThat(cweByOwasp.get("a1")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
-      SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
-      SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+      SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+      SecurityStandardCategoryStatistics::getInReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
       .containsExactlyInAnyOrder(
         tuple("123", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 0L),
         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 0L),
         tuple("unknown", 0L, OptionalInt.empty(), 1L /* openhotspot1 */, 0L, 0L));
     assertThat(cweByOwasp.get("a3")).extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
-      SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
-      SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+      SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+      SecurityStandardCategoryStatistics::getInReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
       .containsExactlyInAnyOrder(
         tuple("123", 2L /* openvul1, openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 0L, 0L),
         tuple("456", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 0L, 1L /* toReviewHotspot */, 0L),
@@ -193,35 +193,28 @@ public class IssueIndexSecurityReportsTest {
   private List<SecurityStandardCategoryStatistics> indexIssuesAndAssertOwaspReport(boolean includeCwe) {
     OrganizationDto org = newOrganizationDto();
     ComponentDto project = newPrivateProjectDto(org);
-    ComponentDto another = newPrivateProjectDto(org);
     indexIssues(
-      newDoc("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
-        .setSeverity(Severity.MAJOR),
-      newDoc("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(asList("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED)
-        .setSeverity(Severity.MINOR),
-      newDoc("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
-        .setSeverity(Severity.CRITICAL),
-      newDoc("openhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_OPEN),
-      newDoc("openhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REOPENED),
-      newDoc("toReviewHotspot", project).setOwaspTop10(asList("a5", "a3")).setCwe(asList("456")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_FIXED),
-      newDoc("WFHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_WONT_FIX),
-      newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+      newDoc("openvul1", project).setOwaspTop10(asList("a1", "a3")).setCwe(asList("123", "456")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR),
+      newDoc("openvul2", project).setOwaspTop10(asList("a3", "a6")).setCwe(asList("123")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_REOPENED).setSeverity(Severity.MINOR),
+      newDoc("notowaspvul", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
+      newDoc("toreviewhotspot1", project).setOwaspTop10(asList("a1", "a3")).setCwe(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("toreviewhotspot2", project).setOwaspTop10(asList("a3", "a6")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("inreviewhotspot", project).setOwaspTop10(asList("a5", "a3")).setCwe(asList("456")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
+      newDoc("WFHotspot", project).setOwaspTop10(asList("a3", "a8")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX),
+      newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
 
     List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe);
     assertThat(owaspTop10Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
-        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
-        SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+        SecurityStandardCategoryStatistics::getInReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
       .containsExactlyInAnyOrder(
-        tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* openhotspot1 */, 0L, 0L),
+        tuple("a1", 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 0L),
         tuple("a2", 0L, OptionalInt.empty(), 0L, 0L, 0L),
-        tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* openhotspot1,openhotspot2 */, 1L /* toReviewHotspot */, 1L /* WFHotspot */),
+        tuple("a3", 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* inReviewHotspot */, 1L /* WFHotspot */),
         tuple("a4", 0L, OptionalInt.empty(), 0L, 0L, 0L),
-        tuple("a5", 0L, OptionalInt.empty(), 0L, 1L/* toReviewHotspot */, 0L),
-        tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* openhotspot2 */, 0L, 0L),
+        tuple("a5", 0L, OptionalInt.empty(), 0L, 1L/* inReviewHotspot */, 0L),
+        tuple("a6", 1L /* openvul2 */, OptionalInt.of(2) /* MINOR = B */, 1L /* toreviewhotspot2 */, 0L, 0L),
         tuple("a7", 0L, OptionalInt.empty(), 0L, 0L, 0L),
         tuple("a8", 0L, OptionalInt.empty(), 0L, 0L, 1L /* WFHotspot */),
         tuple("a9", 0L, OptionalInt.empty(), 0L, 0L, 0L),
@@ -242,27 +235,21 @@ public class IssueIndexSecurityReportsTest {
       newDoc("notopenvul", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
         .setResolution(Issue.RESOLUTION_FIXED)
         .setSeverity(Severity.BLOCKER),
-      newDoc("notsansvul", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
-        .setSeverity(Severity.CRITICAL),
-      newDoc("openhotspot1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_OPEN),
-      newDoc("openhotspot2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_REOPENED),
-      newDoc("toReviewHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_FIXED),
-      newDoc("WFHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_WONT_FIX),
-      newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+      newDoc("notsansvul", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
+      newDoc("toreviewhotspot1", project).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("toreviewhotspot2", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("inReviewHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
+      newDoc("WFHotspot", project).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX),
+      newDoc("notowasphotspot", project).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
 
     List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(project.uuid(), false, false);
     assertThat(sansTop25Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
-        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
-        SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+        SecurityStandardCategoryStatistics::getInReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
       .containsExactlyInAnyOrder(
-        tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* openhotspot1 */, 0L, 0L),
-        tuple(SANS_TOP_25_RISKY_RESOURCE, 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* openhotspot1,openhotspot2 */, 1L /* toReviewHotspot */,
-          1L /* WFHotspot */),
+        tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 0L),
+        tuple(SANS_TOP_25_RISKY_RESOURCE, 2L /* openvul1,openvul2 */, OptionalInt.of(3)/* MAJOR = C */, 2L/* toreviewhotspot1,toreviewhotspot2 */, 1L /* inReviewHotspot */,1L /* WFHotspot */),
         tuple(SANS_TOP_25_POROUS_DEFENSES, 1L /* openvul2 */, OptionalInt.of(2)/* MINOR = B */, 1L/* openhotspot2 */, 0L, 0L));
 
     assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
@@ -283,17 +270,12 @@ public class IssueIndexSecurityReportsTest {
       newDoc("notopenvul", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED)
         .setResolution(Issue.RESOLUTION_FIXED)
         .setSeverity(Severity.BLOCKER),
-      newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN)
-        .setSeverity(Severity.CRITICAL),
-      newDoc("openhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_OPEN),
-      newDoc("openhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT)
-        .setStatus(Issue.STATUS_REOPENED),
-      newDoc("toReviewHotspot", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_FIXED),
-      newDoc("WFHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED)
-        .setResolution(Issue.RESOLUTION_WONT_FIX),
-      newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN));
+      newDoc("notsansvul", project2).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
+      newDoc("toreviewhotspot1", project1).setSansTop25(asList(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("toreviewhotspot2", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
+      newDoc("inReviewHotspot", project1).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW),
+      newDoc("WFHotspot", project2).setSansTop25(asList(SANS_TOP_25_RISKY_RESOURCE)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX),
+      newDoc("notowasphotspot", project1).setSansTop25(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
 
     indexView(portfolio1.uuid(), singletonList(project1.uuid()));
     indexView(portfolio2.uuid(), singletonList(project2.uuid()));
@@ -301,11 +283,11 @@ public class IssueIndexSecurityReportsTest {
     List<SecurityStandardCategoryStatistics> sansTop25Report = underTest.getSansTop25Report(portfolio1.uuid(), true, false);
     assertThat(sansTop25Report)
       .extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
-        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getOpenSecurityHotspots,
-        SecurityStandardCategoryStatistics::getToReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
+        SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
+        SecurityStandardCategoryStatistics::getInReviewSecurityHotspots, SecurityStandardCategoryStatistics::getWontFixSecurityHotspots)
       .containsExactlyInAnyOrder(
-        tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* openhotspot1 */, 0L, 0L),
-        tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* openhotspot1 */, 1L /* toReviewHotspot */, 0L),
+        tuple(SANS_TOP_25_INSECURE_INTERACTION, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L /* toreviewhotspot1 */, 0L, 0L),
+        tuple(SANS_TOP_25_RISKY_RESOURCE, 1L /* openvul1 */, OptionalInt.of(3)/* MAJOR = C */, 1L/* toreviewhotspot1 */, 1L /* inReviewHotspot */, 0L),
         tuple(SANS_TOP_25_POROUS_DEFENSES, 0L, OptionalInt.empty(), 0L, 0L, 0L));
 
     assertThat(sansTop25Report).allMatch(category -> category.getChildren().isEmpty());
index 021a872cefb7ca98ddf9835dde31beaa334108ee..21961822894752a5df1bea3033e9e850a5edec0b 100644 (file)
@@ -288,6 +288,8 @@ public class QGChangeEventListenersImplTest {
   public void test_status_mapping_on_security_hotspots() {
     assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW)))
       .isEqualTo(QGChangeEventListener.Status.TO_REVIEW);
+    assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_IN_REVIEW)))
+      .isEqualTo(QGChangeEventListener.Status.IN_REVIEW);
   }
 
   private void verifyListenerCalled(QGChangeEventListener listener, QGChangeEvent changeEvent, DefaultIssue... issues) {
index 4c8399afeb06b9dede973f94fb6eb4f2310848e8..c7c300c51a5171d8527c229b9968ffd0d4cb85e0 100644 (file)
@@ -3,8 +3,8 @@
     {
       "category": "a1",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
@@ -13,8 +13,8 @@
     {
       "category": "a2",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
@@ -23,8 +23,8 @@
     {
       "category": "a3",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -33,8 +33,8 @@
     {
       "category": "a4",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -43,8 +43,8 @@
     {
       "category": "a5",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -53,8 +53,8 @@
     {
       "category": "a6",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -63,8 +63,8 @@
     {
       "category": "a7",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -73,8 +73,8 @@
     {
       "category": "a8",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -83,8 +83,8 @@
     {
       "category": "a9",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -93,8 +93,8 @@
     {
       "category": "a10",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "unknown",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
index 99e1e68464fcb70f69869032742a98c5e2644e12..3e4614196f9331c56115141d4c2b5d82cde22025 100644 (file)
@@ -3,8 +3,8 @@
     {
       "category": "a1",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
@@ -14,8 +14,8 @@
       "category": "a2",
       "vulnerabilities": 1,
       "vulnerabilityRating": 3,
+      "inReviewSecurityHotspots": 1,
       "toReviewSecurityHotspots": 1,
-      "openSecurityHotspots": 1,
       "wontFixSecurityHotspots": 1,
       "distribution": [],
       "activeRules": 1,
@@ -24,8 +24,8 @@
     {
       "category": "a3",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -34,8 +34,8 @@
     {
       "category": "a4",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -44,8 +44,8 @@
     {
       "category": "a5",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -54,8 +54,8 @@
     {
       "category": "a6",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -64,8 +64,8 @@
     {
       "category": "a7",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -74,8 +74,8 @@
     {
       "category": "a8",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -84,8 +84,8 @@
     {
       "category": "a9",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -94,8 +94,8 @@
     {
       "category": "a10",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "unknown",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
index 30733dbca616aa4a35576dc83c6bd8db31e276e8..7d7cb59ac973f9d8a25c62195459a98902e11bed 100644 (file)
@@ -3,8 +3,8 @@
     {
       "category": "porous-defenses",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -13,8 +13,8 @@
     {
       "category": "risky-resource",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
       "category": "insecure-interaction",
       "vulnerabilities": 1,
       "vulnerabilityRating": 3,
+      "inReviewSecurityHotspots": 1,
       "toReviewSecurityHotspots": 1,
-      "openSecurityHotspots": 1,
       "wontFixSecurityHotspots": 0,
       "distribution": [
         {
           "cwe": "89",
           "vulnerabilities": 1,
           "vulnerabilityRating": 3,
+          "inReviewSecurityHotspots": 1,
           "toReviewSecurityHotspots": 1,
-          "openSecurityHotspots": 1,
           "wontFixSecurityHotspots": 0,
           "activeRules": 1,
           "totalRules": 1
index fb15c48a6e9f21895e918c8b169eae81499caf35..f5e6c4eb70292329f8757e41a5b7132a2b5cfd97 100644 (file)
@@ -3,8 +3,8 @@
     {
       "category": "ldap-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -13,8 +13,8 @@
     {
       "category": "object-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -23,8 +23,8 @@
     {
       "category": "ssrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -33,8 +33,8 @@
     {
       "category": "insecure-conf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -43,8 +43,8 @@
     {
       "category": "xxe",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -53,8 +53,8 @@
     {
       "category": "auth",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -63,8 +63,8 @@
     {
       "category": "xpath-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -73,8 +73,8 @@
     {
       "category": "weak-cryptography",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -83,8 +83,8 @@
     {
       "category": "dos",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -93,8 +93,8 @@
     {
       "category": "open-redirect",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "log-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "csrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
       "category": "sql-injection",
       "vulnerabilities": 1,
       "vulnerabilityRating": 3,
+      "inReviewSecurityHotspots": 1,
       "toReviewSecurityHotspots": 1,
-      "openSecurityHotspots": 1,
       "wontFixSecurityHotspots": 1,
       "distribution": [],
       "activeRules": 1,
     {
       "category": "file-manipulation",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "expression-lang-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "rce",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "xss",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "path-traversal-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
     {
       "category": "command-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
     {
       "category": "http-response-splitting",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
index b565b5d309da8b82793a1ded8c61241a9baa6be6..a01609a9d19b95be5273d1a87c0a5bed7df9dd8f 100644 (file)
@@ -3,96 +3,96 @@
     {
       "category": "ldap-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "object-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "ssrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "insecure-conf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "xxe",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "auth",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "xpath-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "weak-cryptography",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "dos",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "open-redirect",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "log-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "csrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
       "category": "sql-injection",
       "vulnerabilities": 1,
       "vulnerabilityRating": 3,
+      "inReviewSecurityHotspots": 1,
       "toReviewSecurityHotspots": 1,
-      "openSecurityHotspots": 1,
       "wontFixSecurityHotspots": 1,
       "distribution": [
         {
           "cwe": "89",
           "vulnerabilities": 1,
           "vulnerabilityRating": 3,
+          "inReviewSecurityHotspots": 1,
           "toReviewSecurityHotspots": 1,
-          "openSecurityHotspots": 1,
           "wontFixSecurityHotspots": 1
         }
       ]
     {
       "category": "file-manipulation",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "expression-lang-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "rce",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "xss",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "path-traversal-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "command-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     },
     {
       "category": "http-response-splitting",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": []
     }
index 2936bdadd8bb64e8eb3fdfe5be403f65623b7f7b..61f4ca5cacd4a5097ee00743dd13b8f9a51a915a 100644 (file)
@@ -3,8 +3,8 @@
     {
       "category": "ldap-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -13,8 +13,8 @@
     {
       "category": "object-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -23,8 +23,8 @@
     {
       "category": "ssrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -33,8 +33,8 @@
     {
       "category": "insecure-conf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -43,8 +43,8 @@
     {
       "category": "xxe",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -53,8 +53,8 @@
     {
       "category": "auth",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -63,8 +63,8 @@
     {
       "category": "xpath-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -73,8 +73,8 @@
     {
       "category": "weak-cryptography",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -83,8 +83,8 @@
     {
       "category": "dos",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
@@ -93,8 +93,8 @@
     {
       "category": "open-redirect",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "log-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "csrf",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
       "category": "sql-injection",
       "vulnerabilities": 1,
       "vulnerabilityRating": 3,
+      "inReviewSecurityHotspots": 1,
       "toReviewSecurityHotspots": 1,
-      "openSecurityHotspots": 1,
       "wontFixSecurityHotspots": 1,
       "distribution": [
         {
           "cwe": "89",
           "vulnerabilities": 1,
           "vulnerabilityRating": 3,
+          "inReviewSecurityHotspots": 1,
           "toReviewSecurityHotspots": 1,
-          "openSecurityHotspots": 1,
           "wontFixSecurityHotspots": 1,
           "activeRules": 1,
           "totalRules": 1
     {
       "category": "file-manipulation",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "expression-lang-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "rce",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "xss",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
     {
       "category": "path-traversal-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
     {
       "category": "command-injection",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 1,
     {
       "category": "http-response-splitting",
       "vulnerabilities": 0,
+      "inReviewSecurityHotspots": 0,
       "toReviewSecurityHotspots": 0,
-      "openSecurityHotspots": 0,
       "wontFixSecurityHotspots": 0,
       "distribution": [],
       "activeRules": 0,
index 6bca276eec36de8b00a82ca52096bdfc45e17d87..106dfb0cbd7561d9248b46f550ad4216a3f33f7a 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.issue;
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableList;
 
 /**
  * @since 3.6
@@ -46,13 +47,18 @@ public interface DefaultTransitions {
   String DETECT = "detect";
   String DISMISS = "dismiss";
   String REJECT = "reject";
-  String REQUEST_REVIEW = "requestreview";
   String ACCEPT = "accept";
   String CLEAR = "clear";
   String REOPEN_HOTSPOT = "reopenhotspot";
 
+  /**
+   * @since 7.8
+   */
+  String SET_AS_IN_REVIEW = "setinreview";
+
   /**
    * @since 4.4
    */
-  List<String> ALL = asList(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, WONT_FIX, CLOSE, DETECT, DISMISS, REJECT, REQUEST_REVIEW, ACCEPT, CLEAR, REOPEN_HOTSPOT);
+  List<String> ALL = unmodifiableList(asList(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, WONT_FIX, CLOSE,
+    DETECT, DISMISS, REJECT, SET_AS_IN_REVIEW, ACCEPT, CLEAR, REOPEN_HOTSPOT));
 }
index c5add0eab45362d1d691374ad54043cf2d336de2..49db5e45f3190d7ef84f13eaa0acb1626733311e 100644 (file)
@@ -80,6 +80,11 @@ public interface Issue extends Serializable {
    */
   String STATUS_TO_REVIEW = "TOREVIEW";
 
+  /**
+   * @since 7.8
+   */
+  String STATUS_IN_REVIEW = "INREVIEW";
+
   /**
    * Return all available statuses
    *
index b9d9b3686ae1fd4bc7a82ef77c0980997afbd450..059bd1666da0300c07e6c60f5a597b219abbf656 100644 (file)
@@ -35,8 +35,8 @@ message SecurityStandardCategoryStatistics {
   optional string category = 1;
   optional int64 vulnerabilities = 2;
   optional int64 vulnerabilityRating = 3;
-  optional int64 toReviewSecurityHotspots = 4;
-  optional int64 openSecurityHotspots = 5;
+  optional int64 inReviewSecurityHotspots = 4;
+  optional int64 toReviewSecurityHotspots = 5;
   optional int64 wontFixSecurityHotspots = 6;
   repeated CweStatistics distribution = 7;
   optional int64 activeRules = 8;
@@ -47,8 +47,8 @@ message CweStatistics {
   optional string cwe = 1;
   optional int64 vulnerabilities = 2;
   optional int64 vulnerabilityRating = 3;
-  optional int64 toReviewSecurityHotspots = 4;
-  optional int64 openSecurityHotspots = 5;
+  optional int64 inReviewSecurityHotspots = 4;
+  optional int64 toReviewSecurityHotspots = 5;
   optional int64 wontFixSecurityHotspots = 6;
   optional int64 activeRules = 7;
   optional int64 totalRules = 8;