diff --git a/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java b/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java index 776df7aeb..aa73e62b9 100644 --- a/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java +++ b/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/CsvGenerator.java @@ -478,6 +478,11 @@ public CsvGenerator disable(Feature f) { return this; } + @Override + public StreamWriteConstraints streamWriteConstraints() { + return _ioContext.streamWriteConstraints(); + } + /* /********************************************************** /* Public API: low-level I/O @@ -551,6 +556,7 @@ && _skipValue && isEnabled(JsonGenerator.Feature.IGNORE_UNKNOWN)) { } } _tokenWriteContext = _tokenWriteContext.createChildArrayContext(null); + streamWriteConstraints().validateNestingDepth(_tokenWriteContext.getNestingDepth()); // and that's about it, really } @@ -597,6 +603,7 @@ public final void writeStartObject() throws IOException } } _tokenWriteContext = _tokenWriteContext.createChildObjectContext(null); + streamWriteConstraints().validateNestingDepth(_tokenWriteContext.getNestingDepth()); } @Override diff --git a/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/SimpleTokenWriteContext.java b/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/SimpleTokenWriteContext.java index 0c270fec3..734a4754b 100644 --- a/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/SimpleTokenWriteContext.java +++ b/csv/src/main/java/com/fasterxml/jackson/dataformat/csv/impl/SimpleTokenWriteContext.java @@ -60,6 +60,7 @@ protected SimpleTokenWriteContext(int type, SimpleTokenWriteContext parent, DupD super(); _type = type; _parent = parent; + _nestingDepth = parent == null ? 0 : parent._nestingDepth + 1; _dups = dups; _index = -1; _currentValue = currentValue; diff --git a/csv/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/dos/CyclicDataSerTest.java b/csv/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/dos/CyclicDataSerTest.java new file mode 100644 index 000000000..23440889a --- /dev/null +++ b/csv/src/test/java/com/fasterxml/jackson/dataformat/csv/ser/dos/CyclicDataSerTest.java @@ -0,0 +1,32 @@ +package com.fasterxml.jackson.dataformat.csv.ser.dos; + +import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.ModuleTestBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple unit tests to verify that we fail gracefully if you attempt to serialize + * data that is cyclic (eg a list that contains itself). + */ +public class CyclicDataSerTest extends ModuleTestBase +{ + private final CsvMapper MAPPER = mapperForCsv(); + + public void testListWithSelfReference() throws Exception { + List list = new ArrayList<>(); + list.add(list); + try { + MAPPER.writeValueAsString(list); + fail("expected JsonMappingException"); + } catch (JsonMappingException jmex) { + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamWriteConstraints.DEFAULT_MAX_DEPTH + 1); + assertTrue("JsonMappingException message is as expected?", + jmex.getMessage().startsWith(exceptionPrefix)); + } + } +} diff --git a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java index 34bc1adc7..90f3a2b4d 100644 --- a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java +++ b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java @@ -80,6 +80,11 @@ public JavaPropsGenerator(IOContext ctxt, int stdFeatures, ObjectCodec codec) _jpropContext = JPropWriteContext.createRootContext(); } + @Override + public StreamWriteConstraints streamWriteConstraints() { + return _ioContext.streamWriteConstraints(); + } + @Override // since 2.13 public Object currentValue() { return _jpropContext.getCurrentValue(); @@ -267,6 +272,7 @@ public void writeFieldName(String name) throws IOException public void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _jpropContext = _jpropContext.createChildArrayContext(_basePath.length()); + streamWriteConstraints().validateNestingDepth(_jpropContext.getNestingDepth()); } @Override @@ -281,6 +287,7 @@ public void writeEndArray() throws IOException { public void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _jpropContext = _jpropContext.createChildObjectContext(_basePath.length()); + streamWriteConstraints().validateNestingDepth(_jpropContext.getNestingDepth()); } @Override diff --git a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/io/JPropWriteContext.java b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/io/JPropWriteContext.java index 4117612b3..a857e7f3a 100644 --- a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/io/JPropWriteContext.java +++ b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/io/JPropWriteContext.java @@ -59,6 +59,7 @@ protected JPropWriteContext(int type, JPropWriteContext parent, super(); _type = type; _parent = parent; + _nestingDepth = parent == null ? 0 : parent._nestingDepth + 1; _basePathLength = basePathLength; _index = -1; } diff --git a/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/CyclicDataSerTest.java b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/CyclicDataSerTest.java new file mode 100644 index 000000000..010201727 --- /dev/null +++ b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/CyclicDataSerTest.java @@ -0,0 +1,32 @@ +package com.fasterxml.jackson.dataformat.javaprop.dos; + +import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper; +import com.fasterxml.jackson.dataformat.javaprop.ModuleTestBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple unit tests to verify that we fail gracefully if you attempt to serialize + * data that is cyclic (eg a list that contains itself). + */ +public class CyclicDataSerTest extends ModuleTestBase +{ + private final JavaPropsMapper MAPPER = newPropertiesMapper(); + + public void testListWithSelfReference() throws Exception { + List list = new ArrayList<>(); + list.add(list); + try { + MAPPER.writeValueAsString(list); + fail("expected JsonMappingException"); + } catch (JsonMappingException jmex) { + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamWriteConstraints.DEFAULT_MAX_DEPTH + 1); + assertTrue("JsonMappingException message is as expected?", + jmex.getMessage().startsWith(exceptionPrefix)); + } + } +} diff --git a/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/DeepNestParserTest.java b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/DeepNestParserTest.java index 870982782..6cd5ded74 100644 --- a/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/DeepNestParserTest.java +++ b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/dos/DeepNestParserTest.java @@ -23,8 +23,10 @@ public void testDeeplyNestedData() throws IOException { } fail("expected StreamConstraintsException"); } catch (StreamConstraintsException e) { + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamReadConstraints.DEFAULT_MAX_DEPTH + 1); assertTrue("unexpected exception message: " + e.getMessage(), - e.getMessage().startsWith("Document nesting depth (1001) exceeds the maximum allowed")); + e.getMessage().startsWith(exceptionPrefix)); } } diff --git a/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlGenerator.java b/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlGenerator.java index c9a864d25..d27941ccf 100644 --- a/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlGenerator.java +++ b/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlGenerator.java @@ -91,6 +91,11 @@ public TomlGenerator(IOContext ioCtxt, int stdFeatures, int tomlFeatures, Object _outputEnd = _outputBuffer.length; } + @Override + public StreamWriteConstraints streamWriteConstraints() { + return _ioContext.streamWriteConstraints(); + } + /* /********************************************************************** /* Versioned @@ -343,6 +348,7 @@ public void writeStartArray(Object currValue) throws IOException { _verifyValueWrite("start an array", true); _streamWriteContext = _streamWriteContext.createChildArrayContext(currValue, _basePath.length()); + streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth()); if (_streamWriteContext._inline) { _writeRaw('['); } @@ -373,6 +379,7 @@ public void writeStartObject(Object forValue) throws IOException { // objects aren't always materialized right now _verifyValueWrite("start an object", false); _streamWriteContext = _streamWriteContext.createChildObjectContext(forValue, _basePath.length()); + streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth()); if (_streamWriteContext._inline) { writeRaw('{'); } diff --git a/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlWriteContext.java b/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlWriteContext.java index d81eb7d28..41ad20513 100644 --- a/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlWriteContext.java +++ b/toml/src/main/java/com/fasterxml/jackson/dataformat/toml/TomlWriteContext.java @@ -56,6 +56,7 @@ final class TomlWriteContext extends JsonStreamContext { super(); _type = type; _parent = parent; + _nestingDepth = parent == null ? 0 : parent._nestingDepth + 1; _basePathLength = basePathLength; _index = -1; _currentValue = currValue; diff --git a/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/FuzzTomlReadTest.java b/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/FuzzTomlReadTest.java index db7bd88e9..f0adafd67 100644 --- a/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/FuzzTomlReadTest.java +++ b/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/FuzzTomlReadTest.java @@ -4,6 +4,8 @@ import java.io.InputStream; import java.util.Arrays; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamWriteConstraints; import com.fasterxml.jackson.core.exc.StreamConstraintsException; import org.junit.Assert; import org.junit.Test; @@ -62,7 +64,9 @@ public void testParseInlineTable50432() throws Exception TOML_MAPPER.readTree(is); Assert.fail("Should not pass"); } catch (IOException e) { - verifyException(e, "Document nesting depth (1001) exceeds the maximum allowed"); + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamReadConstraints.DEFAULT_MAX_DEPTH + 1); + verifyException(e, exceptionPrefix); } } } @@ -120,7 +124,9 @@ public void testStackOverflow50083() throws Exception TOML_MAPPER.readTree(input.toString()); Assert.fail("Should not pass"); } catch (StreamConstraintsException e) { - verifyException(e, "Document nesting depth (1001) exceeds the maximum allowed"); + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamWriteConstraints.DEFAULT_MAX_DEPTH + 1); + verifyException(e, exceptionPrefix); } } diff --git a/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/dos/CyclicDataSerTest.java b/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/dos/CyclicDataSerTest.java new file mode 100644 index 000000000..23dd3bb32 --- /dev/null +++ b/toml/src/test/java/com/fasterxml/jackson/dataformat/toml/dos/CyclicDataSerTest.java @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.dataformat.toml.dos; + +import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.toml.TomlMapperTestBase; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Simple unit tests to verify that we fail gracefully if you attempt to serialize + * data that is cyclic (eg a list that contains itself). + */ +public class CyclicDataSerTest extends TomlMapperTestBase +{ + private final ObjectMapper MAPPER = newTomlMapper(); + + @Test + public void testListWithSelfReference() throws Exception { + List list = new ArrayList<>(); + list.add(list); + try { + MAPPER.writeValueAsString(list); + fail("expected JsonMappingException"); + } catch (JsonMappingException jmex) { + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamWriteConstraints.DEFAULT_MAX_DEPTH + 1); + assertTrue("JsonMappingException message is as expected?", + jmex.getMessage().startsWith(exceptionPrefix)); + } + } +} 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 e7073c956..f127f9a3c 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 @@ -380,6 +380,11 @@ protected DumperOptions buildDumperOptions(int jsonFeatures, int yamlFeatures, return opt; } + @Override + public StreamWriteConstraints streamWriteConstraints() { + return _ioContext.streamWriteConstraints(); + } + /* /********************************************************************** /* Versioned @@ -585,6 +590,7 @@ public final void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); + streamWriteConstraints().validateNestingDepth(_writeContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); @@ -613,6 +619,7 @@ public final void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); + streamWriteConstraints().validateNestingDepth(_writeContext.getNestingDepth()); FlowStyle style = _outputOptions.getDefaultFlowStyle(); String yamlTag = _typeId; boolean implicit = (yamlTag == null); diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/dos/CyclicDataSerTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/dos/CyclicDataSerTest.java new file mode 100644 index 000000000..d0239480d --- /dev/null +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/dos/CyclicDataSerTest.java @@ -0,0 +1,33 @@ +package com.fasterxml.jackson.dataformat.yaml.ser.dos; + +import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.ModuleTestBase; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple unit tests to verify that we fail gracefully if you attempt to serialize + * data that is cyclic (eg a list that contains itself). + */ +public class CyclicDataSerTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = YAMLMapper.builder().build(); + + public void testListWithSelfReference() throws Exception { + List list = new ArrayList<>(); + list.add(list); + try { + MAPPER.writeValueAsString(list); + fail("expected JsonMappingException"); + } catch (JsonMappingException jmex) { + String exceptionPrefix = String.format("Document nesting depth (%d) exceeds the maximum allowed", + StreamWriteConstraints.DEFAULT_MAX_DEPTH + 1); + assertTrue("JsonMappingException message is as expected?", + jmex.getMessage().startsWith(exceptionPrefix)); + } + } +}