diff --git a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/GroovySandbox.java b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/GroovySandbox.java
index 44334b922..39a836c52 100644
--- a/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/GroovySandbox.java
+++ b/src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/GroovySandbox.java
@@ -72,7 +72,7 @@ public class GroovySandbox {
public static @Nonnull ClassLoader createSecureClassLoader(ClassLoader base) {
return new SandboxResolvingClassLoader(base);
}
-
+
/**
* Runs a block in the sandbox.
* You must have used {@link #createSecureCompilerConfiguration} to prepare the Groovy shell.
@@ -132,10 +132,7 @@ public void run() {
* @throws RejectedAccessException in case an attempted call was not whitelisted
*/
public static Object run(@Nonnull Script script, @Nonnull final Whitelist whitelist) throws RejectedAccessException {
- Whitelist wrapperWhitelist = new ProxyWhitelist(
- new ClassLoaderWhitelist(script.getClass().getClassLoader()),
- whitelist);
- GroovyInterceptor sandbox = new SandboxInterceptor(wrapperWhitelist);
+ GroovyInterceptor sandbox = createSandbox(script, whitelist);
sandbox.register();
try {
return script.run();
@@ -144,6 +141,20 @@ public static Object run(@Nonnull Script script, @Nonnull final Whitelist whitel
}
}
+ /**
+ * Prepares a sandbox for a script.
+ * You must have used {@link #createSecureCompilerConfiguration} to prepare the Groovy shell.
+ * @param script a script ready to {@link Script#run}, created for example by {@link GroovyShell#parse(String)}
+ * @param whitelist the whitelist to use, such as {@link Whitelist#all()}
+ * @return the sandbox for running this script
+ */
+ public static GroovyInterceptor createSandbox(@Nonnull Script script, @Nonnull final Whitelist whitelist) {
+ Whitelist wrapperWhitelist = new ProxyWhitelist(
+ new ClassLoaderWhitelist(script.getClass().getClassLoader()),
+ whitelist);
+ return new SandboxInterceptor(wrapperWhitelist);
+ }
+
private GroovySandbox() {}
}
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 db87d307b..610be843c 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
@@ -28,6 +28,7 @@
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
+import groovy.lang.Script;
import hudson.Extension;
import hudson.PluginManager;
import hudson.model.AbstractDescribableImpl;
@@ -36,6 +37,7 @@
import hudson.util.FormValidation;
import java.beans.Introspector;
+import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
@@ -68,6 +70,7 @@
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.groovy.sandbox.GroovyInterceptor;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
@@ -80,7 +83,7 @@
* Use {@code } to configure it from Jelly.
*/
public final class SecureGroovyScript extends AbstractDescribableImpl {
-
+
private final @Nonnull String script;
private final boolean sandbox;
private final @CheckForNull List classpath;
@@ -277,6 +280,10 @@ private static void cleanUpObjectStreamClassCaches(@Nonnull Class> clazz) thro
}
}
+ public PreparedScript prepare(ClassLoader loader, Binding binding) throws IllegalAccessException, IOException {
+ return new PreparedScript(loader, binding);
+ }
+
/**
* Runs the Groovy script, using the sandbox if so configured.
* @param loader a class loader for constructing the shell, such as {@link PluginManager#uberClassLoader} (will be augmented by {@link #getClasspath} if nonempty)
@@ -287,63 +294,99 @@ private static void cleanUpObjectStreamClassCaches(@Nonnull Class> clazz) thro
* @throws UnapprovedUsageException in case of a non-sandbox issue
* @throws UnapprovedClasspathException in case some unapproved classpath entries were requested
*/
- @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Managed by GroovyShell.")
public Object evaluate(ClassLoader loader, Binding binding) throws Exception {
- if (!calledConfiguring) {
- throw new IllegalStateException("you need to call configuring or a related method before using GroovyScript");
- }
- URLClassLoader urlcl = null;
- ClassLoader memoryProtectedLoader = null;
- List cp = getClasspath();
- if (!cp.isEmpty()) {
- List urlList = new ArrayList(cp.size());
-
- for (ClasspathEntry entry : cp) {
- ScriptApproval.get().using(entry);
- urlList.add(entry.getURL());
- }
-
- loader = urlcl = new URLClassLoader(urlList.toArray(new URL[urlList.size()]), loader);
- }
- boolean canDoCleanup = false;
+ PreparedScript scriptInstance = prepare(loader, binding);
try {
- loader = GroovySandbox.createSecureClassLoader(loader);
+ return scriptInstance.run();
+ } finally {
+ scriptInstance.cleanUp();
+ }
+ }
- Field loaderF = null;
- try {
- loaderF = GroovyShell.class.getDeclaredField("loader");
- loaderF.setAccessible(true);
- canDoCleanup = true;
- } catch (NoSuchFieldException nsme) {
- LOGGER.log(Level.FINE, "GroovyShell fields have changed, field loader no longer exists -- memory leak fixes won't work");
+ /**
+ * Allows access to execue a script multiple times without preparation and clean-up overhead.
+ * While the intricate logic to do this continues to be hidden from users.
+ */
+ public final class PreparedScript {
+
+ private final GroovyShell sh;
+ private final Script preparedScript;
+ private ClassLoader memoryProtectedLoader = null;
+ private GroovyInterceptor scriptSandbox = null;
+ private URLClassLoader urlcl = null;
+ private boolean canDoCleanup = false;
+
+ /**
+ * @see @see SecureGroovyScript#evaluate()
+ */
+ @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Managed by GroovyShell.")
+ private PreparedScript(ClassLoader loader, Binding binding) throws IllegalAccessException, IOException {
+ List cp = getClasspath();
+ if (!cp.isEmpty()) {
+ List urlList = new ArrayList(cp.size());
+
+ for (ClasspathEntry entry : cp) {
+ ScriptApproval.get().using(entry);
+ urlList.add(entry.getURL());
+ }
+
+ loader = urlcl = new URLClassLoader(urlList.toArray(new URL[urlList.size()]), loader);
}
- GroovyShell sh;
- if (sandbox) {
- CompilerConfiguration cc = GroovySandbox.createSecureCompilerConfiguration();
- sh = new GroovyShell(loader, binding, cc);
+ try {
+ loader = GroovySandbox.createSecureClassLoader(loader);
+ Field loaderF = null;
+ try {
+ loaderF = GroovyShell.class.getDeclaredField("loader");
+ loaderF.setAccessible(true);
+ canDoCleanup = true;
+ } catch (NoSuchFieldException nsme) {
+ LOGGER.log(Level.FINE, "GroovyShell fields have changed, field loader no longer exists -- memory leak fixes won't work");
+ }
- if (canDoCleanup) {
- memoryProtectedLoader = new CleanGroovyClassLoader(loader, cc);
- loaderF.set(sh, memoryProtectedLoader);
+ if (sandbox) {
+ CompilerConfiguration cc = GroovySandbox.createSecureCompilerConfiguration();
+ sh = new GroovyShell(loader, binding, cc);
+
+ if (canDoCleanup) {
+ memoryProtectedLoader = new CleanGroovyClassLoader(loader, cc);
+ loaderF.set(sh, memoryProtectedLoader);
+ }
+
+ preparedScript = sh.parse(script);
+ scriptSandbox = GroovySandbox.createSandbox(preparedScript, Whitelist.all());
+ } else {
+ sh = new GroovyShell(loader, binding);
+ if (canDoCleanup) {
+ memoryProtectedLoader = new CleanGroovyClassLoader(loader);
+ loaderF.set(sh, memoryProtectedLoader);
+ }
+
+ preparedScript = sh.parse(ScriptApproval.get().using(script, GroovyLanguage.get()));
}
+ } catch (Exception e) {
+ cleanUp();
+ throw e;
+ }
+ }
+ public Object run() throws Exception {
+ if (sandbox) {
+ scriptSandbox.register();
try {
- return GroovySandbox.run(sh.parse(script), Whitelist.all());
+ return preparedScript.run();
} catch (RejectedAccessException x) {
throw ScriptApproval.get().accessRejected(x, ApprovalContext.create());
+ } finally {
+ scriptSandbox.unregister();
}
} else {
- sh = new GroovyShell(loader, binding);
- if (canDoCleanup) {
- memoryProtectedLoader = new CleanGroovyClassLoader(loader);
- loaderF.set(sh, memoryProtectedLoader);
- }
- return sh.evaluate(ScriptApproval.get().using(script, GroovyLanguage.get()));
+ return preparedScript.run();
}
+ }
- } finally {
+ public void cleanUp() throws IOException {
try {
if (canDoCleanup) {
cleanUpLoader(memoryProtectedLoader, new HashSet(), new HashSet>());
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 9b1e1a515..363f896cb 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
@@ -77,7 +77,7 @@ public class SecureGroovyScriptTest {
*/
@Test public void basicApproval() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -142,7 +142,7 @@ public class SecureGroovyScriptTest {
*/
@Test public void testSandboxDefault_with_RUN_SCRIPTS_privs() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
mockStrategy.grant(Jenkins.RUN_SCRIPTS).everywhere().to("devel");
@@ -151,7 +151,7 @@ public class SecureGroovyScriptTest {
mockStrategy.grant(p).everywhere().to("devel");
}
r.jenkins.setAuthorizationStrategy(mockStrategy);
-
+
FreeStyleProject p = r.createFreeStyleProject("p");
JenkinsRule.WebClient wc = r.createWebClient();
wc.login("devel");
@@ -185,7 +185,7 @@ public class SecureGroovyScriptTest {
*/
@Test public void testSandboxDefault_without_RUN_SCRIPTS_privs() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -230,18 +230,18 @@ public class SecureGroovyScriptTest {
private List getAllJarFiles() throws URISyntaxException {
String testClassPath = String.format(StringUtils.join(getClass().getName().split("\\."), "/"));
File testClassDir = new File(ClassLoader.getSystemResource(testClassPath).toURI()).getAbsoluteFile();
-
+
DirectoryScanner ds = new DirectoryScanner();
ds.setBasedir(testClassDir);
ds.setIncludes(new String[]{ "*.jar" });
ds.scan();
-
+
List ret = new ArrayList();
-
+
for (String relpath: ds.getIncludedFiles()) {
ret.add(new File(testClassDir, relpath));
}
-
+
return ret;
}
@@ -267,20 +267,20 @@ private List files2entries(Iterable files) throws IOExcept
private List getAllUpdatedJarFiles() throws URISyntaxException {
String testClassPath = String.format(StringUtils.join(getClass().getName().split("\\."), "/"));
File testClassDir = new File(ClassLoader.getSystemResource(testClassPath).toURI()).getAbsoluteFile();
-
+
File updatedDir = new File(testClassDir, "updated");
-
+
DirectoryScanner ds = new DirectoryScanner();
ds.setBasedir(updatedDir);
ds.setIncludes(new String[]{ "*.jar" });
ds.scan();
-
+
List ret = new ArrayList();
-
+
for (String relpath: ds.getIncludedFiles()) {
ret.add(new File(updatedDir, relpath));
}
-
+
return ret;
}
@@ -303,7 +303,7 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
@Test public void testClasspathInSandbox() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -315,21 +315,21 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
for (File jarfile: getAllJarFiles()) {
classpath.add(new ClasspathEntry(jarfile.getAbsolutePath()));
}
-
+
// Approve classpath.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript("", true, classpath)));
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertNotEquals(0, pcps.size());
for(ScriptApproval.PendingClasspathEntry pcp: pcps) {
ScriptApproval.get().approveClasspathEntry(pcp.getHash());
}
}
-
+
final String testingDisplayName = "TESTDISPLAYNAME";
-
+
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
@@ -337,13 +337,13 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
classpath
)));
-
+
FreeStyleBuild b = p.scheduleBuild2(0).get();
// fails for accessing non-whitelisted method.
r.assertBuildStatus(Result.FAILURE, b);
assertNotEquals(testingDisplayName, b.getDisplayName());
}
-
+
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
@@ -354,13 +354,13 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
classpath
)));
-
+
FreeStyleBuild b = p.scheduleBuild2(0).get();
// fails for accessing non-whitelisted method.
r.assertBuildStatus(Result.FAILURE, b);
assertNotEquals(testingDisplayName, b.getDisplayName());
}
-
+
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
@@ -371,16 +371,16 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
classpath
)));
-
+
FreeStyleBuild b = p.scheduleBuild2(0).get();
r.assertBuildStatusSuccess(b);
assertEquals(testingDisplayName, b.getDisplayName());
}
}
-
+
@Test public void testNonapprovedClasspathInSandbox() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -392,81 +392,81 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
for (File jarfile: getAllJarFiles()) {
String path = jarfile.getAbsolutePath();
classpath.add(new ClasspathEntry(path));
-
+
// String hash = ScriptApproval.hashClasspath(path);
// ScriptApproval.get().addApprovedClasspathEntry(new ScriptApproval.ApprovedClasspathEntry(hash, path));
}
-
+
String SCRIPT_TO_RUN = "\"Script is run\";";
-
+
// approve script
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false)));
-
+
Set pss = ScriptApproval.get().getPendingScripts();
assertNotEquals(0, pss.size());
for(ScriptApproval.PendingScript ps: pss) {
ScriptApproval.get().approveScript(ps.getHash());
}
}
-
+
// Success without classpaths
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false)));
-
+
r.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
}
-
+
// Fail as the classpath is not approved.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false, classpath)));
-
+
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
}
-
+
// Fail even in sandbox.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, true, classpath)));
-
+
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
}
-
+
// Approve classpath.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript("", true, classpath)));
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertNotEquals(0, pcps.size());
for(ScriptApproval.PendingClasspathEntry pcp: pcps) {
ScriptApproval.get().approveClasspathEntry(pcp.getHash());
}
}
-
+
// Success without sandbox.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false, classpath)));
-
+
r.assertBuildStatusSuccess(p.scheduleBuild2(0));
}
-
+
// Success also in sandbox.
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, true, classpath)));
-
+
r.assertBuildStatusSuccess(p.scheduleBuild2(0));
}
}
-
+
@Test public void testUpdatedClasspath() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -476,44 +476,44 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
// Copy jar files to temporary directory, then overwrite them with updated jar files.
File tmpDir = tmpFolderRule.newFolder();
-
+
for (File jarfile: getAllJarFiles()) {
FileUtils.copyFileToDirectory(jarfile, tmpDir);
}
-
+
List classpath = new ArrayList();
for (File jarfile: tmpDir.listFiles()) {
classpath.add(new ClasspathEntry(jarfile.getAbsolutePath()));
}
-
+
String SCRIPT_TO_RUN = "\"Script is run\";";
-
+
// approve script
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false)));
-
+
Set pss = ScriptApproval.get().getPendingScripts();
assertNotEquals(0, pss.size());
for(ScriptApproval.PendingScript ps: pss) {
ScriptApproval.get().approveScript(ps.getHash());
}
}
-
+
// Success without classpaths
{
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false)));
-
+
r.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
}
-
+
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(SCRIPT_TO_RUN, false, classpath)));
-
+
// Fail as the classpath is not approved.
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
-
+
// Approve classpath.
{
List pcps = ScriptApproval.get().getPendingClasspathEntries();
@@ -522,18 +522,18 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
ScriptApproval.get().approveClasspathEntry(pcp.getHash());
}
}
-
+
// Success as approved.
r.assertBuildStatusSuccess(p.scheduleBuild2(0));
-
+
// overwrite jar files.
for (File jarfile: getAllUpdatedJarFiles()) {
FileUtils.copyFileToDirectory(jarfile, tmpDir);
}
-
+
// Fail as the updated jar files are not approved.
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
-
+
// Approve classpath.
{
List pcps = ScriptApproval.get().getPendingClasspathEntries();
@@ -542,14 +542,14 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
ScriptApproval.get().approveClasspathEntry(pcp.getHash());
}
}
-
+
// Success as approved.
r.assertBuildStatusSuccess(p.scheduleBuild2(0));
}
-
+
@Test public void testClasspathWithClassDirectory() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -559,19 +559,19 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
// Copy jar files to temporary directory, then overwrite them with updated jar files.
File tmpDir = tmpFolderRule.newFolder();
-
+
for (File jarfile: getAllJarFiles()) {
Expand e = new Expand();
e.setSrc(jarfile);
e.setDest(tmpDir);
e.execute();
}
-
+
List classpath = new ArrayList();
classpath.add(new ClasspathEntry(tmpDir.getAbsolutePath()));
-
+
final String testingDisplayName = "TESTDISPLAYNAME";
-
+
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
String.format(
@@ -581,7 +581,7 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
classpath
)));
-
+
// Fail as the classpath is not approved.
{
FreeStyleBuild b = p.scheduleBuild2(0).get();
@@ -590,17 +590,17 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
r.assertLogContains("is a class directory, which are not allowed", b);
assertNotEquals(testingDisplayName, b.getDisplayName());
}
-
+
// Unable to approve classpath.
{
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertEquals(0, pcps.size());
}
}
-
+
@Test public void testDifferentClasspathButSameContent() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
for (Permission p : Item.PERMISSIONS.getPermissions()) {
@@ -609,9 +609,9 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
r.jenkins.setAuthorizationStrategy(mockStrategy);
final String testingDisplayName = "TESTDISPLAYNAME";
-
+
final List jars = getAllJarFiles();
-
+
FreeStyleProject p1 = r.createFreeStyleProject();
p1.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
String.format(
@@ -621,14 +621,14 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
files2entries(jars)
)));
-
+
// Fail as the classpath is not approved.
{
FreeStyleBuild b = p1.scheduleBuild2(0).get();
r.assertBuildStatus(Result.FAILURE, b);
assertNotEquals(testingDisplayName, b.getDisplayName());
}
-
+
// Approve classpath.
{
List pcps = ScriptApproval.get().getPendingClasspathEntries();
@@ -637,14 +637,14 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
ScriptApproval.get().approveClasspathEntry(pcp.getHash());
}
}
-
+
// Success as approved.
{
FreeStyleBuild b = p1.scheduleBuild2(0).get();
r.assertBuildStatusSuccess(b);
assertEquals(testingDisplayName, b.getDisplayName());
}
-
+
// New job with jars in other places.
FreeStyleProject p2 = r.createFreeStyleProject();
p2.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
@@ -655,7 +655,7 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
files2entries(copy2TempDir(jars))
)));
-
+
// Success as approved.
{
FreeStyleBuild b = p2.scheduleBuild2(0).get();
@@ -663,10 +663,10 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
assertEquals(testingDisplayName, b.getDisplayName());
}
}
-
+
@Test public void testClasspathAutomaticApprove() throws Exception {
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
-
+
MockAuthorizationStrategy mockStrategy = new MockAuthorizationStrategy();
mockStrategy.grant(Jenkins.READ).everywhere().to("devel");
mockStrategy.grant(Jenkins.READ).everywhere().to("approver");
@@ -677,23 +677,23 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
mockStrategy.grant(p).everywhere().to("approver");
}
r.jenkins.setAuthorizationStrategy(mockStrategy);
-
+
JenkinsRule.WebClient wcDevel = r.createWebClient();
wcDevel.login("devel");
-
+
JenkinsRule.WebClient wcApprover = r.createWebClient();
wcApprover.login("approver");
-
-
+
+
List classpath = new ArrayList();
-
+
for (File jarfile: getAllJarFiles()) {
classpath.add(new ClasspathEntry(jarfile.getAbsolutePath()));
System.out.println(jarfile);
}
-
+
final String testingDisplayName = "TESTDISPLAYNAME";
-
+
FreeStyleProject p = r.createFreeStyleProject();
p.getPublishersList().add(new TestGroovyRecorder(new SecureGroovyScript(
String.format(
@@ -703,7 +703,7 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
true,
classpath
)));
-
+
// Deny classpath.
{
List pcps = ScriptApproval.get().getPendingClasspathEntries();
@@ -711,73 +711,73 @@ private List getAllUpdatedJarFiles() throws URISyntaxException {
for(ScriptApproval.PendingClasspathEntry pcp: pcps) {
ScriptApproval.get().denyClasspathEntry(pcp.getHash());
}
-
+
assertEquals(0, ScriptApproval.get().getPendingClasspathEntries().size());
assertEquals(0, ScriptApproval.get().getApprovedClasspathEntries().size());
}
-
+
// If configured by a user with RUN_SCRIPTS, the classpath is automatically approved
{
r.submit(wcApprover.getPage(p, "configure").getFormByName("config"));
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertEquals(0, pcps.size());
List acps = ScriptApproval.get().getApprovedClasspathEntries();
assertNotEquals(0, acps.size());
-
+
for(ScriptApproval.ApprovedClasspathEntry acp: acps) {
ScriptApproval.get().denyApprovedClasspathEntry(acp.getHash());
}
-
+
assertEquals(0, ScriptApproval.get().getPendingClasspathEntries().size());
assertEquals(0, ScriptApproval.get().getApprovedClasspathEntries().size());
}
-
+
// If configured by a user without RUN_SCRIPTS, approval is requested
{
r.submit(wcDevel.getPage(p, "configure").getFormByName("config"));
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertNotEquals(0, pcps.size());
List acps = ScriptApproval.get().getApprovedClasspathEntries();
assertEquals(0, acps.size());
-
+
// don't remove pending classpaths.
}
-
+
// If configured by a user with RUN_SCRIPTS, the classpath is automatically approved, and removed from approval request.
{
assertNotEquals(0, ScriptApproval.get().getPendingClasspathEntries().size());
assertEquals(0, ScriptApproval.get().getApprovedClasspathEntries().size());
-
+
r.submit(wcApprover.getPage(p, "configure").getFormByName("config"));
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertEquals(0, pcps.size());
List acps = ScriptApproval.get().getApprovedClasspathEntries();
assertNotEquals(0, acps.size());
-
+
for(ScriptApproval.ApprovedClasspathEntry acp: acps) {
ScriptApproval.get().denyApprovedClasspathEntry(acp.getHash());
}
-
+
assertEquals(0, ScriptApproval.get().getPendingClasspathEntries().size());
assertEquals(0, ScriptApproval.get().getApprovedClasspathEntries().size());
}
-
+
// If run with SYSTEM user, an approval is requested.
{
r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
-
+
List pcps = ScriptApproval.get().getPendingClasspathEntries();
assertNotEquals(0, pcps.size());
List acps = ScriptApproval.get().getApprovedClasspathEntries();
assertEquals(0, acps.size());
-
+
for(ScriptApproval.PendingClasspathEntry pcp: pcps) {
ScriptApproval.get().denyClasspathEntry(pcp.getHash());
}
-
+
assertEquals(0, ScriptApproval.get().getPendingClasspathEntries().size());
assertEquals(0, ScriptApproval.get().getApprovedClasspathEntries().size());
}
@@ -802,5 +802,40 @@ public void testSandboxClassResolution() throws Exception {
assertTrue(e.getMessage().contains("staticMethod java.lang.System gc"));
}
}
-
+
+ @Test public void testSandboxUsesSelectedBinding() throws Exception {
+ SecureGroovyScript sgs = new SecureGroovyScript("return a++", true, null);
+ Binding b = new Binding();
+ b.setVariable("a", 5);
+ SecureGroovyScript.PreparedScript script = sgs.configuringWithKeyItem().prepare(Jenkins.getInstance().getPluginManager().uberClassLoader, b);
+ try {
+ Object res = script.run();
+ assertTrue((int) res == 5);
+ assertTrue((int) b.getVariable("a") == 6);
+ res = script.run();
+ assertTrue((int) res == 6);
+ assertTrue((int) b.getVariable("a") == 7);
+ } catch (Exception e) {
+ script.cleanUp();
+ throw e;
+ }
+ }
+
+ @Test public void testNonSandboxUsesSelectedBinding() throws Exception {
+ SecureGroovyScript sgs = new SecureGroovyScript("return a++", false, null);
+ Binding b = new Binding();
+ b.setVariable("a", 5);
+ SecureGroovyScript.PreparedScript script = sgs.configuringWithKeyItem().prepare(Jenkins.getInstance().getPluginManager().uberClassLoader, b);
+ try {
+ Object res = script.run();
+ assertTrue((int) res == 5);
+ assertTrue((int) b.getVariable("a") == 6);
+ res = script.run();
+ assertTrue((int) res == 6);
+ assertTrue((int) b.getVariable("a") == 7);
+ } catch (Exception e) {
+ script.cleanUp();
+ throw e;
+ }
+ }
}