From f4389b1fa5178a3809975f4ef924a846afc2d390 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Thu, 25 Jan 2024 14:50:36 -0800 Subject: [PATCH] Fixes #456: limit maximum nesting of YAML documents read with StreamReadConstraints (#457) --- release-notes/VERSION-2.x | 2 + .../dataformat/yaml/YAMLGenerator.java | 4 +- .../jackson/dataformat/yaml/YAMLParser.java | 4 +- .../DeeplyNestedYAMLReadWriteTest.java | 70 +++++++++++++++++++ .../yaml/fuzz/FuzzYAML_65918_Test.java | 26 +++++++ yaml/src/test/resources/data/fuzz-65918.yaml | 1 + 6 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java create mode 100644 yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java create mode 100644 yaml/src/test/resources/data/fuzz-65918.yaml 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/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java index 2d9786e9e..b92d5957b 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java @@ -593,7 +593,7 @@ public final void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); - streamWriteConstraints().validateNestingDepth(_writeContext.getNestingDepth()); + _streamWriteConstraints.validateNestingDepth(_writeContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); @@ -622,7 +622,7 @@ public final void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); - streamWriteConstraints().validateNestingDepth(_writeContext.getNestingDepth()); + _streamWriteConstraints.validateNestingDepth(_writeContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java index b776b7416..5f98bb014 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java @@ -517,7 +517,7 @@ public JsonToken nextToken() throws IOException Mark m = evt.getStartMark(); MappingStartEvent map = (MappingStartEvent) evt; _currentAnchor = map.getAnchor(); - _parsingContext = _parsingContext.createChildObjectContext(m.getLine(), m.getColumn()); + createChildObjectContext(m.getLine(), m.getColumn()); return (_currToken = JsonToken.START_OBJECT); } if (evt.is(Event.ID.MappingEnd)) { // actually error; can not have map-end here @@ -526,7 +526,7 @@ public JsonToken nextToken() throws IOException if (evt.is(Event.ID.SequenceStart)) { Mark m = evt.getStartMark(); _currentAnchor = ((NodeEvent)evt).getAnchor(); - _parsingContext = _parsingContext.createChildArrayContext(m.getLine(), m.getColumn()); + createChildArrayContext(m.getLine(), m.getColumn()); return (_currToken = JsonToken.START_ARRAY); } if (evt.is(Event.ID.SequenceEnd)) { diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java new file mode 100644 index 000000000..bfb76aa96 --- /dev/null +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/constraints/DeeplyNestedYAMLReadWriteTest.java @@ -0,0 +1,70 @@ +package com.fasterxml.jackson.dataformat.yaml.constraints; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.exc.StreamConstraintsException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.dataformat.yaml.ModuleTestBase; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.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/com/fasterxml/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java new file mode 100644 index 000000000..3bea01e3d --- /dev/null +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/fuzz/FuzzYAML_65918_Test.java @@ -0,0 +1,26 @@ +package com.fasterxml.jackson.dataformat.yaml.fuzz; + +import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.exc.StreamConstraintsException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import com.fasterxml.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