Skip to content

Commit 6214e5c

Browse files
authored
Merge first part of #4848 (actual fix to "type pollution") (#4862)
1 parent 7d94c2f commit 6214e5c

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

release-notes/CREDITS-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ Jonas Konrad (yawkat@github)
425425
* Contributed fix for #3655: `ObjectMapper` default heap consumption increased significantly
426426
from 2.13.x to 2.14.0
427427
(2.14.1)
428+
* Contributed fix for #4848: Avoid type pollution in `StringCollectionDeserializer`
429+
(2.18.3)
428430
429431
Jirka Kremser (Jiri-Kremser@github)
430432
* Suggested #924: SequenceWriter.writeAll() could accept Iterable

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Project: jackson-databind
1212
(fix by Joo-Hyuk K)
1313
#4844: Fix wrapped array hanlding wrt `null` by `StdDeserializer`
1414
(fix by Stanislav S)
15+
#4848: Avoid type pollution in `StringCollectionDeserializer`
16+
(contributed by Jonas K)
1517
#4860: `ConstructorDetector.USE_PROPERTIES_BASED` does not work with
1618
multiple constructors since 2.18
1719
(reported by Tomáš P)

src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java

+31-13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.fasterxml.jackson.databind.deser.std;
22

33
import java.io.IOException;
4-
import java.util.Collection;
5-
import java.util.Objects;
4+
import java.util.*;
65

76
import com.fasterxml.jackson.annotation.JsonFormat;
87

9-
import com.fasterxml.jackson.core.*;
8+
import com.fasterxml.jackson.core.JsonParser;
9+
import com.fasterxml.jackson.core.JsonToken;
1010

1111
import com.fasterxml.jackson.databind.*;
1212
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
@@ -171,16 +171,15 @@ public ValueInstantiator getValueInstantiator() {
171171
/**********************************************************
172172
*/
173173

174-
@SuppressWarnings("unchecked")
175174
@Override
176175
public Collection<String> deserialize(JsonParser p, DeserializationContext ctxt)
177176
throws IOException
178177
{
179178
if (_delegateDeserializer != null) {
180-
return (Collection<String>) _valueInstantiator.createUsingDelegate(ctxt,
181-
_delegateDeserializer.deserialize(p, ctxt));
179+
return castToCollection(_valueInstantiator.createUsingDelegate(ctxt,
180+
_delegateDeserializer.deserialize(p, ctxt)));
182181
}
183-
final Collection<String> result = (Collection<String>) _valueInstantiator.createUsingDefault(ctxt);
182+
final Collection<String> result = castToCollection(_valueInstantiator.createUsingDefault(ctxt));
184183
return deserialize(p, ctxt, result);
185184
}
186185

@@ -273,7 +272,6 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
273272
* throw an exception, or try to handle value as if member of implicit
274273
* array, depending on configuration.
275274
*/
276-
@SuppressWarnings("unchecked")
277275
private final Collection<String> handleNonArray(JsonParser p, DeserializationContext ctxt,
278276
Collection<String> result) throws IOException
279277
{
@@ -285,7 +283,7 @@ private final Collection<String> handleNonArray(JsonParser p, DeserializationCon
285283
if (p.hasToken(JsonToken.VALUE_STRING)) {
286284
return _deserializeFromString(p, ctxt);
287285
}
288-
return (Collection<String>) ctxt.handleUnexpectedToken(_containerType, p);
286+
return castToCollection(ctxt.handleUnexpectedToken(_containerType, p));
289287
}
290288
// Strings are one of "native" (intrinsic) types, so there's never type deserializer involved
291289
JsonDeserializer<String> valueDes = _valueDeserializer;
@@ -307,15 +305,15 @@ private final Collection<String> handleNonArray(JsonParser p, DeserializationCon
307305
final CoercionAction act = ctxt.findCoercionAction(logicalType(), handledType(),
308306
CoercionInputShape.EmptyString);
309307
if (act != CoercionAction.Fail) {
310-
return (Collection<String>) _deserializeFromEmptyString(p, ctxt, act, handledType(),
311-
"empty String (\"\")");
308+
return castToCollection(_deserializeFromEmptyString(p, ctxt, act, handledType(),
309+
"empty String (\"\")"));
312310
}
313311
} else if (_isBlank(textValue)) {
314312
final CoercionAction act = ctxt.findCoercionFromBlankString(logicalType(), handledType(),
315313
CoercionAction.Fail);
316314
if (act != CoercionAction.Fail) {
317-
return (Collection<String>) _deserializeFromEmptyString(p, ctxt, act, handledType(),
318-
"blank String (all whitespace)");
315+
return castToCollection(_deserializeFromEmptyString(p, ctxt, act, handledType(),
316+
"blank String (all whitespace)"));
319317
}
320318
}
321319
// if coercion failed, we can still add it to a list
@@ -330,4 +328,24 @@ private final Collection<String> handleNonArray(JsonParser p, DeserializationCon
330328
result.add(value);
331329
return result;
332330
}
331+
332+
// Used to avoid type pollution: see
333+
// https://micronaut-projects.github.io/micronaut-test/latest/guide/#typePollution
334+
// for details
335+
//
336+
// @since 2.18
337+
@SuppressWarnings("unchecked")
338+
private static Collection<String> castToCollection(Object o) {
339+
if (o != null) {
340+
// fast path for specific classes to avoid type pollution:
341+
// https://micronaut-projects.github.io/micronaut-test/latest/guide/#typePollution
342+
if (o.getClass() == ArrayList.class) {
343+
return (ArrayList<String>) o;
344+
}
345+
if (o.getClass() == HashSet.class) {
346+
return (HashSet<String>) o;
347+
}
348+
}
349+
return (Collection<String>) o;
350+
}
333351
}

0 commit comments

Comments
 (0)