Skip to content

Commit 444e62c

Browse files
authored
support overriding the codePointLimit (#339)
1 parent 7baf4cc commit 444e62c

File tree

4 files changed

+123
-16
lines changed

4 files changed

+123
-16
lines changed

yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java

+26-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.io.*;
44
import java.net.URL;
5-
import java.nio.charset.Charset;
65

76
import org.yaml.snakeyaml.DumperOptions;
87

@@ -11,14 +10,13 @@
1110
import com.fasterxml.jackson.core.format.MatchStrength;
1211
import com.fasterxml.jackson.core.io.IOContext;
1312
import com.fasterxml.jackson.dataformat.yaml.util.StringQuotingChecker;
13+
import org.yaml.snakeyaml.LoaderOptions;
1414

1515
@SuppressWarnings("resource")
1616
public class YAMLFactory extends JsonFactory
1717
{
1818
private static final long serialVersionUID = 1L;
1919

20-
protected final static Charset UTF8 = Charset.forName("UTF-8");
21-
2220
/**
2321
* Name used to identify YAML format.
2422
* (and returned by {@link #getFormatName()}
@@ -66,6 +64,20 @@ public class YAMLFactory extends JsonFactory
6664
*/
6765
protected final StringQuotingChecker _quotingChecker;
6866

67+
/**
68+
* Configuration for underlying parser to follow, if specified;
69+
* left as {@code null} for backwards compatibility (which means
70+
* whatever default settings {@code SnakeYAML} deems best).
71+
* <p>
72+
* If you need to support parsing YAML files that are larger than 3Mb,
73+
* it is recommended that you provide a LoaderOptions instance where
74+
* you set the Codepoint Limit to a larger value than its 3Mb default.
75+
* </p>
76+
*
77+
* @since 2.14
78+
*/
79+
protected final LoaderOptions _loaderOptions;
80+
6981
/*
7082
/**********************************************************************
7183
/* Factory construction, configuration
@@ -94,6 +106,7 @@ public YAMLFactory(ObjectCodec oc)
94106
//_version = DumperOptions.Version.V1_1;
95107
_version = null;
96108
_quotingChecker = StringQuotingChecker.Default.instance();
109+
_loaderOptions = null;
97110
}
98111

99112
/**
@@ -106,6 +119,7 @@ public YAMLFactory(YAMLFactory src, ObjectCodec oc)
106119
_yamlGeneratorFeatures = src._yamlGeneratorFeatures;
107120
_version = src._version;
108121
_quotingChecker = src._quotingChecker;
122+
_loaderOptions = src._loaderOptions;
109123
}
110124

111125
/**
@@ -119,6 +133,7 @@ protected YAMLFactory(YAMLFactoryBuilder b)
119133
_yamlGeneratorFeatures = b.formatGeneratorFeaturesMask();
120134
_version = b.yamlVersionToWrite();
121135
_quotingChecker = b.stringQuotingChecker();
136+
_loaderOptions = b.loaderOptions();
122137
}
123138

124139
@Override
@@ -462,28 +477,28 @@ public JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOExceptio
462477

463478
@Override
464479
protected YAMLParser _createParser(InputStream in, IOContext ctxt) throws IOException {
465-
return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
466-
_objectCodec, _createReader(in, null, ctxt));
480+
return new YAMLParser(ctxt, _parserFeatures, _yamlParserFeatures,
481+
_loaderOptions, _objectCodec, _createReader(in, null, ctxt));
467482
}
468483

469484
@Override
470485
protected YAMLParser _createParser(Reader r, IOContext ctxt) throws IOException {
471-
return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
472-
_objectCodec, r);
486+
return new YAMLParser(ctxt, _parserFeatures, _yamlParserFeatures,
487+
_loaderOptions, _objectCodec, r);
473488
}
474489

475490
// since 2.4
476491
@Override
477492
protected YAMLParser _createParser(char[] data, int offset, int len, IOContext ctxt,
478493
boolean recyclable) throws IOException {
479-
return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
480-
_objectCodec, new CharArrayReader(data, offset, len));
494+
return new YAMLParser(ctxt, _parserFeatures, _yamlParserFeatures,
495+
_loaderOptions, _objectCodec, new CharArrayReader(data, offset, len));
481496
}
482497

483498
@Override
484499
protected YAMLParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
485-
return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
486-
_objectCodec, _createReader(data, offset, len, null, ctxt));
500+
return new YAMLParser(ctxt, _parserFeatures, _yamlParserFeatures,
501+
_loaderOptions, _objectCodec, _createReader(data, offset, len, null, ctxt));
487502
}
488503

489504
@Override

yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactoryBuilder.java

+51-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.fasterxml.jackson.core.TSFBuilder;
66
import com.fasterxml.jackson.dataformat.yaml.util.StringQuotingChecker;
7+
import org.yaml.snakeyaml.LoaderOptions;
78

89
/**
910
* {@link com.fasterxml.jackson.core.TSFBuilder}
@@ -18,8 +19,6 @@ public class YAMLFactoryBuilder extends TSFBuilder<YAMLFactory, YAMLFactoryBuild
1819
/**********************************************************
1920
*/
2021

21-
// protected int _formatParserFeatures;
22-
2322
/**
2423
* Set of {@link YAMLGenerator.Feature}s enabled, as bitmask.
2524
*/
@@ -40,6 +39,20 @@ public class YAMLFactoryBuilder extends TSFBuilder<YAMLFactory, YAMLFactoryBuild
4039
*/
4140
protected DumperOptions.Version _version;
4241

42+
/**
43+
* Configuration for underlying parser to follow, if specified;
44+
* left as {@code null} for backwards compatibility (which means
45+
* whatever default settings {@code SnakeYAML} deems best).
46+
* <p>
47+
* If you need to support parsing YAML files that are larger than 3Mb,
48+
* it is recommended that you provide a LoaderOptions instance where
49+
* you set the Codepoint Limit to a larger value than its 3Mb default.
50+
* </p>
51+
*
52+
* @since 2.14
53+
*/
54+
protected LoaderOptions _loaderOptions;
55+
4356
/*
4457
/**********************************************************
4558
/* Life cycle
@@ -132,6 +145,25 @@ public YAMLFactoryBuilder yamlVersionToWrite(DumperOptions.Version v) {
132145
return this;
133146
}
134147

148+
/**
149+
* Configuration for underlying parser to follow, if specified;
150+
* left as {@code null} for backwards compatibility (which means
151+
* whatever default settings {@code SnakeYAML} deems best).
152+
* <p>
153+
* If you need to support parsing YAML files that are larger than 3Mb,
154+
* it is recommended that you provide a LoaderOptions instance where
155+
* you set the Codepoint Limit to a larger value than its 3Mb default.
156+
* </p>
157+
*
158+
* @param loaderOptions the {@code SnakeYAML} configuration to use when parsing YAML
159+
* @return This builder instance, to allow chaining
160+
* @since 2.14
161+
*/
162+
public YAMLFactoryBuilder loaderOptions(LoaderOptions loaderOptions) {
163+
_loaderOptions = loaderOptions;
164+
return this;
165+
}
166+
135167
/*
136168
/**********************************************************
137169
/* Accessors
@@ -152,6 +184,23 @@ public StringQuotingChecker stringQuotingChecker() {
152184
return StringQuotingChecker.Default.instance();
153185
}
154186

187+
/**
188+
* Configuration for underlying parser to follow, if specified;
189+
* left as {@code null} for backwards compatibility (which means
190+
* whatever default settings {@code SnakeYAML} deems best).
191+
* <p>
192+
* If you need to support parsing YAML files that are larger than 3Mb,
193+
* it is recommended that you provide a LoaderOptions instance where
194+
* you set the Codepoint Limit to a larger value than its 3Mb default.
195+
* </p>
196+
*
197+
* @return the {@code SnakeYAML} configuration to use when parsing YAML
198+
* @since 2.14
199+
*/
200+
public LoaderOptions loaderOptions() {
201+
return _loaderOptions;
202+
}
203+
155204
@Override
156205
public YAMLFactory build() {
157206
return new YAMLFactory(this);

yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java

+19-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.math.BigInteger;
55

66
import com.fasterxml.jackson.core.io.NumberInput;
7+
import org.yaml.snakeyaml.LoaderOptions;
78
import org.yaml.snakeyaml.error.Mark;
89
import org.yaml.snakeyaml.events.*;
910
import org.yaml.snakeyaml.nodes.NodeId;
@@ -166,16 +167,31 @@ private Feature(boolean defaultState) {
166167
/* Life-cycle
167168
/**********************************************************************
168169
*/
169-
170+
171+
172+
/**
173+
* @deprecated since 2.14, use other constructor
174+
*/
175+
@Deprecated
170176
public YAMLParser(IOContext ctxt, BufferRecycler br,
171177
int parserFeatures, int formatFeatures,
172178
ObjectCodec codec, Reader reader)
173179
{
174-
super(ctxt, parserFeatures);
180+
this(ctxt, parserFeatures, formatFeatures, null, codec, reader);
181+
}
182+
183+
public YAMLParser(IOContext ctxt, int parserFeatures, int formatFeatures,
184+
LoaderOptions loaderOptions, ObjectCodec codec, Reader reader)
185+
{
186+
super(ctxt, parserFeatures);
175187
_objectCodec = codec;
176188
_formatFeatures = formatFeatures;
177189
_reader = reader;
178-
_yamlParser = new ParserImpl(new StreamReader(reader));
190+
if (loaderOptions == null) {
191+
_yamlParser = new ParserImpl(new StreamReader(reader));
192+
} else {
193+
_yamlParser = new ParserImpl(new StreamReader(reader), loaderOptions);
194+
}
179195
_cfgEmptyStringsToNull = Feature.EMPTY_STRING_AS_NULL.enabledIn(formatFeatures);
180196
}
181197

yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/deser/StreamingParseTest.java

+27
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import com.fasterxml.jackson.core.JsonParser;
88
import com.fasterxml.jackson.core.JsonToken;
99

10+
import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException;
1011
import com.fasterxml.jackson.dataformat.yaml.ModuleTestBase;
1112
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
1213
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
14+
import org.yaml.snakeyaml.LoaderOptions;
1315

1416
/**
1517
* Unit tests for checking functioning of the underlying
@@ -597,4 +599,29 @@ public void testTimeLikeValues() throws Exception
597599
assertNull(p.nextToken());
598600
p.close();
599601
}
602+
603+
public void testYamlParseFailsWhenCodePointLimitVerySmall() throws Exception
604+
{
605+
final String YAML = "---\n"
606+
+"content:\n"
607+
+" uri: \"http://javaone.com/keynote.mpg\"\n"
608+
+" title: \"Javaone Keynote\"\n"
609+
+" width: 640\n"
610+
+" height: 480\n"
611+
+" persons:\n"
612+
+" - \"Foo Bar\"\n"
613+
+" - \"Max Power\"\n"
614+
;
615+
LoaderOptions loaderOptions = new LoaderOptions();
616+
loaderOptions.setCodePointLimit(5); //5 bytes
617+
YAMLFactory yamlFactory = YAMLFactory.builder()
618+
.loaderOptions(loaderOptions)
619+
.build();
620+
try (JsonParser p = yamlFactory.createParser(YAML)) {
621+
assertToken(JsonToken.START_OBJECT, p.nextToken());
622+
fail("expected to fail by now");
623+
} catch (JacksonYAMLParseException e) {
624+
assertTrue(e.getMessage().startsWith("The incoming YAML document exceeds the limit: 5 code points."));
625+
}
626+
}
600627
}

0 commit comments

Comments
 (0)