diff options
author | Antoine Vinot <antoine.vinot@sonarsource.com> | 2024-12-18 19:31:03 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-12-18 20:03:12 +0000 |
commit | 2d1ebbaab1f390b6bd677a4d8796a37c2c7b38b9 (patch) | |
tree | 762601133646b875149d6428cce1c7f8779dd490 | |
parent | cb59c36c030f93c0ebe93ea6425bf8c0057fde34 (diff) | |
download | sonarqube-2d1ebbaab1f390b6bd677a4d8796a37c2c7b38b9.tar.gz sonarqube-2d1ebbaab1f390b6bd677a4d8796a37c2c7b38b9.zip |
SONAR-15049 Taint Vulnerabilities should be reopened if new flows are created
16 files changed, 172 insertions, 76 deletions
diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index 2d2a032c41d..0550bead69c 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -95,6 +95,7 @@ import org.sonar.server.extension.CoreExtensionStopper; import org.sonar.server.favorite.FavoriteUpdater; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.IssueStorage; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.issue.index.IssueIteratorFactory; import org.sonar.server.issue.notification.IssuesChangesNotificationModule; @@ -395,6 +396,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { IssueIteratorFactory.class, IssueFieldsSetter.class, // used in Web Services and CE's DebtCalculator FunctionExecutor.class, // used by IssueWorkflow + TaintChecker.class, IssueWorkflow.class, // used in Web Services and CE's DebtCalculator NewIssuesEmailTemplate.class, MyNewIssuesEmailTemplate.class, diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/TaintChecker.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/TaintChecker.java index 8dd24d57dab..da5f22e5694 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/TaintChecker.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/TaintChecker.java @@ -89,7 +89,6 @@ public class TaintChecker { return repositories; } - public boolean isTaintVulnerability(DefaultIssue issue) { return taintRepositories.contains(issue.getRuleKey().repository()) && issue.getLocations() != null diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Condition.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Condition.java index 603c718004a..985d37722e9 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Condition.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Condition.java @@ -21,6 +21,7 @@ package org.sonar.server.issue.workflow; import org.sonar.api.issue.Issue; +@FunctionalInterface public interface Condition { boolean matches(Issue issue); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Function.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Function.java index 69a4e17de6c..4d5ad54e4a1 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Function.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/workflow/Function.java @@ -24,6 +24,7 @@ import org.sonar.api.issue.Issue; import org.sonar.api.rules.RuleType; import org.sonar.db.user.UserDto; +@FunctionalInterface interface Function { interface Context { Issue issue(); 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 f9127dadc6e..813594eb30f 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 @@ -29,8 +29,10 @@ import org.sonar.api.rules.RuleType; import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.IssueChangeContext; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -55,11 +57,13 @@ public class IssueWorkflow implements Startable { private static final String AUTOMATIC_CLOSE_TRANSITION = "automaticclose"; private final FunctionExecutor functionExecutor; private final IssueFieldsSetter updater; + private final TaintChecker taintChecker; private StateMachine machine; - public IssueWorkflow(FunctionExecutor functionExecutor, IssueFieldsSetter updater) { + public IssueWorkflow(FunctionExecutor functionExecutor, IssueFieldsSetter updater, TaintChecker taintChecker) { this.functionExecutor = functionExecutor; this.updater = updater; + this.taintChecker = taintChecker; } @Override @@ -238,7 +242,7 @@ public class IssueWorkflow implements Startable { .build()); } - private static void buildAutomaticTransitions(StateMachine.Builder builder) { + private void buildAutomaticTransitions(StateMachine.Builder builder) { // Close the "end of life" issues (disabled/deleted rule, deleted component) builder .transition(Transition.builder(AUTOMATIC_CLOSE_TRANSITION) @@ -277,7 +281,6 @@ public class IssueWorkflow implements Startable { .functions(SetClosed.INSTANCE, SetCloseDate.INSTANCE) .automatic() .build()) - // Reopen issues that are marked as resolved but that are still alive. .transition(Transition.builder("automaticreopen") .from(STATUS_RESOLVED).to(STATUS_REOPENED) @@ -285,7 +288,6 @@ public class IssueWorkflow implements Startable { .functions(new SetResolution(null), UnsetCloseDate.INSTANCE) .automatic() .build()) - .transition(Transition.builder("automaticuncloseopen") .from(STATUS_CLOSED).to(STATUS_OPEN) .conditions( @@ -322,7 +324,6 @@ public class IssueWorkflow implements Startable { .functions(RestoreResolutionFunction.INSTANCE, UnsetCloseDate.INSTANCE) .automatic() .build()) - // reopen closed hotspots .transition(Transition.builder("automaticunclosetoreview") .from(STATUS_CLOSED).to(STATUS_TO_REVIEW) @@ -341,7 +342,29 @@ public class IssueWorkflow implements Startable { IsHotspot.INSTANCE) .functions(RestoreResolutionFunction.INSTANCE, UnsetCloseDate.INSTANCE) .automatic() - .build()); + .build()) + .transition(reopenTaintVulnOnFlowChanged()); + } + + private Transition reopenTaintVulnOnFlowChanged() { + return Transition.builder("reopentaintvulnerability") + .from(STATUS_RESOLVED) + .to(STATUS_REOPENED) + .conditions( + issue -> taintChecker.isTaintVulnerability((DefaultIssue) issue), + issue -> ((DefaultIssue) issue).locationsChanged()) + .functions( + Function.Context::unsetCloseDate, + context -> context.setResolution(null), + IssueWorkflow::commentOnTaintVulnReopened) + .automatic() + .build(); + } + + private static void commentOnTaintVulnReopened(Function.Context context) { + DefaultIssue issue = (DefaultIssue) context.issue(); + DefaultIssueComment defaultIssueComment = DefaultIssueComment.create(issue.key(), "Automatically reopened because the vulnerability flow changed."); + issue.addComment(defaultIssueComment); } @Override 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 index 52b3abd9e10..d01e7a4a8ad 100644 --- 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 @@ -37,9 +37,11 @@ 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 org.sonar.server.issue.TaintChecker; import static org.apache.commons.lang3.RandomStringUtils.secure; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.sonar.api.issue.DefaultTransitions.RESET_AS_TO_REVIEW; import static org.sonar.api.issue.DefaultTransitions.RESOLVE_AS_ACKNOWLEDGED; import static org.sonar.api.issue.DefaultTransitions.RESOLVE_AS_REVIEWED; @@ -63,7 +65,7 @@ public class IssueWorkflowForSecurityHotspotsTest { private static final List<String> RESOLUTION_TYPES = List.of(RESOLUTION_FIXED, RESOLUTION_SAFE, RESOLUTION_ACKNOWLEDGED); private final IssueFieldsSetter updater = new IssueFieldsSetter(); - private final IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater); + private final IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater, mock(TaintChecker.class)); @Test @UseDataProvider("anyResolutionIncludingNone") 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 52bbc77b442..01a1eea0822 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 @@ -20,9 +20,6 @@ package org.sonar.server.issue.workflow; 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; @@ -30,22 +27,29 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Random; +import java.util.stream.Stream; import javax.annotation.Nullable; import org.apache.commons.lang3.time.DateUtils; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.api.issue.DefaultTransitions; +import org.sonar.api.issue.IssueStatus; import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.FieldDiffs; import org.sonar.core.issue.IssueChangeContext; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.time.DateUtils.addDays; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE; import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; import static org.sonar.api.issue.Issue.RESOLUTION_REMOVED; @@ -59,15 +63,25 @@ import static org.sonar.api.issue.Issue.STATUS_REVIEWED; import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW; import static org.sonar.core.issue.IssueChangeContext.issueChangeContextByScanBuilder; -@RunWith(DataProviderRunner.class) -public class IssueWorkflowTest { +class IssueWorkflowTest { - private IssueFieldsSetter updater = new IssueFieldsSetter(); + 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, + RESOLUTION_FIXED, + RESOLUTION_WONT_FIX, + RESOLUTION_FALSE_POSITIVE + }; + private static final String[] SUPPORTED_RESOLUTIONS_FOR_UNCLOSING = new String[] {RESOLUTION_FIXED, RESOLUTION_REMOVED}; + + private final IssueFieldsSetter updater = new IssueFieldsSetter(); - private IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater); + private final TaintChecker taintChecker = mock(TaintChecker.class); + + private final IssueWorkflow underTest = new IssueWorkflow(new FunctionExecutor(updater), updater, taintChecker); @Test - public void list_statuses() { + void list_statuses() { underTest.start(); List<String> expectedStatus = new ArrayList<>(); @@ -80,7 +94,7 @@ public class IssueWorkflowTest { } @Test - public void list_out_transitions_from_status_open() { + void list_out_transitions_from_status_open() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus(STATUS_OPEN); @@ -89,7 +103,7 @@ public class IssueWorkflowTest { } @Test - public void list_out_transitions_from_status_confirmed() { + void list_out_transitions_from_status_confirmed() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus(STATUS_CONFIRMED); @@ -98,7 +112,7 @@ public class IssueWorkflowTest { } @Test - public void list_out_transitions_from_status_resolved() { + void list_out_transitions_from_status_resolved() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus(STATUS_RESOLVED); @@ -107,7 +121,7 @@ public class IssueWorkflowTest { } @Test - public void list_out_transitions_from_status_reopen() { + void list_out_transitions_from_status_reopen() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus(STATUS_REOPENED); @@ -116,7 +130,7 @@ public class IssueWorkflowTest { } @Test - public void list_no_out_transition_from_status_closed() { + void list_no_out_transition_from_status_closed() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus(STATUS_CLOSED).setRuleKey(RuleKey.of("java", "R1 ")); @@ -125,7 +139,7 @@ public class IssueWorkflowTest { } @Test - public void fail_if_unknown_status_when_listing_transitions() { + void fail_if_unknown_status_when_listing_transitions() { underTest.start(); DefaultIssue issue = new DefaultIssue().setStatus("xxx"); @@ -138,7 +152,7 @@ public class IssueWorkflowTest { } @Test - public void automatically_close_resolved_issue() { + void automatically_close_resolved_issue() { underTest.start(); DefaultIssue issue = new DefaultIssue() @@ -156,9 +170,9 @@ public class IssueWorkflowTest { 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) { + @ParameterizedTest + @MethodSource("allStatusesLeadingToClosed") + void automatically_reopen_closed_issue_to_its_previous_status_from_changelog(String previousStatus) { DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(resolution -> { DefaultIssue issue = newClosedIssue(resolution); @@ -179,9 +193,9 @@ public class IssueWorkflowTest { }); } - @Test - @UseDataProvider("allStatusesLeadingToClosed") - public void automatically_reopen_closed_issue_to_most_recent_previous_status_from_changelog(String previousStatus) { + @ParameterizedTest + @MethodSource("allStatusesLeadingToClosed") + void automatically_reopen_closed_issue_to_most_recent_previous_status_from_changelog(String previousStatus) { DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(resolution -> { DefaultIssue issue = newClosedIssue(resolution); @@ -205,9 +219,9 @@ public class IssueWorkflowTest { }); } - @Test - @UseDataProvider("allResolutionsBeforeClosing") - public void automatically_reopen_closed_issue_to_previous_resolution_from_changelog(String resolutionBeforeClosed) { + @ParameterizedTest + @MethodSource("allResolutionsBeforeClosing") + void automatically_reopen_closed_issue_to_previous_resolution_from_changelog(String resolutionBeforeClosed) { String randomPreviousStatus = ALL_STATUSES_LEADING_TO_CLOSED[new Random().nextInt(ALL_STATUSES_LEADING_TO_CLOSED.length)]; DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(resolution -> { @@ -231,7 +245,7 @@ public class IssueWorkflowTest { } @Test - public void automatically_reopen_closed_issue_to_no_resolution_if_no_previous_one_changelog() { + void automatically_reopen_closed_issue_to_no_resolution_if_no_previous_one_changelog() { String randomPreviousStatus = ALL_STATUSES_LEADING_TO_CLOSED[new Random().nextInt(ALL_STATUSES_LEADING_TO_CLOSED.length)]; DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(resolution -> { @@ -254,9 +268,9 @@ public class IssueWorkflowTest { }); } - @Test - @UseDataProvider("allResolutionsBeforeClosing") - public void automatically_reopen_closed_issue_to_previous_resolution_of_closing_the_issue_if_most_recent_of_all_resolution_changes(String resolutionBeforeClosed) { + @ParameterizedTest + @MethodSource("allResolutionsBeforeClosing") + void automatically_reopen_closed_issue_to_previous_resolution_of_closing_the_issue_if_most_recent_of_all_resolution_changes(String resolutionBeforeClosed) { String randomPreviousStatus = ALL_STATUSES_LEADING_TO_CLOSED[new Random().nextInt(ALL_STATUSES_LEADING_TO_CLOSED.length)]; DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(resolution -> { @@ -282,15 +296,12 @@ public class IssueWorkflowTest { }); } - @DataProvider - public static Object[][] allResolutionsBeforeClosing() { - return Arrays.stream(ALL_RESOLUTIONS_BEFORE_CLOSING) - .map(t -> new Object[] {t}) - .toArray(Object[][]::new); + static Stream<String> allResolutionsBeforeClosing() { + return Stream.of(ALL_RESOLUTIONS_BEFORE_CLOSING); } @Test - public void do_not_automatically_reopen_closed_issue_which_have_no_previous_status_in_changelog() { + void do_not_automatically_reopen_closed_issue_which_have_no_previous_status_in_changelog() { DefaultIssue[] issues = Arrays.stream(SUPPORTED_RESOLUTIONS_FOR_UNCLOSING) .map(IssueWorkflowTest::newClosedIssue) .toArray(DefaultIssue[]::new); @@ -305,25 +316,70 @@ public class IssueWorkflowTest { }); } - 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, - RESOLUTION_FIXED, - RESOLUTION_WONT_FIX, - RESOLUTION_FALSE_POSITIVE - }; + @Test + void automatically_reopen_taint_vulnerability_when_flow_changed() { + DefaultIssue issue = new DefaultIssue() + .setKey("issue_key") + .setRuleKey(RuleKey.of("xoo", "S001")) + .setStatus(STATUS_RESOLVED) + .setResolution(RESOLUTION_FALSE_POSITIVE) + .setLocationsChanged(true) + .setNew(false); + when(taintChecker.isTaintVulnerability(issue)) + .thenReturn(true); - private static final String[] SUPPORTED_RESOLUTIONS_FOR_UNCLOSING = new String[] {RESOLUTION_FIXED, RESOLUTION_REMOVED}; + underTest.start(); + underTest.doAutomaticTransition(issue, issueChangeContextByScanBuilder(new Date()).build()); + + assertThat(issue.issueStatus()).isEqualTo(IssueStatus.OPEN); + List<DefaultIssueComment> issueComments = issue.defaultIssueComments(); + assertThat(issueComments).hasSize(1); + DefaultIssueComment defaultIssueComment = issueComments.get(0); + assertThat(defaultIssueComment.markdownText()).isEqualTo("Automatically reopened because the vulnerability flow changed."); + } + + @Test + void do_not_automatically_reopen_issue_when_flow_changed_but_not_taint_vulnerability() { + DefaultIssue issue = new DefaultIssue() + .setKey("issue_key") + .setRuleKey(RuleKey.of("xoo", "S001")) + .setStatus(STATUS_RESOLVED) + .setResolution(RESOLUTION_FALSE_POSITIVE) + .setLocationsChanged(true) + .setNew(false); + when(taintChecker.isTaintVulnerability(issue)) + .thenReturn(false); + + underTest.start(); + underTest.doAutomaticTransition(issue, issueChangeContextByScanBuilder(new Date()).build()); + + assertThat(issue.issueStatus()).isEqualTo(IssueStatus.FALSE_POSITIVE); + } + + @Test + void do_not_automatically_reopen_taint_vulnerability_when_flow_did_not_change() { + DefaultIssue issue = new DefaultIssue() + .setKey("issue_key") + .setRuleKey(RuleKey.of("xoo", "S001")) + .setStatus(STATUS_RESOLVED) + .setResolution(RESOLUTION_FALSE_POSITIVE) + .setLocationsChanged(false) + .setNew(false); + when(taintChecker.isTaintVulnerability(issue)) + .thenReturn(true); + + underTest.start(); + underTest.doAutomaticTransition(issue, issueChangeContextByScanBuilder(new Date()).build()); + + assertThat(issue.issueStatus()).isEqualTo(IssueStatus.FALSE_POSITIVE); + } - @DataProvider - public static Object[][] allStatusesLeadingToClosed() { - return Arrays.stream(ALL_STATUSES_LEADING_TO_CLOSED) - .map(t -> new Object[]{t}) - .toArray(Object[][]::new); + static Stream<String> allStatusesLeadingToClosed() { + return Stream.of(ALL_STATUSES_LEADING_TO_CLOSED); } @Test - public void close_open_dead_issue() { + void close_open_dead_issue() { underTest.start(); DefaultIssue issue = new DefaultIssue() @@ -341,7 +397,7 @@ public class IssueWorkflowTest { } @Test - public void close_reopened_dead_issue() { + void close_reopened_dead_issue() { underTest.start(); DefaultIssue issue = new DefaultIssue() @@ -359,7 +415,7 @@ public class IssueWorkflowTest { } @Test - public void close_confirmed_dead_issue() { + void close_confirmed_dead_issue() { underTest.start(); DefaultIssue issue = new DefaultIssue() @@ -377,7 +433,7 @@ public class IssueWorkflowTest { } @Test - public void fail_if_unknown_status_on_automatic_trans() { + void fail_if_unknown_status_on_automatic_trans() { underTest.start(); DefaultIssue issue = new DefaultIssue() @@ -394,7 +450,7 @@ public class IssueWorkflowTest { } @Test - public void flag_as_false_positive() { + void flag_as_false_positive() { DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setStatus(STATUS_OPEN) @@ -412,7 +468,7 @@ public class IssueWorkflowTest { } @Test - public void wont_fix() { + void wont_fix() { DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setStatus(STATUS_OPEN) @@ -430,7 +486,7 @@ public class IssueWorkflowTest { } @Test - public void doManualTransition_shouldTransitionToResolutionWontFix_whenAccepted() { + void doManualTransition_shouldTransitionToResolutionWontFix_whenAccepted() { DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setStatus(STATUS_OPEN) diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionActionIT.java index 59aa812c38c..5ee187c8f9d 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionActionIT.java @@ -58,7 +58,7 @@ public class TransitionActionIT { public UserSessionRule userSession = UserSessionRule.standalone(); private final IssueFieldsSetter updater = new IssueFieldsSetter(); - private final IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater); + private final IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater, mock(TaintChecker.class)); private final TransitionService transitionService = new TransitionService(userSession, workflow); private final Action.Context context = mock(Action.Context.class); private final DefaultIssue issue = newIssue().toDefaultIssue(); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionServiceIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionServiceIT.java index 93568596174..ea4a458d05b 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionServiceIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/TransitionServiceIT.java @@ -36,6 +36,7 @@ import org.sonar.server.issue.workflow.Transition; import org.sonar.server.tester.UserSessionRule; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.sonar.api.issue.Issue.STATUS_CONFIRMED; import static org.sonar.api.issue.Issue.STATUS_OPEN; import static org.sonar.api.rules.RuleType.CODE_SMELL; @@ -51,7 +52,7 @@ public class TransitionServiceIT { public UserSessionRule userSession = UserSessionRule.standalone(); private IssueFieldsSetter updater = new IssueFieldsSetter(); - private IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater); + private IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater, mock(TaintChecker.class)); private TransitionService underTest = new TransitionService(userSession, workflow); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/BulkChangeActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/BulkChangeActionIT.java index cbda06ae6a1..9829b3f3c22 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/BulkChangeActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/BulkChangeActionIT.java @@ -47,6 +47,7 @@ import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.Action; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TestIssueChangePostProcessor; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.WebIssueStorage; @@ -120,7 +121,7 @@ public class BulkChangeActionIT { private IssueChangeEventService issueChangeEventService = mock(IssueChangeEventService.class); private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); - private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter, mock(TaintChecker.class)); private WebIssueStorage issueStorage = new WebIssueStorage(system2, dbClient, new DefaultRuleFinder(dbClient, mock(RuleDescriptionFormatter.class)), new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), null), new SequenceUuidFactory()); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/DoTransitionActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/DoTransitionActionIT.java index 4e557120ac2..d2430df5aeb 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/DoTransitionActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/DoTransitionActionIT.java @@ -42,6 +42,7 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.IssueFinder; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TestIssueChangePostProcessor; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.WebIssueStorage; @@ -95,7 +96,7 @@ public class DoTransitionActionIT { private IssueChangeEventService issueChangeEventService = mock(IssueChangeEventService.class); private IssueFieldsSetter updater = new IssueFieldsSetter(); - private IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater); + private IssueWorkflow workflow = new IssueWorkflow(new FunctionExecutor(updater), updater, mock(TaintChecker.class)); private TransitionService transitionService = new TransitionService(userSession, workflow); private OperationResponseWriter responseWriter = mock(OperationResponseWriter.class); private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), null); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/ListActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/ListActionIT.java index 4606f7936cb..9e4416b0751 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/ListActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/ListActionIT.java @@ -51,6 +51,7 @@ import org.sonar.server.component.TestComponentFinder; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.NewCodePeriodResolver; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TextRangeResponseFormatter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.workflow.FunctionExecutor; @@ -68,6 +69,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.groups.Tuple.tuple; +import static org.mockito.Mockito.mock; import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX; import static org.sonar.api.issue.Issue.STATUS_CLOSED; @@ -102,7 +104,7 @@ public class ListActionIT { private final DbClient dbClient = db.getDbClient(); private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); - private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter, mock(TaintChecker.class)); private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); private final Languages languages = new Languages(); private final UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl()); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionComponentsIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionComponentsIT.java index 462d716dc40..4b948491032 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionComponentsIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionComponentsIT.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Date; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.mockito.Mockito; import org.sonar.api.config.Configuration; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; @@ -42,6 +41,7 @@ import org.sonar.db.rule.RuleDto; import org.sonar.server.common.avatar.AvatarResolverImpl; import org.sonar.server.es.EsTester; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TextRangeResponseFormatter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.index.IssueIndex; @@ -64,6 +64,7 @@ import org.sonarqube.ws.Issues.SearchWsResponse; import static org.apache.commons.lang3.RandomStringUtils.secure; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.Mockito.mock; import static org.sonar.api.utils.DateUtils.addDays; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.api.web.UserRole.USER; @@ -92,8 +93,8 @@ class SearchActionComponentsIT { private final DbTester db = DbTester.create(); @RegisterExtension private final EsTester es = EsTester.create(); - - private final Configuration config = Mockito.mock(Configuration.class); + + private final Configuration config = mock(Configuration.class); private final DbClient dbClient = db.getDbClient(); private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession), config); @@ -101,7 +102,7 @@ class SearchActionComponentsIT { private final ViewIndexer viewIndexer = new ViewIndexer(dbClient, es.client()); private final IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession); private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); - private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter, mock(TaintChecker.class)); private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); private final Languages languages = new Languages(); private final UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl()); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java index b564e5f5352..bae8a1ba30d 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionDependenciesIT.java @@ -40,6 +40,7 @@ import org.sonar.db.rule.RuleDto; import org.sonar.server.common.avatar.AvatarResolverImpl; import org.sonar.server.es.EsTester; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TextRangeResponseFormatter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.index.AsyncIssueIndexing; @@ -78,7 +79,7 @@ class SearchActionDependenciesIT { private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), mock(AsyncIssueIndexing.class)); private final IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession); private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); - private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter, mock(TaintChecker.class)); private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); private final Languages languages = new Languages(); private final UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl()); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java index 66fd1979232..8c408f44ce3 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java @@ -77,6 +77,7 @@ import org.sonar.server.common.avatar.AvatarResolverImpl; import org.sonar.server.es.EsTester; import org.sonar.server.es.SearchOptions; import org.sonar.server.issue.IssueFieldsSetter; +import org.sonar.server.issue.TaintChecker; import org.sonar.server.issue.TextRangeResponseFormatter; import org.sonar.server.issue.TransitionService; import org.sonar.server.issue.index.IssueIndex; @@ -179,7 +180,7 @@ class SearchActionIT { private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), null); private final IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession); private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter(); - private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter); + private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter, mock(TaintChecker.class)); private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow)); private final Languages languages = new Languages(); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueComment.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueComment.java index 9f548926fcc..ec253c71268 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueComment.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueComment.java @@ -40,6 +40,10 @@ public class DefaultIssueComment implements Serializable { private String markdownText; private boolean isNew; + public static DefaultIssueComment create(String issueKey, String markdownText) { + return create(issueKey, null, markdownText); + } + public static DefaultIssueComment create(String issueKey, @Nullable String userUuid, String markdownText) { DefaultIssueComment comment = new DefaultIssueComment(); comment.setIssueKey(issueKey); |