Skip to content

Commit 3d33744

Browse files
authored
Merge pull request #15 from dheid/master
JENKINS-43637 Secures groovy script execution
2 parents cdceece + 71e3782 commit 3d33744

File tree

5 files changed

+60
-146
lines changed

5 files changed

+60
-146
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ work
33
*.iml
44
*.ipr
55
*.iws
6+
.idea

pom.xml

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>org.jenkins-ci.plugins</groupId>
66
<artifactId>plugin</artifactId>
7-
<version>1.501</version>
7+
<version>2.37</version>
88
</parent>
99

1010
<artifactId>postbuildscript</artifactId>
@@ -28,34 +28,21 @@
2828
</developer>
2929
</developers>
3030

31-
<properties>
32-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
33-
<maven.compiler.source>1.6</maven.compiler.source>
34-
<maven.compiler.target>1.6</maven.compiler.target>
35-
<ivy.plugin.version>1.19</ivy.plugin.version>
36-
</properties>
37-
3831
<scm>
3932
<connection>scm:git:git://github.com/jenkinsci/postbuildscript-plugin.git</connection>
4033
<developerConnection>scm:git:[email protected]:jenkinsci/postbuildscript-plugin.git</developerConnection>
4134
<tag>HEAD</tag>
42-
</scm>
35+
</scm>
4336

4437
<dependencies>
4538

46-
<dependency>
47-
<groupId>org.jenkins-ci.main</groupId>
48-
<artifactId>maven-plugin</artifactId>
49-
</dependency>
50-
51-
<dependency>
52-
<groupId>org.jenkins-ci.plugins</groupId>
53-
<artifactId>ivy</artifactId>
54-
<version>${ivy.plugin.version}</version>
55-
<optional>true</optional>
56-
</dependency>
39+
<dependency>
40+
<groupId>org.jenkins-ci.plugins</groupId>
41+
<artifactId>matrix-project</artifactId>
42+
<version>1.12</version>
43+
</dependency>
5744

58-
</dependencies>
45+
</dependencies>
5946

6047
<repositories>
6148
<repository>

src/main/java/org/jenkinsci/plugins/postbuildscript/PostBuildScript.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import hudson.Launcher;
66
import hudson.Util;
77
import hudson.matrix.*;
8-
import hudson.maven.MavenModuleSet;
98
import hudson.model.*;
109
import hudson.tasks.*;
1110
import org.jenkinsci.plugins.postbuildscript.service.ScriptExecutor;
@@ -317,10 +316,7 @@ public String getHelpFile() {
317316

318317
@Override
319318
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
320-
return Project.class.isAssignableFrom(jobType)
321-
|| MatrixProject.class.isAssignableFrom(jobType)
322-
|| MavenModuleSet.class.isAssignableFrom(jobType)
323-
|| (Hudson.getInstance().getPlugin("ivy") != null && hudson.ivy.IvyModuleSet.class.isAssignableFrom(jobType));
319+
return true;
324320
}
325321

326322
public boolean isMatrixProject(Object job) {

src/main/java/org/jenkinsci/plugins/postbuildscript/PostBuildScriptListener.java

Lines changed: 0 additions & 77 deletions
This file was deleted.
Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
package org.jenkinsci.plugins.postbuildscript.service;
22

3-
import groovy.lang.GroovyShell;
3+
import groovy.lang.Binding;
44
import hudson.EnvVars;
55
import hudson.FilePath;
66
import hudson.Launcher;
77
import hudson.Util;
88
import hudson.model.BuildListener;
9-
import hudson.remoting.Callable;
109
import hudson.remoting.VirtualChannel;
1110
import hudson.tasks.BatchFile;
1211
import hudson.tasks.CommandInterpreter;
1312
import hudson.tasks.Shell;
13+
import jenkins.SlaveToMasterFileCallable;
14+
import jenkins.security.SlaveToMasterCallable;
1415
import org.jenkinsci.plugins.postbuildscript.PostBuildScriptException;
1516
import org.jenkinsci.plugins.postbuildscript.PostBuildScriptLog;
17+
import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript;
1618

1719
import java.io.File;
1820
import java.io.IOException;
1921
import java.io.Serializable;
22+
import java.util.ArrayList;
23+
import java.util.Arrays;
24+
import java.util.List;
2025

2126
/**
2227
* @author Gregory Boissinot
2328
*/
2429
public class ScriptExecutor implements Serializable {
2530

26-
protected PostBuildScriptLog log;
31+
private PostBuildScriptLog log;
2732

2833
private BuildListener listener;
2934

@@ -32,46 +37,44 @@ public ScriptExecutor(PostBuildScriptLog log, BuildListener listener) {
3237
this.listener = listener;
3338
}
3439

35-
public int executeScriptPathAndGetExitCode(FilePath workspace, String scriptFilePath, Launcher launcher) throws PostBuildScriptException {
40+
public int executeScriptPathAndGetExitCode(FilePath workspace, String command, Launcher launcher) throws PostBuildScriptException {
3641

37-
if (scriptFilePath == null) {
38-
throw new NullPointerException("The scriptFilePath object must be set.");
42+
FilePath filePath = resolveScriptPath(workspace, command);
43+
String[] splittedCommand = command.split("\\s+");
44+
String[] parameters;
45+
parameters = new String[splittedCommand.length - 1];
46+
if (splittedCommand.length > 1) {
47+
System.arraycopy(splittedCommand, 1, parameters, 0, parameters.length);
3948
}
49+
return executeScript(workspace, filePath, launcher, parameters);
4050

41-
FilePath filePath = getFilePath(workspace, scriptFilePath);
42-
if (filePath == null) {
43-
throw new PostBuildScriptException(String.format("The script file path '%s' doesn't exist.", scriptFilePath));
44-
}
45-
46-
return executeScript(workspace, filePath, launcher);
4751
}
4852

4953
private String getResolvedContentWithEnvVars(FilePath filePath) throws PostBuildScriptException {
5054
String scriptContentResolved;
5155
try {
5256
log.info("Resolving environment variables for the script content.");
5357
scriptContentResolved =
54-
filePath.act(new FilePath.FileCallable<String>() {
58+
filePath.act(new SlaveToMasterFileCallable<String>() {
59+
@Override
5560
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
5661
String scriptContent = Util.loadFile(f);
5762
return Util.replaceMacro(scriptContent, EnvVars.masterEnvVars);
5863
}
5964
});
60-
} catch (IOException ioe) {
65+
} catch (IOException | InterruptedException ioe) {
6166
throw new PostBuildScriptException("Error to resolve environment variables", ioe);
62-
} catch (InterruptedException ie) {
63-
throw new PostBuildScriptException("Error to resolve environment variables", ie);
6467
}
6568
return scriptContentResolved;
6669
}
6770

68-
private int executeScript(FilePath workspace, FilePath script, final Launcher launcher) throws PostBuildScriptException {
71+
private int executeScript(FilePath workspace, FilePath script, final Launcher launcher, String[] parameters) throws PostBuildScriptException {
6972

7073
assert script != null;
7174
assert launcher != null;
7275

7376
String scriptContent = getResolvedContentWithEnvVars(script);
74-
log.info(String.format("Evaluating the script: \n %s", scriptContent));
77+
log.info(String.format("Executing the script %s with parameters %s", script, Arrays.toString(parameters)));
7578
FilePath tmpFile;
7679
try {
7780
final CommandInterpreter batchRunner;
@@ -81,19 +84,20 @@ private int executeScript(FilePath workspace, FilePath script, final Launcher la
8184
batchRunner = new BatchFile(scriptContent);
8285
}
8386
tmpFile = batchRunner.createScriptFile(workspace);
84-
return launcher.launch().cmds(batchRunner.buildCommandLine(tmpFile)).stdout(listener).pwd(workspace).join();
85-
} catch (InterruptedException ie) {
87+
List<String> args = new ArrayList<>(Arrays.asList(batchRunner.buildCommandLine(tmpFile)));
88+
args.addAll(Arrays.asList(parameters));
89+
return launcher.launch().cmds(args).stdout(listener).pwd(workspace).join();
90+
} catch (InterruptedException | IOException ie) {
8691
throw new PostBuildScriptException("Error to execute script", ie);
87-
} catch (IOException ioe) {
88-
throw new PostBuildScriptException("Error to execute script", ioe);
8992
}
9093
}
9194

9295

9396
private FilePath getFilePath(final FilePath workspace, final String givenPath) throws PostBuildScriptException {
9497

9598
try {
96-
return workspace.act(new FilePath.FileCallable<FilePath>() {
99+
return workspace.act(new SlaveToMasterFileCallable<FilePath>() {
100+
@Override
97101
public FilePath invoke(File ws, VirtualChannel channel) throws IOException, InterruptedException {
98102
File givenFile = new File(givenPath);
99103
if (givenFile.exists()) {
@@ -107,10 +111,8 @@ public FilePath invoke(File ws, VirtualChannel channel) throws IOException, Inte
107111
return null;
108112
}
109113
});
110-
} catch (IOException ioe) {
114+
} catch (IOException | InterruptedException ioe) {
111115
throw new PostBuildScriptException("Error to resolve script path", ioe);
112-
} catch (InterruptedException ie) {
113-
throw new PostBuildScriptException("Error to resolve script path", ie);
114116
}
115117
}
116118

@@ -121,24 +123,22 @@ public boolean performGroovyScript(final FilePath workspace, final String script
121123
throw new NullPointerException("The script content object must be set.");
122124
}
123125
try {
124-
return workspace.act(new Callable<Boolean, Throwable>() {
126+
return workspace.act(new SlaveToMasterCallable<Boolean, Throwable>() {
127+
@Override
125128
public Boolean call() throws Throwable {
126129
final String groovyExpressionResolved = Util.replaceMacro(scriptContent, EnvVars.masterEnvVars);
127130
log.info(String.format("Evaluating the groovy script: \n %s", scriptContent));
128-
GroovyShell shell = new GroovyShell();
129-
shell.setVariable("workspace", new File(workspace.getRemote()));
130-
shell.setVariable("log", log);
131-
shell.setVariable("out", log.getListener().getLogger());
132-
shell.evaluate(groovyExpressionResolved);
131+
Binding binding = new Binding();
132+
binding.setVariable("workspace", new File(workspace.getRemote()));
133+
binding.setVariable("log", log);
134+
binding.setVariable("out", log.getListener().getLogger());
135+
ClassLoader classLoader = getClass().getClassLoader();
136+
SecureGroovyScript script = new SecureGroovyScript(groovyExpressionResolved, false, null);
137+
script.configuringWithNonKeyItem();
138+
script.evaluate(classLoader, binding);
133139
return true;
134140
}
135141
});
136-
} catch (IOException ioe) {
137-
listener.getLogger().print("Problems occurs: " + ioe.getMessage());
138-
return false;
139-
} catch (InterruptedException ie) {
140-
listener.getLogger().print("Problems occurs: " + ie.getMessage());
141-
return false;
142142
} catch (Throwable e) {
143143
listener.getLogger().print("Problems occurs: " + e.getMessage());
144144
return false;
@@ -147,16 +147,23 @@ public Boolean call() throws Throwable {
147147

148148

149149
public boolean performGroovyScriptFile(FilePath workspace, final String scriptFilePath) throws PostBuildScriptException {
150-
if (scriptFilePath == null) {
151-
throw new NullPointerException("The scriptFilePath object must be set.");
150+
FilePath filePath = resolveScriptPath(workspace, scriptFilePath);
151+
152+
String scriptContent = getResolvedContentWithEnvVars(filePath);
153+
return performGroovyScript(workspace, scriptContent);
154+
}
155+
156+
private FilePath resolveScriptPath(FilePath workspace, String commandString) throws PostBuildScriptException {
157+
if (commandString == null) {
158+
throw new NullPointerException("The commandString object must be set.");
152159
}
153160

161+
String scriptFilePath = commandString.split("\\s+")[0];
162+
154163
FilePath filePath = getFilePath(workspace, scriptFilePath);
155164
if (filePath == null) {
156165
throw new PostBuildScriptException(String.format("The script file path '%s' doesn't exist.", scriptFilePath));
157166
}
158-
159-
String scriptContent = getResolvedContentWithEnvVars(filePath);
160-
return performGroovyScript(workspace, scriptContent);
167+
return filePath;
161168
}
162169
}

0 commit comments

Comments
 (0)