Skip to content

Commit b2bad9b

Browse files
committed
add StreamWriteConstraints
Update TokenFilterContext.java make stream write constraints accessible Update FilteringGeneratorDelegate.java police nesting depth in json generators Update JsonGeneratorImpl.java add test that should fail fix test test issue more tests revert changes in FilteringGeneratorDelegate Update FilteringGeneratorDelegate.java fix tests
1 parent 881566e commit b2bad9b

23 files changed

+367
-16
lines changed

src/main/java/tools/jackson/core/JsonGenerator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ protected JsonGenerator() { }
5050
@Override
5151
public abstract Version version();
5252

53+
/*
54+
/**********************************************************************
55+
/* Constraints violation checking
56+
/**********************************************************************
57+
*/
58+
59+
/**
60+
* Get the constraints to apply when performing streaming writes.
61+
*/
62+
public StreamWriteConstraints streamWriteConstraints() {
63+
return StreamWriteConstraints.defaults();
64+
}
65+
5366
/*
5467
/**********************************************************************
5568
/* Public API, output configuration, state access
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package tools.jackson.core;
2+
3+
import tools.jackson.core.exc.StreamConstraintsException;
4+
5+
/**
6+
* The constraints to use for streaming writes: used to guard against problematic
7+
* output by preventing processing of "too big" output constructs (values,
8+
* structures).
9+
* Constraints are registered with {@code TokenStreamFactory} (such as
10+
* {@code JsonFactory}); if nothing explicitly specified, default
11+
* constraints are used.
12+
*<p>
13+
* Currently constrained aspects, with default settings, are:
14+
* <ul>
15+
* <li>Maximum Nesting depth: default 1000 (see {@link #DEFAULT_MAX_DEPTH})
16+
* </li>
17+
* </ul>
18+
*
19+
* @since 2.16
20+
*/
21+
public class StreamWriteConstraints
22+
implements java.io.Serializable
23+
{
24+
private static final long serialVersionUID = 1L;
25+
26+
/**
27+
* Default setting for maximum depth: see {@link Builder#maxNestingDepth(int)} for details.
28+
*/
29+
public static final int DEFAULT_MAX_DEPTH = 1000;
30+
31+
protected final int _maxNestingDepth;
32+
33+
private static final StreamWriteConstraints DEFAULT =
34+
new StreamWriteConstraints(DEFAULT_MAX_DEPTH);
35+
36+
public static final class Builder {
37+
private int maxNestingDepth;
38+
39+
/**
40+
* Sets the maximum nesting depth. The depth is a count of objects and arrays that have not
41+
* been closed, `{` and `[` respectively.
42+
*
43+
* @param maxNestingDepth the maximum depth
44+
*
45+
* @return this builder
46+
* @throws IllegalArgumentException if the maxNestingDepth is set to a negative value
47+
*/
48+
public Builder maxNestingDepth(final int maxNestingDepth) {
49+
if (maxNestingDepth < 0) {
50+
throw new IllegalArgumentException("Cannot set maxNestingDepth to a negative value");
51+
}
52+
this.maxNestingDepth = maxNestingDepth;
53+
return this;
54+
}
55+
56+
Builder() {
57+
this(DEFAULT_MAX_DEPTH);
58+
}
59+
60+
Builder(final int maxNestingDepth) {
61+
this.maxNestingDepth = maxNestingDepth;
62+
}
63+
64+
Builder(StreamWriteConstraints src) {
65+
maxNestingDepth = src._maxNestingDepth;
66+
}
67+
68+
public StreamWriteConstraints build() {
69+
return new StreamWriteConstraints(maxNestingDepth);
70+
}
71+
}
72+
73+
/*
74+
/**********************************************************************
75+
/* Life-cycle
76+
/**********************************************************************
77+
*/
78+
79+
protected StreamWriteConstraints(final int maxNestingDepth) {
80+
_maxNestingDepth = maxNestingDepth;
81+
}
82+
83+
public static Builder builder() {
84+
return new Builder();
85+
}
86+
87+
public static StreamWriteConstraints defaults() {
88+
return DEFAULT;
89+
}
90+
91+
/**
92+
* @return New {@link Builder} initialized with settings of this constraints
93+
* instance
94+
*/
95+
public Builder rebuild() {
96+
return new Builder(this);
97+
}
98+
99+
/*
100+
/**********************************************************************
101+
/* Accessors
102+
/**********************************************************************
103+
*/
104+
105+
/**
106+
* Accessor for maximum depth.
107+
* see {@link Builder#maxNestingDepth(int)} for details.
108+
*
109+
* @return Maximum allowed depth
110+
*/
111+
public int getMaxNestingDepth() {
112+
return _maxNestingDepth;
113+
}
114+
115+
/*
116+
/**********************************************************************
117+
/* Convenience methods for validation, document limits
118+
/**********************************************************************
119+
*/
120+
121+
/**
122+
* Convenience method that can be used to verify that the
123+
* nesting depth does not exceed the maximum specified by this
124+
* constraints object: if it does, a
125+
* {@link StreamConstraintsException}
126+
* is thrown.
127+
*
128+
* @param depth count of unclosed objects and arrays
129+
*
130+
* @throws StreamConstraintsException If depth exceeds maximum
131+
*/
132+
public void validateNestingDepth(int depth) throws StreamConstraintsException
133+
{
134+
if (depth > _maxNestingDepth) {
135+
throw _constructException(
136+
"Document nesting depth (%d) exceeds the maximum allowed (%d, from %s)",
137+
depth, _maxNestingDepth,
138+
_constrainRef("getMaxNestingDepth"));
139+
}
140+
}
141+
142+
/*
143+
/**********************************************************************
144+
/* Error reporting
145+
/**********************************************************************
146+
*/
147+
148+
// @since 2.16
149+
protected StreamConstraintsException _constructException(String msgTemplate, Object... args) throws StreamConstraintsException {
150+
throw new StreamConstraintsException(String.format(msgTemplate, args));
151+
}
152+
153+
// @since 2.16
154+
protected String _constrainRef(String method) {
155+
return "`StreamWriteConstraints."+method+"()`";
156+
}
157+
}

src/main/java/tools/jackson/core/TSFBuilder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,18 @@ public abstract class TSFBuilder<F extends TokenStreamFactory,
3737
*/
3838
protected StreamReadConstraints _streamReadConstraints;
3939

40+
/**
41+
* StreamWriteConstraints to use.
42+
*/
43+
protected StreamWriteConstraints _streamWriteConstraints;
44+
4045
// // // Construction
4146

4247
protected TSFBuilder(StreamReadConstraints src,
48+
StreamWriteConstraints swc,
4349
int formatReadF, int formatWriteF) {
4450
_streamReadConstraints = src;
51+
_streamWriteConstraints = swc;
4552
_factoryFeatures = TokenStreamFactory.DEFAULT_FACTORY_FEATURE_FLAGS;
4653
_streamReadFeatures = TokenStreamFactory.DEFAULT_STREAM_READ_FEATURE_FLAGS;
4754
_streamWriteFeatures = TokenStreamFactory.DEFAULT_STREAM_WRITE_FEATURE_FLAGS;

src/main/java/tools/jackson/core/TokenStreamFactory.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ public static int collectDefaults() {
227227
*/
228228
protected final StreamReadConstraints _streamReadConstraints;
229229

230+
/**
231+
* Active StreamWriteConstraints to use.
232+
*/
233+
protected final StreamWriteConstraints _streamWriteConstraints;
234+
230235
/*
231236
/**********************************************************************
232237
/* Construction
@@ -248,8 +253,10 @@ public static int collectDefaults() {
248253
* @param formatWriteFeatures Bitmask of format-specific write features enabled
249254
*/
250255
protected TokenStreamFactory(StreamReadConstraints src,
256+
StreamWriteConstraints swc,
251257
int formatReadFeatures, int formatWriteFeatures) {
252258
_streamReadConstraints = src;
259+
_streamWriteConstraints = swc;
253260
_factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS;
254261
_streamReadFeatures = DEFAULT_STREAM_READ_FEATURE_FLAGS;
255262
_streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURE_FLAGS;
@@ -271,6 +278,7 @@ protected TokenStreamFactory(StreamReadConstraints src,
271278
protected TokenStreamFactory(TSFBuilder<?,?> baseBuilder)
272279
{
273280
_streamReadConstraints = baseBuilder._streamReadConstraints;
281+
_streamWriteConstraints = baseBuilder._streamWriteConstraints;
274282
_factoryFeatures = baseBuilder.factoryFeaturesMask();
275283
_streamReadFeatures = baseBuilder.streamReadFeaturesMask();
276284
_streamWriteFeatures = baseBuilder.streamWriteFeaturesMask();
@@ -287,6 +295,7 @@ protected TokenStreamFactory(TSFBuilder<?,?> baseBuilder)
287295
protected TokenStreamFactory(TokenStreamFactory src)
288296
{
289297
_streamReadConstraints = src._streamReadConstraints;
298+
_streamWriteConstraints = src._streamWriteConstraints;
290299
_factoryFeatures = src._factoryFeatures;
291300
_streamReadFeatures = src._streamReadFeatures;
292301
_streamWriteFeatures = src._streamWriteFeatures;
@@ -495,6 +504,8 @@ public final int getStreamWriteFeatures() {
495504

496505
public StreamReadConstraints streamReadConstraints() { return _streamReadConstraints; }
497506

507+
public StreamWriteConstraints streamWriteConstraints() { return _streamWriteConstraints; }
508+
498509
/*
499510
/**********************************************************************
500511
/* Factory methods for helper objects
@@ -1200,7 +1211,8 @@ public BufferRecycler _getBufferRecycler()
12001211
* @return Context constructed
12011212
*/
12021213
protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged) {
1203-
return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef,
1214+
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
1215+
_getBufferRecycler(), contentRef,
12041216
resourceManaged, null);
12051217
}
12061218

@@ -1216,7 +1228,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
12161228
*/
12171229
protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged,
12181230
JsonEncoding enc) {
1219-
return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef,
1231+
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
1232+
_getBufferRecycler(), contentRef,
12201233
resourceManaged, enc);
12211234
}
12221235

src/main/java/tools/jackson/core/base/BinaryTSFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ public abstract class BinaryTSFactory
2626
*/
2727

2828
protected BinaryTSFactory(StreamReadConstraints src,
29+
StreamWriteConstraints swc,
2930
int formatPF, int formatGF) {
30-
super(src, formatPF, formatGF);
31+
super(src, swc, formatPF, formatGF);
3132
}
3233

3334
/**

src/main/java/tools/jackson/core/base/DecorableTSFactory.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ public abstract static class DecorableTSFBuilder<F extends TokenStreamFactory,
4646
// // // Construction
4747

4848
protected DecorableTSFBuilder(StreamReadConstraints src,
49+
StreamWriteConstraints swc,
4950
int formatPF, int formatGF) {
50-
super(src, formatPF, formatGF);
51+
super(src, swc, formatPF, formatGF);
5152
_inputDecorator = null;
5253
_outputDecorator = null;
5354
}
@@ -101,8 +102,9 @@ public T outputDecorator(OutputDecorator dec) {
101102
*/
102103

103104
protected DecorableTSFactory(StreamReadConstraints src,
105+
StreamWriteConstraints swc,
104106
int formatPF, int formatGF) {
105-
super(src, formatPF, formatGF);
107+
super(src, swc, formatPF, formatGF);
106108
_inputDecorator = null;
107109
_outputDecorator = null;
108110
}

src/main/java/tools/jackson/core/base/TextualTSFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public abstract class TextualTSFactory
2727
*/
2828

2929
protected TextualTSFactory(StreamReadConstraints src,
30+
StreamWriteConstraints swc,
3031
int formatPF, int formatGF) {
31-
super(src, formatPF, formatGF);
32+
super(src, swc, formatPF, formatGF);
3233
}
3334

3435
/**

src/main/java/tools/jackson/core/filter/TokenFilterContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ protected TokenFilterContext(int type, TokenFilterContext parent,
7171
super();
7272
_type = type;
7373
_parent = parent;
74+
_nestingDepth = parent == null ? 0 : parent._nestingDepth + 1;
7475
_filter = filter;
7576
_index = -1;
7677
_currentValue = currValue;

src/main/java/tools/jackson/core/io/IOContext.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import tools.jackson.core.JsonEncoding;
44
import tools.jackson.core.StreamReadConstraints;
5+
import tools.jackson.core.StreamWriteConstraints;
56
import tools.jackson.core.util.BufferRecycler;
67
import tools.jackson.core.util.TextBuffer;
78
import tools.jackson.core.util.ReadConstrainedTextBuffer;
@@ -54,6 +55,8 @@ public class IOContext
5455

5556
protected final StreamReadConstraints _streamReadConstraints;
5657

58+
protected final StreamWriteConstraints _streamWriteConstraints;
59+
5760
/**
5861
* Reference to the allocated I/O buffer for low-level input reading,
5962
* if any allocated.
@@ -104,16 +107,21 @@ public class IOContext
104107
* Main constructor to use.
105108
*
106109
* @param streamReadConstraints constraints for streaming reads
110+
* @param streamWriteConstraints constraints for streaming writes
107111
* @param br BufferRecycler to use, if any ({@code null} if none)
108112
* @param contentRef Input source reference for location reporting
109113
* @param managedResource Whether input source is managed (owned) by Jackson library
110114
* @param enc Encoding in use
111115
*/
112-
public IOContext(StreamReadConstraints streamReadConstraints, BufferRecycler br,
113-
ContentReference contentRef, boolean managedResource,
116+
public IOContext(StreamReadConstraints streamReadConstraints,
117+
StreamWriteConstraints streamWriteConstraints,
118+
BufferRecycler br, ContentReference contentRef, boolean managedResource,
114119
JsonEncoding enc)
115120
{
116-
_streamReadConstraints = streamReadConstraints;
121+
_streamReadConstraints = streamReadConstraints == null ?
122+
StreamReadConstraints.defaults() : streamReadConstraints;
123+
_streamWriteConstraints = streamWriteConstraints == null ?
124+
StreamWriteConstraints.defaults() : streamWriteConstraints;;
117125
_bufferRecycler = br;
118126
_contentReference = contentRef;
119127
_managedResource = managedResource;
@@ -127,6 +135,13 @@ public StreamReadConstraints streamReadConstraints() {
127135
return _streamReadConstraints;
128136
}
129137

138+
/**
139+
* @return constraints for streaming writes
140+
*/
141+
public StreamWriteConstraints streamWriteConstraints() {
142+
return _streamWriteConstraints;
143+
}
144+
130145
public IOContext setEncoding(JsonEncoding enc) {
131146
_encoding = enc;
132147
return this;

src/main/java/tools/jackson/core/json/JsonFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public class JsonFactory
134134
* factory instance.
135135
*/
136136
public JsonFactory() {
137-
super(StreamReadConstraints.defaults(),
137+
super(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(),
138138
DEFAULT_JSON_PARSER_FEATURE_FLAGS, DEFAULT_JSON_GENERATOR_FEATURE_FLAGS);
139139
_rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR;
140140
_characterEscapes = null;
@@ -351,7 +351,7 @@ public JsonParser createNonBlockingByteBufferParser(ObjectReadContext readCtxt)
351351
}
352352

353353
protected IOContext _createNonBlockingContext(Object srcRef) {
354-
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
354+
return new IOContext(_streamReadConstraints, _streamWriteConstraints, _getBufferRecycler(),
355355
ContentReference.rawReference(srcRef), false, JsonEncoding.UTF8);
356356
}
357357

0 commit comments

Comments
 (0)