From 23aadee2b2cfe4ef9765c93c663287d2e3a3a551 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 15 Dec 2022 17:24:45 -0500 Subject: [PATCH 1/9] [JENKINS-46795] `TrustworthyBuild` extension point --- src/main/java/jenkins/scm/api/SCMSource.java | 26 +++++++ .../jenkins/scm/api/TrustworthyBuild.java | 68 +++++++++++++++++++ .../jenkins/scm/impl/TrustworthyBuilds.java | 40 +++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/main/java/jenkins/scm/api/TrustworthyBuild.java create mode 100644 src/main/java/jenkins/scm/impl/TrustworthyBuilds.java diff --git a/src/main/java/jenkins/scm/api/SCMSource.java b/src/main/java/jenkins/scm/api/SCMSource.java index 36b6925b..e4a47b27 100644 --- a/src/main/java/jenkins/scm/api/SCMSource.java +++ b/src/main/java/jenkins/scm/api/SCMSource.java @@ -77,6 +77,8 @@ public abstract class SCMSource extends AbstractDescribableImpl implements ExtensionPoint { + private static final Logger LOGGER = Logger.getLogger(SCMSource.class.getName()); + /** * Replaceable pronoun of that points to a {@link SCMSource}. Defaults to {@code null} depending on the context. * @since 2.0 @@ -956,6 +958,7 @@ public final SCM build(@NonNull SCMHead head) { * @throws IOException in case the implementation must call {@link #fetch(SCMHead, TaskListener)} or similar * @throws InterruptedException in case the implementation must call {@link #fetch(SCMHead, TaskListener)} or similar * @since 1.1 + * @see #getTrustedRevisionForBuild */ @NonNull public SCMRevision getTrustedRevision(@NonNull SCMRevision revision, @NonNull TaskListener listener) @@ -963,6 +966,29 @@ public SCMRevision getTrustedRevision(@NonNull SCMRevision revision, @NonNull Ta return revision; } + /** + * Refined version of {@link #getTrustedRevision(SCMRevision, TaskListener)} that takes into account the build context. + * @param build a running build + * @return {@link #getTrustedRevision} if the build itself does not indicate trust; + * simply {@code revision} if any {@link TrustworthyBuild} says that it does + */ + @NonNull + public final SCMRevision getTrustedRevisionForBuild(@NonNull SCMRevision revision, @NonNull TaskListener listener, @NonNull Run build) + throws IOException, InterruptedException { + if (ExtensionList.lookup(TrustworthyBuild.class).stream().anyMatch(tb -> tb.shouldBeTrusted(build))) { + LOGGER.fine(() -> build + " with " + build.getCauses() + " was considered trustworthy, so using " + revision + " as is"); + return revision; + } else { + SCMRevision trustedRevision = getTrustedRevision(revision, listener); + if (trustedRevision.equals(revision)) { + LOGGER.fine(() -> build + " was not considered trustworthy, but " + revision + " was trusted anyway"); + } else { + LOGGER.fine(() -> build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); + } + return trustedRevision; + } + } + /** * Turns a possibly {@code null} {@link TaskListener} reference into a guaranteed non-null reference. * diff --git a/src/main/java/jenkins/scm/api/TrustworthyBuild.java b/src/main/java/jenkins/scm/api/TrustworthyBuild.java new file mode 100644 index 00000000..9c7ce4f9 --- /dev/null +++ b/src/main/java/jenkins/scm/api/TrustworthyBuild.java @@ -0,0 +1,68 @@ +/* + * The MIT License + * + * Copyright 2022 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.scm.api; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.ExtensionPoint; +import hudson.model.Cause; +import hudson.model.Run; +import hudson.triggers.SCMTrigger; +import hudson.triggers.TimerTrigger; + +/** + * Allows plugins to declare that builds were triggered deliberately. + * This allows an authorized user to run CI on (say) a pull request filed by an outsider, + * having confirmed that there is nothing malicious about the contents. + * @see SCMSource#getTrustedRevisionForBuild + */ +public interface TrustworthyBuild extends ExtensionPoint { + + /** + * Should this build be trusted to load sensitive source files? + * If any implementation returns true then it is trusted. + */ + boolean shouldBeTrusted(@NonNull Run build); + + /** + * Convenience for the common case that a particular trigger cause indicates trust. + * Examples of causes which could be trusted include: + * + * Examples of causes which should not be registered include: + * + */ + static TrustworthyBuild byCause(Class causeType) { + return build -> build.getCause(causeType) != null; + } + +} diff --git a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java new file mode 100644 index 00000000..eb5acb29 --- /dev/null +++ b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java @@ -0,0 +1,40 @@ +/* + * The MIT License + * + * Copyright 2022 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package jenkins.scm.impl; + +import hudson.Extension; +import hudson.model.Cause; +import jenkins.scm.api.TrustworthyBuild; + +public class TrustworthyBuilds { + + @Extension + public static TrustworthyBuild byUserId() { + return TrustworthyBuild.byCause(Cause.UserIdCause.class); + } + + private TrustworthyBuilds() {} + +} From 4d9ad1a1c47c946ced6ef12eb5d12681e78f5fd7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 21 Dec 2022 15:34:04 -0500 Subject: [PATCH 2/9] Trust `CheckRunGHEventSubscriber.GitHubChecksRerunActionCause` --- src/main/java/jenkins/scm/impl/TrustworthyBuilds.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java index eb5acb29..128d3e06 100644 --- a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java +++ b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java @@ -30,11 +30,18 @@ public class TrustworthyBuilds { + // Also effectively handles ReplayCause since that is only ever added in conjunction with UserIdCause. (see ReplayAction.run2) @Extension public static TrustworthyBuild byUserId() { return TrustworthyBuild.byCause(Cause.UserIdCause.class); } + // TODO until github-checks can declare a dep on a sufficiently new scm-api + @Extension + public static TrustworthyBuild byGitHubChecks() { + return build -> build.getCauses().stream().anyMatch(cause -> cause.getClass().getName().equals("io.jenkins.plugins.checks.github.CheckRunGHEventSubscriber$GitHubChecksRerunActionCause")); + } + private TrustworthyBuilds() {} } From 6e7c68f4b640fc3a2ee12ccb4982cf31b4893603 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 22 Dec 2022 16:58:51 -0500 Subject: [PATCH 3/9] Restrict `UserIdCause` trust to those with `Item.CONFIGURE` https://github.com/jenkinsci/scm-api-plugin/pull/180#discussion_r1055695870 --- src/main/java/jenkins/scm/api/SCMSource.java | 2 +- .../jenkins/scm/api/TrustworthyBuild.java | 11 ++---- .../jenkins/scm/impl/TrustworthyBuilds.java | 37 ++++++++++++++++++- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/main/java/jenkins/scm/api/SCMSource.java b/src/main/java/jenkins/scm/api/SCMSource.java index e4a47b27..b417a376 100644 --- a/src/main/java/jenkins/scm/api/SCMSource.java +++ b/src/main/java/jenkins/scm/api/SCMSource.java @@ -975,7 +975,7 @@ public SCMRevision getTrustedRevision(@NonNull SCMRevision revision, @NonNull Ta @NonNull public final SCMRevision getTrustedRevisionForBuild(@NonNull SCMRevision revision, @NonNull TaskListener listener, @NonNull Run build) throws IOException, InterruptedException { - if (ExtensionList.lookup(TrustworthyBuild.class).stream().anyMatch(tb -> tb.shouldBeTrusted(build))) { + if (ExtensionList.lookup(TrustworthyBuild.class).stream().anyMatch(tb -> tb.shouldBeTrusted(build, listener))) { LOGGER.fine(() -> build + " with " + build.getCauses() + " was considered trustworthy, so using " + revision + " as is"); return revision; } else { diff --git a/src/main/java/jenkins/scm/api/TrustworthyBuild.java b/src/main/java/jenkins/scm/api/TrustworthyBuild.java index 9c7ce4f9..9bdf08bf 100644 --- a/src/main/java/jenkins/scm/api/TrustworthyBuild.java +++ b/src/main/java/jenkins/scm/api/TrustworthyBuild.java @@ -28,6 +28,7 @@ import hudson.ExtensionPoint; import hudson.model.Cause; import hudson.model.Run; +import hudson.model.TaskListener; import hudson.triggers.SCMTrigger; import hudson.triggers.TimerTrigger; @@ -43,16 +44,10 @@ public interface TrustworthyBuild extends ExtensionPoint { * Should this build be trusted to load sensitive source files? * If any implementation returns true then it is trusted. */ - boolean shouldBeTrusted(@NonNull Run build); + boolean shouldBeTrusted(@NonNull Run build, @NonNull TaskListener listener); /** * Convenience for the common case that a particular trigger cause indicates trust. - * Examples of causes which could be trusted include: - *
    - *
  • {@link Cause.UserIdCause} - *
  • {@code ReplayCause} - *
  • {@code CheckRunGHEventSubscriber.GitHubChecksRerunActionCause} - *
* Examples of causes which should not be registered include: *
    *
  • {@link TimerTrigger.TimerTriggerCause} @@ -62,7 +57,7 @@ public interface TrustworthyBuild extends ExtensionPoint { *
*/ static TrustworthyBuild byCause(Class causeType) { - return build -> build.getCause(causeType) != null; + return (build, listener) -> build.getCause(causeType) != null; } } diff --git a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java index 128d3e06..1f2de667 100644 --- a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java +++ b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java @@ -26,20 +26,53 @@ import hudson.Extension; import hudson.model.Cause; +import hudson.model.Item; +import hudson.model.User; import jenkins.scm.api.TrustworthyBuild; +import org.springframework.security.core.userdetails.UsernameNotFoundException; public class TrustworthyBuilds { // Also effectively handles ReplayCause since that is only ever added in conjunction with UserIdCause. (see ReplayAction.run2) @Extension public static TrustworthyBuild byUserId() { - return TrustworthyBuild.byCause(Cause.UserIdCause.class); + return (build, listener) -> { + var cause = build.getCause(Cause.UserIdCause.class); + if (cause == null) { + // probably some other cause; do not print anything + return false; + } + var userId = cause.getUserId(); + if (userId == null) { + listener.getLogger().println("Not trusting build since no user name was recorded"); + return false; + } + var user = User.getById(userId, false); + if (user == null) { + listener.getLogger().printf("Not trusting build since no user ‘%s’ is known%n", userId); + return false; + } + try { + // TODO could also have workflow-cps offer this to anyone with only ReplayAction.REPLAY + if (build.hasPermission2(user.impersonate2(), Item.CONFIGURE)) { + listener.getLogger().printf("Trusting build since it was started by user ‘%s’%n", userId); + return true; + } else { + listener.getLogger().printf("Not trusting build since user ‘%s’ lacks Job/Configure permission%n", userId); + return false; + } + } catch (UsernameNotFoundException x) { + listener.getLogger().printf("Not trusting build since user ‘%s’ is invalid%n", userId); + return false; + } + }; } // TODO until github-checks can declare a dep on a sufficiently new scm-api @Extension public static TrustworthyBuild byGitHubChecks() { - return build -> build.getCauses().stream().anyMatch(cause -> cause.getClass().getName().equals("io.jenkins.plugins.checks.github.CheckRunGHEventSubscriber$GitHubChecksRerunActionCause")); + return (build, listener) -> build.getCauses().stream().anyMatch( + cause -> cause.getClass().getName().equals("io.jenkins.plugins.checks.github.CheckRunGHEventSubscriber$GitHubChecksRerunActionCause")); } private TrustworthyBuilds() {} From ebe3d6f5b8204bccd856c8c814ba625fb9bfdde2 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 22 Dec 2022 17:10:23 -0500 Subject: [PATCH 4/9] Check Run/Replay permission if defined --- src/main/java/jenkins/scm/impl/TrustworthyBuilds.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java index 1f2de667..9eec6564 100644 --- a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java +++ b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java @@ -27,6 +27,7 @@ import hudson.Extension; import hudson.model.Cause; import hudson.model.Item; +import hudson.model.Run; import hudson.model.User; import jenkins.scm.api.TrustworthyBuild; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -53,12 +54,15 @@ public static TrustworthyBuild byUserId() { return false; } try { - // TODO could also have workflow-cps offer this to anyone with only ReplayAction.REPLAY - if (build.hasPermission2(user.impersonate2(), Item.CONFIGURE)) { + var permission = Run.PERMISSIONS.find("Replay"); // ReplayAction.REPLAY + if (permission == null) { // no workflow-cps + permission = Item.CONFIGURE; + } + if (build.hasPermission2(user.impersonate2(), permission)) { listener.getLogger().printf("Trusting build since it was started by user ‘%s’%n", userId); return true; } else { - listener.getLogger().printf("Not trusting build since user ‘%s’ lacks Job/Configure permission%n", userId); + listener.getLogger().printf("Not trusting build since user ‘%s’ lacks %s/%s permission%n", userId, permission.group.title, permission.name); return false; } } catch (UsernameNotFoundException x) { From 57bfecc70cc4f5bc5d7dec60cd5742eaf6e0baf6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Apr 2023 11:59:30 -0400 Subject: [PATCH 5/9] Clarifying log message https://github.com/jenkinsci/scm-api-plugin/pull/180#discussion_r1156362777 --- src/main/java/jenkins/scm/api/SCMSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jenkins/scm/api/SCMSource.java b/src/main/java/jenkins/scm/api/SCMSource.java index b417a376..b1d35ec9 100644 --- a/src/main/java/jenkins/scm/api/SCMSource.java +++ b/src/main/java/jenkins/scm/api/SCMSource.java @@ -981,7 +981,7 @@ public final SCMRevision getTrustedRevisionForBuild(@NonNull SCMRevision revisio } else { SCMRevision trustedRevision = getTrustedRevision(revision, listener); if (trustedRevision.equals(revision)) { - LOGGER.fine(() -> build + " was not considered trustworthy, but " + revision + " was trusted anyway"); + LOGGER.fine(() -> revision + " was trusted anyway so it is irrelevant that " + build + " was not specifically considered trustworthy"); } else { LOGGER.fine(() -> build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); } From 013950cdb0dbfbc6d1be5e50052c5fc2775bec5e Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Apr 2023 12:59:25 -0400 Subject: [PATCH 6/9] Positively note trust due to `GitHubChecksRerunActionCause` in build log --- src/main/java/jenkins/scm/impl/TrustworthyBuilds.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java index 9eec6564..9661d806 100644 --- a/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java +++ b/src/main/java/jenkins/scm/impl/TrustworthyBuilds.java @@ -75,8 +75,15 @@ public static TrustworthyBuild byUserId() { // TODO until github-checks can declare a dep on a sufficiently new scm-api @Extension public static TrustworthyBuild byGitHubChecks() { - return (build, listener) -> build.getCauses().stream().anyMatch( - cause -> cause.getClass().getName().equals("io.jenkins.plugins.checks.github.CheckRunGHEventSubscriber$GitHubChecksRerunActionCause")); + return (build, listener) -> { + for (Cause cause : build.getCauses()) { + if (cause.getClass().getName().equals("io.jenkins.plugins.checks.github.CheckRunGHEventSubscriber$GitHubChecksRerunActionCause")) { + listener.getLogger().println("Trusting build since it was a rerun request through GitHub checks API"); + return true; + } + } + return false; + }; } private TrustworthyBuilds() {} From e5707b72bf783a4777b90a63e49a619882e74445 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Apr 2023 12:59:53 -0400 Subject: [PATCH 7/9] Removing `TrustworthyBuild.byCause`; impls may want to add logging anyway --- src/main/java/jenkins/scm/api/TrustworthyBuild.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/jenkins/scm/api/TrustworthyBuild.java b/src/main/java/jenkins/scm/api/TrustworthyBuild.java index 9bdf08bf..26533a59 100644 --- a/src/main/java/jenkins/scm/api/TrustworthyBuild.java +++ b/src/main/java/jenkins/scm/api/TrustworthyBuild.java @@ -26,7 +26,6 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionPoint; -import hudson.model.Cause; import hudson.model.Run; import hudson.model.TaskListener; import hudson.triggers.SCMTrigger; @@ -43,12 +42,7 @@ public interface TrustworthyBuild extends ExtensionPoint { /** * Should this build be trusted to load sensitive source files? * If any implementation returns true then it is trusted. - */ - boolean shouldBeTrusted(@NonNull Run build, @NonNull TaskListener listener); - - /** - * Convenience for the common case that a particular trigger cause indicates trust. - * Examples of causes which should not be registered include: + * Examples of build-triggering causes which should not be trusted include: *
    *
  • {@link TimerTrigger.TimerTriggerCause} *
  • {@link SCMTrigger.SCMTriggerCause} @@ -56,8 +50,6 @@ public interface TrustworthyBuild extends ExtensionPoint { *
  • {@code BranchEventCause} *
*/ - static TrustworthyBuild byCause(Class causeType) { - return (build, listener) -> build.getCause(causeType) != null; - } + boolean shouldBeTrusted(@NonNull Run build, @NonNull TaskListener listener); } From 7ea44f84ab05bbe17249c394eb84f1958bd675e4 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 10 Apr 2023 13:09:43 -0400 Subject: [PATCH 8/9] Suppress build log messages in the common case of a non-trustworthy build (e.g., SCM trigger) where this does not matter (e.g., branch push) https://github.com/jenkinsci/scm-api-plugin/pull/180#discussion_r1156362777 --- src/main/java/jenkins/scm/api/SCMSource.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/jenkins/scm/api/SCMSource.java b/src/main/java/jenkins/scm/api/SCMSource.java index 176bfab0..c855290c 100644 --- a/src/main/java/jenkins/scm/api/SCMSource.java +++ b/src/main/java/jenkins/scm/api/SCMSource.java @@ -40,7 +40,9 @@ import hudson.scm.SCM; import hudson.util.AlternativeUiTextProvider; import hudson.util.LogTaskListener; +import hudson.util.StreamTaskListener; import java.io.IOException; +import java.io.StringWriter; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; @@ -974,15 +976,22 @@ public SCMRevision getTrustedRevision(@NonNull SCMRevision revision, @NonNull Ta @NonNull public final SCMRevision getTrustedRevisionForBuild(@NonNull SCMRevision revision, @NonNull TaskListener listener, @NonNull Run build) throws IOException, InterruptedException { - if (ExtensionList.lookup(TrustworthyBuild.class).stream().anyMatch(tb -> tb.shouldBeTrusted(build, listener))) { + // Cheaper to check TrustworthyBuild than to call some getTrustedRevision impls, so try that first, + // but defer printing resulting messages if possible. + StringWriter buffer = new StringWriter(); + TaskListener bufferedListener = new StreamTaskListener(buffer); + if (ExtensionList.lookup(TrustworthyBuild.class).stream().anyMatch(tb -> tb.shouldBeTrusted(build, bufferedListener))) { LOGGER.fine(() -> build + " with " + build.getCauses() + " was considered trustworthy, so using " + revision + " as is"); + listener.getLogger().print(buffer.toString()); return revision; } else { SCMRevision trustedRevision = getTrustedRevision(revision, listener); - if (trustedRevision.equals(revision)) { - LOGGER.fine(() -> revision + " was trusted anyway so it is irrelevant that " + build + " was not specifically considered trustworthy"); + if (trustedRevision.equals(revision)) { // common case + LOGGER.fine(() -> revision + " was trusted anyway so it is irrelevant that " + build + " was not specifically considered trustworthy\n" + buffer); } else { LOGGER.fine(() -> build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); + listener.getLogger().print(buffer.toString()); + listener.getLogger().println(build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); } return trustedRevision; } From f6ec0d005bb5d6ee1288f54cbd105dad86af42d2 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 11 Apr 2023 11:31:59 -0400 Subject: [PATCH 9/9] More explicit guidance https://github.com/jenkinsci/workflow-multibranch-plugin/pull/220#pullrequestreview-1379491079 --- src/main/java/jenkins/scm/api/SCMSource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/jenkins/scm/api/SCMSource.java b/src/main/java/jenkins/scm/api/SCMSource.java index c855290c..fa826374 100644 --- a/src/main/java/jenkins/scm/api/SCMSource.java +++ b/src/main/java/jenkins/scm/api/SCMSource.java @@ -991,7 +991,8 @@ public final SCMRevision getTrustedRevisionForBuild(@NonNull SCMRevision revisio } else { LOGGER.fine(() -> build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); listener.getLogger().print(buffer.toString()); - listener.getLogger().println(build + " was not considered trustworthy, so replacing " + revision + " with " + trustedRevision); + listener.getLogger().printf("This build was not considered trustworthy, so replacing %s with %s for sensitive files.%n", revision, trustedRevision); + listener.getLogger().printf("(To retest using %s, trigger a new build explicitly, for example using Replay.)%n", revision); } return trustedRevision; }