From: Pierre Guillot <50145663+pierre-guillot-sonarsource@users.noreply.github.com> Date: Wed, 8 May 2019 14:36:58 +0000 (+0200) Subject: SONAR-12026 Add transition "Set as In Review" on Security Hotspots X-Git-Tag: 7.8~203 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5d140eb1e005bb57f7d41dc952efdf11dead36ef;p=sonarqube.git SONAR-12026 Add transition "Set as In Review" on Security Hotspots --- diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java index 11a45cb4946..4148b57c121 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/SecurityStandardCategoryStatistics.java @@ -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 children; private long activeRules; private long totalRules; - public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long toReviewSecurityHotspots, long openSecurityHotspots, - long wontFixSecurityHotspots, @Nullable List children) { + public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long inReviewSecurityHotspots, long toReviewSecurityHotspots, + long wontFixSecurityHotspots, @Nullable List 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() { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java index 1a7cfe32e1b..eea7b1e28e7 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java @@ -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 index 00000000000..03bb3584109 --- /dev/null +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowForSecurityHotspotsTest.java @@ -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 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 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 keys(List transitions) { + return Collections2.transform(transitions, new Function() { + @Override + public String apply(@Nullable Transition transition) { + return transition.key(); + } + }); + } +} diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java index 4151f7280b6..a8b7c9998d4 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java @@ -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 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 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") diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 580edfee6aa..b2949e27210 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java index dd3cfb81bd9..972e8f7c38b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java @@ -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.
" + "The transitions '" + DefaultTransitions.WONT_FIX + "' and '" + DefaultTransitions.FALSE_POSITIVE + "' require the permission 'Administer Issues'.
" + - "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); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java index 0f7b94220e6..1e93e6ff96c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java @@ -56,7 +56,8 @@ public interface QGChangeEventListener { RESOLVED_FP, RESOLVED_WF, RESOLVED_FIXED, - TO_REVIEW; + TO_REVIEW, + IN_REVIEW; protected static final Set CLOSED_STATUSES = EnumSet.of(CONFIRMED, RESOLVED_FIXED, RESOLVED_FP, RESOLVED_WF); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java index e1100812ca9..ba51aef85fd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java @@ -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: diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java index bc00cddd508..ce04036104c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.java @@ -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 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 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 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 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 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 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()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java index 021a872cefb..21961822894 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java @@ -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) { diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json index 4c8399afeb0..c7c300c51a5 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/empty.json @@ -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, @@ -103,8 +103,8 @@ { "category": "unknown", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json index 99e1e68464f..3e4614196f9 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/owaspNoCwe.json @@ -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, @@ -104,8 +104,8 @@ { "category": "unknown", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json index 30733dbca61..7d7cb59ac97 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sansWithCwe.json @@ -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, @@ -24,16 +24,16 @@ "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 diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json index fb15c48a6e9..f5e6c4eb702 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityNoCwe.json @@ -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, @@ -103,8 +103,8 @@ { "category": "log-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -113,8 +113,8 @@ { "category": "csrf", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -124,8 +124,8 @@ "category": "sql-injection", "vulnerabilities": 1, "vulnerabilityRating": 3, + "inReviewSecurityHotspots": 1, "toReviewSecurityHotspots": 1, - "openSecurityHotspots": 1, "wontFixSecurityHotspots": 1, "distribution": [], "activeRules": 1, @@ -134,8 +134,8 @@ { "category": "file-manipulation", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -144,8 +144,8 @@ { "category": "expression-lang-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -154,8 +154,8 @@ { "category": "rce", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -164,8 +164,8 @@ { "category": "xss", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -174,8 +174,8 @@ { "category": "path-traversal-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, @@ -184,8 +184,8 @@ { "category": "command-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, @@ -194,8 +194,8 @@ { "category": "http-response-splitting", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json index b565b5d309d..a01609a9d19 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityOnApplication.json @@ -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": [] }, @@ -100,16 +100,16 @@ "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 } ] @@ -117,56 +117,56 @@ { "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": [] } diff --git a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json index 2936bdadd8b..61f4ca5cacd 100644 --- a/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json +++ b/server/sonar-server/src/test/resources/com/sonar/governance/securityreport/ws/ShowActionTest/sonarsourceSecurityWithCwe.json @@ -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, @@ -103,8 +103,8 @@ { "category": "log-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -113,8 +113,8 @@ { "category": "csrf", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -124,16 +124,16 @@ "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 @@ -145,8 +145,8 @@ { "category": "file-manipulation", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -155,8 +155,8 @@ { "category": "expression-lang-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -165,8 +165,8 @@ { "category": "rce", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -175,8 +175,8 @@ { "category": "xss", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, @@ -185,8 +185,8 @@ { "category": "path-traversal-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, @@ -195,8 +195,8 @@ { "category": "command-injection", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 1, @@ -205,8 +205,8 @@ { "category": "http-response-splitting", "vulnerabilities": 0, + "inReviewSecurityHotspots": 0, "toReviewSecurityHotspots": 0, - "openSecurityHotspots": 0, "wontFixSecurityHotspots": 0, "distribution": [], "activeRules": 0, diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java index 6bca276eec3..106dfb0cbd7 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/DefaultTransitions.java @@ -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 ALL = asList(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, WONT_FIX, CLOSE, DETECT, DISMISS, REJECT, REQUEST_REVIEW, ACCEPT, CLEAR, REOPEN_HOTSPOT); + List ALL = unmodifiableList(asList(CONFIRM, UNCONFIRM, REOPEN, RESOLVE, FALSE_POSITIVE, WONT_FIX, CLOSE, + DETECT, DISMISS, REJECT, SET_AS_IN_REVIEW, ACCEPT, CLEAR, REOPEN_HOTSPOT)); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java index c5add0eab45..49db5e45f31 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java @@ -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 * diff --git a/sonar-ws/src/main/protobuf/ws-security.proto b/sonar-ws/src/main/protobuf/ws-security.proto index b9d9b3686ae..059bd1666da 100644 --- a/sonar-ws/src/main/protobuf/ws-security.proto +++ b/sonar-ws/src/main/protobuf/ws-security.proto @@ -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;