diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 554e64c23..95e196108 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -21,6 +21,8 @@ Active Maintainers: (proposed by Mathieu L) #454: (yaml) Unexpected `NumberFormatException` in `YAMLParser` (fix contributed by Arthur C) +#456: (yaml) Support max Read/Write nesting depth limits (`StreamReadConstraints`/ + `StreamWriteConstraints`) for YAML 2.16.1 (24-Dec-2023) diff --git a/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLGenerator.java b/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLGenerator.java index 97494635a..eb369f06e 100644 --- a/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLGenerator.java +++ b/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLGenerator.java @@ -498,7 +498,7 @@ public JsonGenerator writeStartArray() throws JacksonException { _verifyValueWrite("start an array"); _streamWriteContext = _streamWriteContext.createChildArrayContext(null); - streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth()); + _streamWriteConstraints.validateNestingDepth(_streamWriteContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); @@ -536,7 +536,7 @@ public JsonGenerator writeStartObject() throws JacksonException { _verifyValueWrite("start an object"); _streamWriteContext = _streamWriteContext.createChildObjectContext(null); - streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth()); + _streamWriteConstraints.validateNestingDepth(_streamWriteContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); diff --git a/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLParser.java b/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLParser.java index 53bfb6ada..b4f668a84 100644 --- a/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLParser.java +++ b/yaml/src/main/java/tools/jackson/dataformat/yaml/YAMLParser.java @@ -418,6 +418,7 @@ public JsonToken nextToken() throws JacksonException _currentAnchor = map.getAnchor(); _streamReadContext = _streamReadContext.createChildObjectContext( m.map(mark -> mark.getLine()).orElse(0), m.map(mark -> mark.getColumn()).orElse(0)); + _streamReadConstraints.validateNestingDepth(_streamReadContext.getNestingDepth()); return (_currToken = JsonToken.START_OBJECT); case MappingEnd: @@ -429,6 +430,7 @@ public JsonToken nextToken() throws JacksonException _currentAnchor = ((NodeEvent) evt).getAnchor(); _streamReadContext = _streamReadContext.createChildArrayContext( mrk.map(mark -> mark.getLine()).orElse(0), mrk.map(mark -> mark.getColumn()).orElse(0)); + _streamReadConstraints.validateNestingDepth(_streamReadContext.getNestingDepth()); return (_currToken = JsonToken.START_ARRAY); case SequenceEnd: diff --git a/yaml/src/test/java/tools/jackson/dataformat/yaml/ModuleTestBase.java b/yaml/src/test/java/tools/jackson/dataformat/yaml/ModuleTestBase.java index bf2256f9b..d07af9337 100644 --- a/yaml/src/test/java/tools/jackson/dataformat/yaml/ModuleTestBase.java +++ b/yaml/src/test/java/tools/jackson/dataformat/yaml/ModuleTestBase.java @@ -1,5 +1,8 @@ package tools.jackson.dataformat.yaml; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -184,4 +187,25 @@ protected static String trimDocMarker(String doc) } return doc.trim(); } + + protected byte[] readResource(String ref) + { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final byte[] buf = new byte[4000]; + + try (InputStream in = getClass().getResourceAsStream(ref)) { + if (in != null) { + int len; + while ((len = in.read(buf)) > 0) { + bytes.write(buf, 0, len); + } + } + } catch (IOException e) { + throw new RuntimeException("Failed to read resource '"+ref+"': "+e); + } + if (bytes.size() == 0) { + throw new IllegalArgumentException("Failed to read resource '"+ref+"': empty resource?"); + } + return bytes.toByteArray(); + } } diff --git a/yaml/src/test/java/tools/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java b/yaml/src/test/java/tools/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java new file mode 100644 index 000000000..b3b67e955 --- /dev/null +++ b/yaml/src/test/java/tools/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java @@ -0,0 +1,72 @@ +package tools.jackson.dataformat.yaml.constraints; + +import tools.jackson.core.*; +import tools.jackson.core.exc.StreamConstraintsException; + +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; + +import tools.jackson.dataformat.yaml.ModuleTestBase; +import tools.jackson.dataformat.yaml.YAMLFactory; +import tools.jackson.dataformat.yaml.YAMLMapper; + +/** + * Unit test(s) for verifying handling of maximum nesting depth + * for reading (StreamReadConstraints) and writing (StreamWriteConstraints). + */ +public class DeeplyNestedYAMLReadWriteTest + extends ModuleTestBase +{ + private final YAMLMapper YAML_MAPPER = new YAMLMapper( + YAMLFactory.builder() + // Use higher limit for writing to simplify testing setup + .streamReadConstraints(StreamReadConstraints.builder() + .maxNestingDepth(10).build()) + .streamWriteConstraints(StreamWriteConstraints.builder() + .maxNestingDepth(12).build()) + .build() + ); + + public void testDeepNestingRead() throws Exception + { + final String DOC = YAML_MAPPER.writeValueAsString(createDeepNestedDoc(11)); + try (JsonParser p = YAML_MAPPER.createParser(DOC)) { + _testDeepNestingRead(p); + } + } + + private void _testDeepNestingRead(JsonParser p) throws Exception + { + try { + while (p.nextToken() != null) { } + fail("expected StreamConstraintsException"); + } catch (StreamConstraintsException e) { + assertEquals("Document nesting depth (11) exceeds the maximum allowed (10, from `StreamReadConstraints.getMaxNestingDepth()`)", + e.getMessage()); + } + } + + public void testDeepNestingWrite() throws Exception + { + final JsonNode docRoot = createDeepNestedDoc(13); + try { + YAML_MAPPER.writeValueAsString(docRoot); + fail("Should not pass"); + } catch (StreamConstraintsException e) { + e.printStackTrace(); + assertEquals("Document nesting depth (13) exceeds the maximum allowed (12, from `StreamWriteConstraints.getMaxNestingDepth()`)", + e.getMessage()); + } + } + + private JsonNode createDeepNestedDoc(final int depth) throws Exception + { + final ObjectNode root = YAML_MAPPER.createObjectNode(); + ObjectNode curr = root; + for (int i = 0; i < depth; ++i) { + curr = curr.putObject("nested"+i); + } + curr.put("value", 42); + return root; + } +} diff --git a/yaml/src/test/java/tools/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java b/yaml/src/test/java/tools/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java new file mode 100644 index 000000000..dd0067b73 --- /dev/null +++ b/yaml/src/test/java/tools/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java @@ -0,0 +1,26 @@ +package tools.jackson.dataformat.yaml.fuzz; + +import tools.jackson.core.*; +import tools.jackson.core.exc.StreamConstraintsException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; + +import tools.jackson.dataformat.yaml.ModuleTestBase; + +public class FuzzYAML_65918_Test extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newObjectMapper(); + + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65918 + public void testMalformed65918() throws Exception + { + byte[] doc = readResource("/data/fuzz-65918.yaml"); + try (JsonParser p = MAPPER.createParser(doc)) { + JsonNode root = MAPPER.readTree(p); + fail("Should not pass, got: "+root); + } catch (StreamConstraintsException e) { + verifyException(e, "Document nesting depth"); + verifyException(e, "exceeds the maximum allowed"); + } + } +} diff --git a/yaml/src/test/resources/data/fuzz-65918.yaml b/yaml/src/test/resources/data/fuzz-65918.yaml new file mode 100644 index 000000000..b43a78dab --- /dev/null +++ b/yaml/src/test/resources/data/fuzz-65918.yaml @@ -0,0 +1 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -C -C \ No newline at end of file