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 @@ -15,9 +15,12 @@
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import io.swagger.v3.parser.urlresolver.PermittedUrlsChecker;
import io.swagger.v3.parser.util.DeserializationUtils;
import io.swagger.v3.parser.util.RemoteUrl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;

import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -35,6 +38,8 @@ public class ReferenceVisitor extends AbstractVisitor {
protected Reference reference;
protected DereferencerContext context;
private PermittedUrlsChecker permittedUrlsChecker;
private final LoaderOptions loaderOptions;


public ReferenceVisitor(
Reference reference,
Expand All @@ -46,6 +51,8 @@ public ReferenceVisitor(
this.visited = visited;
this.visitedMap = visitedMap;
this.context = null;
this.loaderOptions = DeserializationUtils.buildLoaderOptions();

}

public ReferenceVisitor(
Expand All @@ -61,6 +68,7 @@ public ReferenceVisitor(
this.context = context;
this.permittedUrlsChecker = new PermittedUrlsChecker(context.getParseOptions().getRemoteRefAllowList(),
context.getParseOptions().getRemoteRefBlockList());
this.loaderOptions = DeserializationUtils.buildLoaderOptions();
}

public String toBaseURI(String uri) throws Exception{
Expand Down Expand Up @@ -301,7 +309,25 @@ public JsonNode findAnchor(JsonNode root, String anchor) {

public JsonNode deserializeIntoTree(String content) throws Exception {
boolean isJson = content.trim().startsWith("{");
return isJson ? Json31.mapper().readTree(content) : Yaml31.mapper().readTree(content);
if (isJson) {
return Json31.mapper().readTree(content);
}
Yaml yaml = getYaml();

Object yamlObject = yaml.load(content);
return Yaml31.mapper().valueToTree(yamlObject);
}

private Yaml getYaml() {
Yaml yaml;
String yamlCodePoints = System.getProperty("maxYamlCodePoints");
if (yamlCodePoints != null && !yamlCodePoints.isEmpty() && StringUtils.isNumeric(yamlCodePoints)) {
loaderOptions.setCodePointLimit(Integer.parseInt(yamlCodePoints));
yaml = new Yaml(loaderOptions);
} else {
yaml = new Yaml();
}
return yaml;
}

public JsonNode parse(String absoluteUri, List<AuthorizationValue> auths) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ public void setYamlCycleCheck(boolean yamlCycleCheck) {
}

public Integer getMaxYamlCodePoints() {
return maxYamlCodePoints;
return maxYamlCodePoints;
}

public void setMaxYamlCodePoints(Integer maxYamlCodePoints) {
this.maxYamlCodePoints = maxYamlCodePoints;
this.maxYamlCodePoints = maxYamlCodePoints;
}

public Integer getMaxYamlAliasesForCollections() {
Expand All @@ -103,7 +103,7 @@ public void setYamlAllowRecursiveKeys(boolean yamlAllowRecursiveKeys) {
}
}

private static Options options = new Options();
private static final Options options = new Options();

private static final Logger LOGGER = LoggerFactory.getLogger(DeserializationUtils.class);

Expand Down Expand Up @@ -145,7 +145,6 @@ protected void addImplicitResolvers() {
addImplicitResolver(Tag.MERGE, MERGE, "<");
addImplicitResolver(Tag.NULL, NULL, "~nN\0");
addImplicitResolver(Tag.NULL, EMPTY, null);
// addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
}
}

Expand Down Expand Up @@ -249,15 +248,15 @@ public static JsonNode readYamlTree(String contents, ParseOptions parseOptions,
return Json.mapper().convertValue(yaml.load(contents), JsonNode.class);
}
try {
org.yaml.snakeyaml.Yaml yaml = null;
org.yaml.snakeyaml.Yaml yaml;
if (options.isValidateYamlInput()) {
yaml = buildSnakeYaml(new CustomSnakeYamlConstructor());
} else {
yaml = buildSnakeYaml(new SafeConstructor(buildLoaderOptions()));
}
Object o = yaml.load(contents);
if (options.isValidateYamlInput()) {
boolean res = exceedsLimits(o, null, new Integer(0), new IdentityHashMap<Object, Long>(), deserializationUtilsResult);
boolean res = exceedsLimits(o, null, 0, new IdentityHashMap<>(), deserializationUtilsResult);
if (res) {
LOGGER.warn("Error converting snake-parsed yaml to JsonNode");
return getYaml30Mapper().readTree(contents);
Expand All @@ -270,8 +269,8 @@ public static JsonNode readYamlTree(String contents, ParseOptions parseOptions,
//
}
return Json.mapper().convertValue(o, JsonNode.class);
} catch (Throwable e) {
LOGGER.warn("Error snake-parsing yaml content", e);
} catch (Exception e) {
LOGGER.warn(e.getMessage(), e);
if (deserializationUtilsResult != null) {
deserializationUtilsResult.message(e.getMessage());
}
Expand Down Expand Up @@ -302,8 +301,7 @@ public static org.yaml.snakeyaml.Yaml buildSnakeYaml(BaseConstructor constructor
}
try {
LoaderOptions loaderOptions = buildLoaderOptions();
org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(constructor, new Representer(new DumperOptions()), new DumperOptions(), loaderOptions, new CustomResolver());
return yaml;
return new org.yaml.snakeyaml.Yaml(constructor, new Representer(new DumperOptions()), new DumperOptions(), loaderOptions, new CustomResolver());
} catch (Exception e) {
//
LOGGER.error("error building snakeYaml", e);
Expand Down Expand Up @@ -479,8 +477,8 @@ public Object getSingleData(Class<?> type) {
throw new SnakeException("StackOverflow safe-checking yaml content (maxDepth " + options.getMaxYamlDepth() + ")", e);
} catch (DuplicateKeyException e) {
throw new SnakeException(e.getProblem().replace("found duplicate key", "Duplicate field"));
} catch (Throwable e) {
throw new SnakeException("Exception safe-checking yaml content (maxDepth " + options.getMaxYamlDepth() + ", maxYamlAliasesForCollections " + options.getMaxYamlAliasesForCollections() + ")", e);
} catch (Exception e) {
throw new SnakeException(e.getMessage() + "; Max code points: " + options.getMaxYamlCodePoints(), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.swagger.v3.parser;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;

public class OpenAPIV3ParserLargeFilesTest {

@BeforeSuite
public static void init() {
System.setProperty("maxYamlCodePoints", "10000000");
}

@AfterClass
public void cleanUpAfterAllTests() {
System.clearProperty("maxYamlCodePoints");
}

@Test
public void issue2059() {
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
ParseOptions options = new ParseOptions();
options.setResolve(true);
SwaggerParseResult parseResult = openApiParser.readLocation("issue2059/largeFile.yaml", null, options);
OpenAPI openAPI = parseResult.getOpenAPI();

assertNotNull(openAPI);
assertFalse(parseResult.getMessages().stream().anyMatch(message -> message.contains("exceeds the limit: 3145728")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.swagger.v3.parser.reference;

import com.fasterxml.jackson.databind.JsonNode;
import org.testng.annotations.Test;
import org.yaml.snakeyaml.error.YAMLException;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static org.junit.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;

public class ReferenceVisitorTest {

@Test
public void largeFileShouldBeParsedByJacksonLibraryWhenCodeLimitIsSet() throws Exception {
System.setProperty("maxYamlCodePoints", "10000000");
ReferenceVisitor visitor = new ReferenceVisitor(null, null, null, null);
String resourceName = "/issue2059/largeFile.yaml";
String content = readResourceAsString(resourceName);

JsonNode jsonNode = visitor.deserializeIntoTree(content);

assertNotNull(content);
assertFalse(content.isEmpty());
assertNotNull(jsonNode);
System.clearProperty("maxYamlCodePoints");
}

@Test
public void largeFileShouldNotBeParsedByJacksonLibraryWhenCodeLimitIsNotSet() throws Exception {
ReferenceVisitor visitor = new ReferenceVisitor(null, null, null, null);
String resourceName = "/issue2059/largeFile.yaml";
String content = readResourceAsString(resourceName);

try {
visitor.deserializeIntoTree(content);
// Fail if no exception is thrown
org.testng.Assert.fail("Expected YAMLException was not thrown");
} catch (YAMLException ex) {
org.testng.Assert.assertTrue(
ex.getMessage() != null &&
ex.getMessage().contains("The incoming YAML document exceeds the limit: 3145728 code points."),
"Unexpected YAMLException message: " + ex.getMessage()
);
}
}

private String readResourceAsString(String resourceName) throws IOException {
try (InputStream is = this.getClass().getResourceAsStream(resourceName)) {
if (is == null) {
throw new IOException("Resource not found: " + resourceName);
}
java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream();
byte[] data = new byte[4096];
int nRead;
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
}
}
}
Loading
Loading