Skip to content

Commit

Permalink
police against issues with writing cyclic data (#421)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning authored Jul 18, 2023
1 parent b6edbff commit 95c10ca
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,11 @@ public CsvGenerator disable(Feature f) {
return this;
}

@Override
public StreamWriteConstraints streamWriteConstraints() {
return _ioContext.streamWriteConstraints();
}

/*
/**********************************************************
/* Public API: low-level I/O
Expand Down Expand Up @@ -551,6 +556,7 @@ && _skipValue && isEnabled(JsonGenerator.Feature.IGNORE_UNKNOWN)) {
}
}
_tokenWriteContext = _tokenWriteContext.createChildArrayContext(null);
streamWriteConstraints().validateNestingDepth(_tokenWriteContext.getNestingDepth());
// and that's about it, really
}

Expand Down Expand Up @@ -597,6 +603,7 @@ public final void writeStartObject() throws IOException
}
}
_tokenWriteContext = _tokenWriteContext.createChildObjectContext(null);
streamWriteConstraints().validateNestingDepth(_tokenWriteContext.getNestingDepth());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public TomlGenerator(IOContext ioCtxt, int stdFeatures, int tomlFeatures, Object
_outputEnd = _outputBuffer.length;
}

@Override
public StreamWriteConstraints streamWriteConstraints() {
return _ioContext.streamWriteConstraints();
}

/*
/**********************************************************************
/* Versioned
Expand Down Expand Up @@ -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('[');
}
Expand Down Expand Up @@ -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('{');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ protected DumperOptions buildDumperOptions(int jsonFeatures, int yamlFeatures,
return opt;
}

@Override
public StreamWriteConstraints streamWriteConstraints() {
return _ioContext.streamWriteConstraints();
}

/*
/**********************************************************************
/* Versioned
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> 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));
}
}
}

0 comments on commit 95c10ca

Please sign in to comment.