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
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
import io.swagger.util.Yaml;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.constructor.BaseConstructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
Expand All @@ -26,6 +31,11 @@ public static class Options {
private boolean supportYamlAnchors = System.getProperty("supportYamlAnchors") == null ? true : Boolean.valueOf(System.getProperty("supportYamlAnchors"));
private boolean yamlCycleCheck = System.getProperty("yamlCycleCheck") == null ? true : Boolean.valueOf(System.getProperty("yamlCycleCheck"));


private Integer maxYamlAliasesForCollections = System.getProperty("maxYamlAliasesForCollections") == null ? Integer.MAX_VALUE : Integer.valueOf(System.getProperty("maxYamlAliasesForCollections"));
private boolean yamlAllowRecursiveKeys = System.getProperty("yamlAllowRecursiveKeys") == null ? true : Boolean.valueOf(System.getProperty("yamlAllowRecursiveKeys"));


public Integer getMaxYamlDepth() {
return maxYamlDepth;
}
Expand Down Expand Up @@ -65,6 +75,34 @@ public boolean isYamlCycleCheck() {
public void setYamlCycleCheck(boolean yamlCycleCheck) {
this.yamlCycleCheck = yamlCycleCheck;
}

/**
* @since 1.0.52
*/
public Integer getMaxYamlAliasesForCollections() {
return maxYamlAliasesForCollections;
}

/**
* @since 1.0.52
*/
public void setMaxYamlAliasesForCollections(Integer maxYamlAliasesForCollections) {
this.maxYamlAliasesForCollections = maxYamlAliasesForCollections;
}

/**
* @since 1.0.52
*/
public boolean isYamlAllowRecursiveKeys() {
return yamlAllowRecursiveKeys;
}

/**
* @since 1.0.52
*/
public void setYamlAllowRecursiveKeys(boolean yamlAllowRecursiveKeys) {
this.yamlAllowRecursiveKeys = yamlAllowRecursiveKeys;
}
}

private static Options options = new Options();
Expand Down Expand Up @@ -105,7 +143,7 @@ public static <T> T deserialize(Object contents, String fileOrHost, Class<T> exp
if (isJson) {
result = Json.mapper().readValue((String) contents, expectedType);
} else {
result = Yaml.mapper().readValue((String) contents, expectedType);
result = Yaml.mapper().convertValue(readYamlTree((String) contents), expectedType);
}
} else {
result = Json.mapper().convertValue(contents, expectedType);
Expand All @@ -121,6 +159,29 @@ private static boolean isJson(String contents) {
return contents.toString().trim().startsWith("{");
}


public static org.yaml.snakeyaml.Yaml buildSnakeYaml(BaseConstructor constructor) {
try {
LoaderOptions.class.getMethod("getMaxAliasesForCollections");
} catch (NoSuchMethodException e) {
return new org.yaml.snakeyaml.Yaml(constructor);
}
try {
LoaderOptions loaderOptions = new LoaderOptions();
Method method = LoaderOptions.class.getMethod("setMaxAliasesForCollections", int.class);
method.invoke(loaderOptions, options.getMaxYamlAliasesForCollections());
method = LoaderOptions.class.getMethod("setAllowRecursiveKeys", boolean.class);
method.invoke(loaderOptions, options.isYamlAllowRecursiveKeys());
org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(constructor, new Representer(), new DumperOptions(), loaderOptions);
return yaml;
} catch (ReflectiveOperationException e) {
//
LOGGER.debug("using snakeyaml < 1.25, not setting YAML Billion Laughs Attack snakeyaml level protection");
}
return new org.yaml.snakeyaml.Yaml(constructor);
}


public static JsonNode readYamlTree(String contents) throws IOException {

if (!options.isSupportYamlAnchors()) {
Expand All @@ -129,9 +190,9 @@ public static JsonNode readYamlTree(String contents) throws IOException {
try {
org.yaml.snakeyaml.Yaml yaml = null;
if (options.isValidateYamlInput()) {
yaml = new org.yaml.snakeyaml.Yaml(new CustomSnakeYamlConstructor());
yaml = buildSnakeYaml(new CustomSnakeYamlConstructor());
} else {
yaml = new org.yaml.snakeyaml.Yaml(new SafeConstructor());
yaml = buildSnakeYaml(new SafeConstructor());
}

Object o = yaml.load(contents);
Expand Down Expand Up @@ -297,7 +358,7 @@ public Object getSingleData(Class<?> type) {
} catch (StackOverflowError e) {
throw new SnakeException("StackOverflow safe-checking yaml content (maxDepth " + options.getMaxYamlDepth() + ")", e);
} catch (Throwable e) {
throw new SnakeException("Exception safe-checking yaml content (maxDepth " + options.getMaxYamlDepth() + ")", e);
throw new SnakeException("Exception safe-checking yaml content (maxDepth " + options.getMaxYamlDepth() + ", maxYamlAliasesForCollections " + options.getMaxYamlAliasesForCollections() + ")", e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.models.ModelImpl;
import io.swagger.models.Swagger;
import io.swagger.models.properties.StringProperty;
import io.swagger.parser.util.DeserializationUtils;
import io.swagger.parser.util.SwaggerDeserializationResult;
import io.swagger.util.Json;
Expand All @@ -10,6 +11,7 @@
import java.util.Arrays;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;

public class AnchorTest {
Expand Down Expand Up @@ -111,4 +113,26 @@ public void testIssue998Billion() throws Exception{
DeserializationUtils.getOptions().setMaxYamlReferences(10000000L);

}

@org.testng.annotations.Test
public void testBillionLaughProtectionSnakeYaml() {
SwaggerDeserializationResult result = new SwaggerParser().readWithInfo("billion_laughs_snake_yaml.yaml", null, true);

assertNotNull(result.getSwagger().getDefinitions().get("a1"));
assertEquals(((ModelImpl)result.getSwagger().getDefinitions().get("a1")).getEnum().get(0), "AA1");
assertNotNull(result.getSwagger().getDefinitions().get("c1"));
assertEquals(((StringProperty)result.getSwagger().getDefinitions().get("c1").getProperties().get("a")).getEnum().get(0), "AA1");


DeserializationUtils.getOptions().setMaxYamlAliasesForCollections(50);
DeserializationUtils.getOptions().setYamlAllowRecursiveKeys(false);

result = new SwaggerParser().readWithInfo("billion_laughs_snake_yaml.yaml", null, true);

assertNull(result.getSwagger());
DeserializationUtils.getOptions().setMaxYamlAliasesForCollections(Integer.MAX_VALUE);
DeserializationUtils.getOptions().setYamlAllowRecursiveKeys(true);

}

}
Loading