From 6e29f5fafde540bec967a0bb23cbae1b3c5dadf0 Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Sat, 7 May 2022 11:32:17 +0200 Subject: [PATCH 01/12] Add script listener to track usage --- .../sandbox/groovy/SecureGroovyScript.java | 14 +++----- .../scripts/ScriptApproval.java | 26 +++++++++++++- .../scripts/ScriptListener.java | 35 +++++++++++++++++++ 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java index 9303365cd..0a1beff56 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java @@ -30,10 +30,7 @@ import groovy.lang.GroovyShell; import hudson.Extension; import hudson.PluginManager; -import hudson.model.AbstractDescribableImpl; -import hudson.model.Descriptor; -import hudson.model.Item; -import hudson.model.TaskListener; +import hudson.model.*; import hudson.util.FormValidation; import java.beans.Introspector; @@ -62,11 +59,7 @@ import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.SourceUnit; import org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException; -import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext; -import org.jenkinsci.plugins.scriptsecurity.scripts.ClasspathEntry; -import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval; -import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedClasspathException; -import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedUsageException; +import org.jenkinsci.plugins.scriptsecurity.scripts.*; import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; @@ -374,7 +367,8 @@ public Object evaluate(ClassLoader loader, Binding binding, @CheckForNull TaskLi memoryProtectedLoader = new CleanGroovyClassLoader(loader); loaderF.set(sh, memoryProtectedLoader); } - return sh.evaluate(ScriptApproval.get().using(script, GroovyLanguage.get())); + Run run = (Run) binding.getVariable("build"); + return sh.evaluate(ScriptApproval.get().using(script, GroovyLanguage.get(), run)); } } finally { diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index 1ff5ed692..5e7958e60 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.scriptsecurity.scripts; +import hudson.model.Run; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import net.sf.json.JSONArray; @@ -467,9 +468,32 @@ public synchronized String using(@NonNull String script, @NonNull Language langu // Probably need not add to pendingScripts, since generally that would have happened already in configuring. throw new UnapprovedUsageException(hash); } + + return script; + } + /** + * Called when a script is about to be used (evaluated). + * @param script a possibly unapproved script + * @param language the language in which it is written + * @param run the run executing the groovy script. + * @return {@code script}, for convenience + * @throws UnapprovedUsageException in case it has not yet been approved + */ + public synchronized String using(@NonNull String script, @NonNull Language language, @NonNull Run run) throws UnapprovedUsageException { + if (script.length() == 0) { + // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, + // and in many cases there is some sensible behavior for an emoty script which we want to permit. + ScriptListener.fireScriptFromConsoleEvent(script, run); + return script; + } + String hash = hash(script, language.getName()); + if (!approvedScriptHashes.contains(hash)) { + // Probably need not add to pendingScripts, since generally that would have happened already in configuring. + throw new UnapprovedUsageException(hash); + } + ScriptListener.fireScriptFromConsoleEvent(script, run); return script; } - // Only for testing synchronized boolean isScriptHashApproved(String hash) { return approvedScriptHashes.contains(hash); diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java new file mode 100644 index 000000000..c2696c2b7 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java @@ -0,0 +1,35 @@ +package org.jenkinsci.plugins.scriptsecurity.scripts; + +import hudson.ExtensionPoint; +import hudson.model.Run; +import jenkins.model.Jenkins; +import jenkins.util.Listeners; + +/** + * A listener to track usage of Groovy scripts running outside of a sandbox. + * + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(groovy.lang.GroovyClassLoader, groovy.lang.Binding, hudson.model.TaskListener) + */ +public interface ScriptListener extends ExtensionPoint { + + /** + * Called when a groovy script is executed in a pipeline outside of a sandbox. + * + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(groovy.lang.GroovyClassLoader, groovy.lang.Binding, hudson.model.TaskListener) + * @param script The Groovy script that is excecuted. + * @param run The run calling the Groovy script. + */ + void onScriptFromPipeline(String script, Run run); + + + /** + * Fires the {@link #onScriptFromPipeline(String, Run)} event to track the usage of the script console. + * + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, Binding, TaskListener) + * @param script The Groovy script that is excecuted. + * @param run The run calling the Groovy script. + */ + static void fireScriptFromConsoleEvent(String script, Run run) { + Listeners.notify(ScriptListener.class, true, listener -> listener.onScriptFromPipeline(script, run)); + } +} From e7bb854838e74bdf625f248164c1c79687067994 Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Sat, 7 May 2022 11:54:13 +0200 Subject: [PATCH 02/12] Fix imports --- .../sandbox/groovy/SecureGroovyScript.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java index 0a1beff56..431b7916a 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java @@ -30,7 +30,10 @@ import groovy.lang.GroovyShell; import hudson.Extension; import hudson.PluginManager; -import hudson.model.*; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Descriptor; +import hudson.model.Item; +import hudson.model.TaskListener; import hudson.util.FormValidation; import java.beans.Introspector; @@ -59,7 +62,11 @@ import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.SourceUnit; import org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException; -import org.jenkinsci.plugins.scriptsecurity.scripts.*; +import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext; +import org.jenkinsci.plugins.scriptsecurity.scripts.ClasspathEntry; +import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval; +import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedClasspathException; +import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedUsageException; import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; From d947485795a0167f55bc5df25540aa127f96430e Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Mon, 9 May 2022 11:50:01 +0200 Subject: [PATCH 03/12] Remove redundant code --- .../sandbox/groovy/SecureGroovyScript.java | 1 + .../plugins/scriptsecurity/scripts/ScriptApproval.java | 4 ++-- .../plugins/scriptsecurity/scripts/ScriptListener.java | 10 ++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java index 431b7916a..a57cbe4a7 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java @@ -33,6 +33,7 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.model.Item; +import hudson.model.Run; import hudson.model.TaskListener; import hudson.util.FormValidation; diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index 5e7958e60..9cd4d0daf 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -483,7 +483,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu if (script.length() == 0) { // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, // and in many cases there is some sensible behavior for an emoty script which we want to permit. - ScriptListener.fireScriptFromConsoleEvent(script, run); + ScriptListener.fireScriptFromPipelineEvent(script, run); return script; } String hash = hash(script, language.getName()); @@ -491,7 +491,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu // Probably need not add to pendingScripts, since generally that would have happened already in configuring. throw new UnapprovedUsageException(hash); } - ScriptListener.fireScriptFromConsoleEvent(script, run); + ScriptListener.fireScriptFromPipelineEvent(script, run); return script; } // Only for testing diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java index c2696c2b7..3ffc349bd 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java @@ -2,34 +2,32 @@ import hudson.ExtensionPoint; import hudson.model.Run; -import jenkins.model.Jenkins; import jenkins.util.Listeners; /** * A listener to track usage of Groovy scripts running outside of a sandbox. * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(groovy.lang.GroovyClassLoader, groovy.lang.Binding, hudson.model.TaskListener) + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) */ public interface ScriptListener extends ExtensionPoint { /** * Called when a groovy script is executed in a pipeline outside of a sandbox. * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(groovy.lang.GroovyClassLoader, groovy.lang.Binding, hudson.model.TaskListener) + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) * @param script The Groovy script that is excecuted. * @param run The run calling the Groovy script. */ void onScriptFromPipeline(String script, Run run); - /** * Fires the {@link #onScriptFromPipeline(String, Run)} event to track the usage of the script console. * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, Binding, TaskListener) + * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) * @param script The Groovy script that is excecuted. * @param run The run calling the Groovy script. */ - static void fireScriptFromConsoleEvent(String script, Run run) { + static void fireScriptFromPipelineEvent(String script, Run run) { Listeners.notify(ScriptListener.class, true, listener -> listener.onScriptFromPipeline(script, run)); } } From b9eafc1e6a3bfeaec4404030890d3fe2d56b2859 Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Mon, 9 May 2022 11:51:04 +0200 Subject: [PATCH 04/12] Fix docstring --- .../plugins/scriptsecurity/scripts/ScriptListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java index 3ffc349bd..d4702e317 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java @@ -21,7 +21,7 @@ public interface ScriptListener extends ExtensionPoint { void onScriptFromPipeline(String script, Run run); /** - * Fires the {@link #onScriptFromPipeline(String, Run)} event to track the usage of the script console. + * Fires the {@link #onScriptFromPipeline(String, Run)} event to track the usage of groovy scripts running outside the sandbox. * * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) * @param script The Groovy script that is excecuted. From e69d7cb0eb9d7ccdc2246d714dc69d6593b2c28e Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Tue, 10 May 2022 11:39:37 +0200 Subject: [PATCH 05/12] Remove NonNull from Run for compatibility --- .../plugins/scriptsecurity/scripts/ScriptApproval.java | 2 +- .../plugins/scriptsecurity/scripts/ScriptListener.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index 9cd4d0daf..bd3e6d4bd 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -479,7 +479,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu * @return {@code script}, for convenience * @throws UnapprovedUsageException in case it has not yet been approved */ - public synchronized String using(@NonNull String script, @NonNull Language language, @NonNull Run run) throws UnapprovedUsageException { + public synchronized String using(@NonNull String script, @NonNull Language language, Run run) throws UnapprovedUsageException { if (script.length() == 0) { // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, // and in many cases there is some sensible behavior for an emoty script which we want to permit. diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java index d4702e317..ac56d4247 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java @@ -16,7 +16,7 @@ public interface ScriptListener extends ExtensionPoint { * * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) * @param script The Groovy script that is excecuted. - * @param run The run calling the Groovy script. + * @param run The run calling the Groovy script. May be null. */ void onScriptFromPipeline(String script, Run run); From df3ee0eeeae067d3d421184860bbfe4616ac52b0 Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Tue, 10 May 2022 12:07:20 +0200 Subject: [PATCH 06/12] Improve compatibility by generalizing origin to String --- .../sandbox/groovy/SecureGroovyScript.java | 11 ++++++-- .../scripts/ScriptApproval.java | 25 ++++++------------- .../scripts/ScriptListener.java | 14 +++++------ .../groovy/SecureGroovyScriptTest.java | 4 +-- .../scripts/ScriptApprovalTest.java | 2 +- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java index a57cbe4a7..3ea8f43cf 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java @@ -375,8 +375,15 @@ public Object evaluate(ClassLoader loader, Binding binding, @CheckForNull TaskLi memoryProtectedLoader = new CleanGroovyClassLoader(loader); loaderF.set(sh, memoryProtectedLoader); } - Run run = (Run) binding.getVariable("build"); - return sh.evaluate(ScriptApproval.get().using(script, GroovyLanguage.get(), run)); + String origin = "UNKNOWN"; + if (binding.hasVariable("build")) { + Run run = (Run) binding.getVariable("build"); + origin = run.getFullDisplayName(); + } else { + LOGGER.log(Level.INFO, "Could not determine origin of the groovy script - missing implementation. Please open an issue for this!"); + } + + return sh.evaluate(ScriptApproval.get().using(script, GroovyLanguage.get(), origin)); } } finally { diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index bd3e6d4bd..eec4c33cc 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -455,35 +455,26 @@ public synchronized String configuring(@NonNull String script, @NonNull Language * @param script a possibly unapproved script * @param language the language in which it is written * @return {@code script}, for convenience - * @throws UnapprovedUsageException in case it has not yet been approved + * @throws UnapprovedUsageException in case it has not yet been approve + * @deprecated Use {@link #using(String, Language, String)}. */ + @Deprecated public synchronized String using(@NonNull String script, @NonNull Language language) throws UnapprovedUsageException { - if (script.length() == 0) { - // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, - // and in many cases there is some sensible behavior for an emoty script which we want to permit. - return script; - } - String hash = hash(script, language.getName()); - if (!approvedScriptHashes.contains(hash)) { - // Probably need not add to pendingScripts, since generally that would have happened already in configuring. - throw new UnapprovedUsageException(hash); - } - - return script; + return using(script, language, "N/A"); } /** * Called when a script is about to be used (evaluated). * @param script a possibly unapproved script * @param language the language in which it is written - * @param run the run executing the groovy script. + * @param origin A descriptive, trackable identifier of the entity running the script. * @return {@code script}, for convenience * @throws UnapprovedUsageException in case it has not yet been approved */ - public synchronized String using(@NonNull String script, @NonNull Language language, Run run) throws UnapprovedUsageException { + public synchronized String using(@NonNull String script, @NonNull Language language, String origin) throws UnapprovedUsageException { if (script.length() == 0) { // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, // and in many cases there is some sensible behavior for an emoty script which we want to permit. - ScriptListener.fireScriptFromPipelineEvent(script, run); + ScriptListener.fireScriptEvent(script, origin); return script; } String hash = hash(script, language.getName()); @@ -491,7 +482,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu // Probably need not add to pendingScripts, since generally that would have happened already in configuring. throw new UnapprovedUsageException(hash); } - ScriptListener.fireScriptFromPipelineEvent(script, run); + ScriptListener.fireScriptEvent(script, origin); return script; } // Only for testing diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java index ac56d4247..9f1934861 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java @@ -15,19 +15,19 @@ public interface ScriptListener extends ExtensionPoint { * Called when a groovy script is executed in a pipeline outside of a sandbox. * * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) - * @param script The Groovy script that is excecuted. - * @param run The run calling the Groovy script. May be null. + * @param script The Groovy script that is executed. + * @param origin A descriptive, trackable identifier of the entity running the script. */ - void onScriptFromPipeline(String script, Run run); + void onScript(String script, String origin); /** - * Fires the {@link #onScriptFromPipeline(String, Run)} event to track the usage of groovy scripts running outside the sandbox. + * Fires the {@link #onScript(String, String)} event to track the usage of groovy scripts running outside the sandbox. * * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) * @param script The Groovy script that is excecuted. - * @param run The run calling the Groovy script. + * @param origin A descriptive, trackable identifier of the entity running the script. */ - static void fireScriptFromPipelineEvent(String script, Run run) { - Listeners.notify(ScriptListener.class, true, listener -> listener.onScriptFromPipeline(script, run)); + static void fireScriptEvent(String script, String origin) { + Listeners.notify(ScriptListener.class, true, listener -> listener.onScript(script, origin)); } } diff --git a/src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScriptTest.java b/src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScriptTest.java index 9217d61e3..eba1d179e 100644 --- a/src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScriptTest.java +++ b/src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScriptTest.java @@ -198,7 +198,7 @@ public class SecureGroovyScriptTest { assertEquals(0, pendingScripts.size()); // Test that the script is executable. If it's not, we will get an UnapprovedUsageException - assertEquals(groovy, ScriptApproval.get().using(groovy, GroovyLanguage.get())); + assertEquals(groovy, ScriptApproval.get().using(groovy, GroovyLanguage.get(), "Testing")); } /** @@ -243,7 +243,7 @@ public class SecureGroovyScriptTest { // We didn't add the approved classpath so ... final UnapprovedUsageException e = assertThrows(UnapprovedUsageException.class, - () -> ScriptApproval.get().using(groovy, GroovyLanguage.get())); + () -> ScriptApproval.get().using(groovy, GroovyLanguage.get(), "Testing")); assertEquals("script not yet approved for use", e.getMessage()); } diff --git a/src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApprovalTest.java b/src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApprovalTest.java index 1473e63f6..738b7f64e 100644 --- a/src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApprovalTest.java +++ b/src/test/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApprovalTest.java @@ -222,7 +222,7 @@ boolean findApproved() { @Override Script use() { - assertEquals(groovy, ScriptApproval.get().using(groovy, GroovyLanguage.get())); + assertEquals(groovy, ScriptApproval.get().using(groovy, GroovyLanguage.get(), "Testing")); return this; } From 7b433d5bac060071bbda836376c655594fe6d444 Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Tue, 17 May 2022 16:22:57 +0200 Subject: [PATCH 07/12] Simplify listener by extending from Jenkins --- .../scripts/ScriptApproval.java | 3 ++ .../scripts/ScriptListener.java | 33 ------------------- 2 files changed, 3 insertions(+), 33 deletions(-) delete mode 100644 src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index eec4c33cc..f829cf388 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -27,8 +27,10 @@ import hudson.model.Run; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; +import jenkins.model.ScriptListener; import net.sf.json.JSONArray; import net.sf.json.JSONObject; +import org.apache.commons.jelly.Script; import org.jenkinsci.Symbol; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.AclAwareWhitelist; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.ProxyWhitelist; @@ -475,6 +477,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, // and in many cases there is some sensible behavior for an emoty script which we want to permit. ScriptListener.fireScriptEvent(script, origin); + return script; } String hash = hash(script, language.getName()); diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java deleted file mode 100644 index 9f1934861..000000000 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.jenkinsci.plugins.scriptsecurity.scripts; - -import hudson.ExtensionPoint; -import hudson.model.Run; -import jenkins.util.Listeners; - -/** - * A listener to track usage of Groovy scripts running outside of a sandbox. - * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) - */ -public interface ScriptListener extends ExtensionPoint { - - /** - * Called when a groovy script is executed in a pipeline outside of a sandbox. - * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) - * @param script The Groovy script that is executed. - * @param origin A descriptive, trackable identifier of the entity running the script. - */ - void onScript(String script, String origin); - - /** - * Fires the {@link #onScript(String, String)} event to track the usage of groovy scripts running outside the sandbox. - * - * @see org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript#evaluate(ClassLoader, groovy.lang.Binding, hudson.model.TaskListener) - * @param script The Groovy script that is excecuted. - * @param origin A descriptive, trackable identifier of the entity running the script. - */ - static void fireScriptEvent(String script, String origin) { - Listeners.notify(ScriptListener.class, true, listener -> listener.onScript(script, origin)); - } -} From cf6ae25c6ece53e93e2f11885170595ba260a7db Mon Sep 17 00:00:00 2001 From: meiswjn <41284403+meiswjn@users.noreply.github.com> Date: Tue, 17 May 2022 16:25:33 +0200 Subject: [PATCH 08/12] Remove unnecessary imports --- .../plugins/scriptsecurity/scripts/ScriptApproval.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index f829cf388..31ff1fc1b 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -24,13 +24,11 @@ package org.jenkinsci.plugins.scriptsecurity.scripts; -import hudson.model.Run; import jenkins.model.GlobalConfiguration; import jenkins.model.GlobalConfigurationCategory; import jenkins.model.ScriptListener; import net.sf.json.JSONArray; import net.sf.json.JSONObject; -import org.apache.commons.jelly.Script; import org.jenkinsci.Symbol; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.AclAwareWhitelist; import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.ProxyWhitelist; @@ -57,7 +55,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; From abfbc35aec9569d85706ff4e9b7a98e99c275422 Mon Sep 17 00:00:00 2001 From: "Meiswinkel, Jan SF/HZA-ZC2S" Date: Fri, 24 Jun 2022 10:11:16 +0200 Subject: [PATCH 09/12] Adapt to new scriptlistener format --- pom.xml | 2 +- .../scriptsecurity/sandbox/groovy/SecureGroovyScript.java | 3 ++- .../plugins/scriptsecurity/scripts/ScriptApproval.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 30b43be52..002dbc072 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ https://github.com/jenkinsci/${project.artifactId}-plugin 999999-SNAPSHOT - 2.332.1 + 2.355-SNAPSHOT jenkinsci/${project.artifactId}-plugin diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java index fda900fe2..9bd832d51 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SecureGroovyScript.java @@ -397,7 +397,8 @@ public Object evaluate(ClassLoader loader, Binding binding, @CheckForNull TaskLi String origin = "UNKNOWN"; if (binding.hasVariable("build")) { Run run = (Run) binding.getVariable("build"); - origin = run.getFullDisplayName(); + + origin = String.format("build '%s'", run.getExternalizableId()); } else { LOGGER.log(Level.INFO, "Could not determine origin of the groovy script - missing implementation. Please open an issue for this!"); } diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java index 11deef22a..8a0f2f1c5 100644 --- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java +++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/scripts/ScriptApproval.java @@ -495,7 +495,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu if (script.length() == 0) { // As a special case, always consider the empty script preapproved, as this is usually the default for new fields, // and in many cases there is some sensible behavior for an emoty script which we want to permit. - ScriptListener.fireScriptEvent(script, origin); + ScriptListener.fireScriptEvent(script, origin, null); return script; } @@ -504,7 +504,7 @@ public synchronized String using(@NonNull String script, @NonNull Language langu // Probably need not add to pendingScripts, since generally that would have happened already in configuring. throw new UnapprovedUsageException(hash); } - ScriptListener.fireScriptEvent(script, origin); + ScriptListener.fireScriptEvent(script, origin, null); return script; } // Only for testing From aa41a17c40a74b7602c53e600629ef528d3caf5e Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Wed, 31 Aug 2022 21:37:22 +0200 Subject: [PATCH 10/12] Use incremental --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 002dbc072..e98fdcd14 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,8 @@ https://github.com/jenkinsci/${project.artifactId}-plugin 999999-SNAPSHOT - 2.355-SNAPSHOT + + 2.366-rc32795.df5b_49c75b_0e jenkinsci/${project.artifactId}-plugin From 45829267d9a209267ed4a803239608a386024958 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Wed, 31 Aug 2022 21:46:59 +0200 Subject: [PATCH 11/12] Make build --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index e98fdcd14..783f0ea2d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.jenkins-ci.plugins plugin - 4.40 + 4.47 @@ -51,8 +51,8 @@ io.jenkins.tools.bom - bom-2.332.x - 1289.v5c4b_1c43511b_ + bom-2.361.x + 1607.va_c1576527071 import pom From 81eef7b193a650cc7c5cca0f0c6f288cdb47bc99 Mon Sep 17 00:00:00 2001 From: Daniel Beck <1831569+daniel-beck@users.noreply.github.com> Date: Wed, 31 Aug 2022 22:42:01 +0200 Subject: [PATCH 12/12] We need to build on JDK 11 --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 688a641ff..3765128a6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ buildPlugin(useAci: true, configurations: [ - [ platform: "windows", jdk: "8" ], + [ platform: "windows", jdk: "11" ], [ platform: "linux", jdk: "11" ] ])