From 6214e5c3b002841bf8415a44cac90ce60bf7b181 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 20 Dec 2024 19:31:40 -0800 Subject: [PATCH] Merge first part of #4848 (actual fix to "type pollution") (#4862) --- release-notes/CREDITS-2.x | 2 + release-notes/VERSION-2.x | 2 + .../std/StringCollectionDeserializer.java | 44 +++++++++++++------ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index b8f506009b..42dba502e1 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -425,6 +425,8 @@ Jonas Konrad (yawkat@github) * Contributed fix for #3655: `ObjectMapper` default heap consumption increased significantly from 2.13.x to 2.14.0 (2.14.1) + * Contributed fix for #4848: Avoid type pollution in `StringCollectionDeserializer` + (2.18.3) Jirka Kremser (Jiri-Kremser@github) * Suggested #924: SequenceWriter.writeAll() could accept Iterable diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 9a94b5f02d..9989083c96 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -12,6 +12,8 @@ Project: jackson-databind (fix by Joo-Hyuk K) #4844: Fix wrapped array hanlding wrt `null` by `StdDeserializer` (fix by Stanislav S) +#4848: Avoid type pollution in `StringCollectionDeserializer` + (contributed by Jonas K) #4860: `ConstructorDetector.USE_PROPERTIES_BASED` does not work with multiple constructors since 2.18 (reported by Tomáš P) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java index 8997265021..a008a6017c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java @@ -1,12 +1,12 @@ package com.fasterxml.jackson.databind.deser.std; import java.io.IOException; -import java.util.Collection; -import java.util.Objects; +import java.util.*; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; @@ -171,16 +171,15 @@ public ValueInstantiator getValueInstantiator() { /********************************************************** */ - @SuppressWarnings("unchecked") @Override public Collection deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if (_delegateDeserializer != null) { - return (Collection) _valueInstantiator.createUsingDelegate(ctxt, - _delegateDeserializer.deserialize(p, ctxt)); + return castToCollection(_valueInstantiator.createUsingDelegate(ctxt, + _delegateDeserializer.deserialize(p, ctxt))); } - final Collection result = (Collection) _valueInstantiator.createUsingDefault(ctxt); + final Collection result = castToCollection(_valueInstantiator.createUsingDefault(ctxt)); return deserialize(p, ctxt, result); } @@ -273,7 +272,6 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, * throw an exception, or try to handle value as if member of implicit * array, depending on configuration. */ - @SuppressWarnings("unchecked") private final Collection handleNonArray(JsonParser p, DeserializationContext ctxt, Collection result) throws IOException { @@ -285,7 +283,7 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon if (p.hasToken(JsonToken.VALUE_STRING)) { return _deserializeFromString(p, ctxt); } - return (Collection) ctxt.handleUnexpectedToken(_containerType, p); + return castToCollection(ctxt.handleUnexpectedToken(_containerType, p)); } // Strings are one of "native" (intrinsic) types, so there's never type deserializer involved JsonDeserializer valueDes = _valueDeserializer; @@ -307,15 +305,15 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon final CoercionAction act = ctxt.findCoercionAction(logicalType(), handledType(), CoercionInputShape.EmptyString); if (act != CoercionAction.Fail) { - return (Collection) _deserializeFromEmptyString(p, ctxt, act, handledType(), - "empty String (\"\")"); + return castToCollection(_deserializeFromEmptyString(p, ctxt, act, handledType(), + "empty String (\"\")")); } } else if (_isBlank(textValue)) { final CoercionAction act = ctxt.findCoercionFromBlankString(logicalType(), handledType(), CoercionAction.Fail); if (act != CoercionAction.Fail) { - return (Collection) _deserializeFromEmptyString(p, ctxt, act, handledType(), - "blank String (all whitespace)"); + return castToCollection(_deserializeFromEmptyString(p, ctxt, act, handledType(), + "blank String (all whitespace)")); } } // if coercion failed, we can still add it to a list @@ -330,4 +328,24 @@ private final Collection handleNonArray(JsonParser p, DeserializationCon result.add(value); return result; } + + // Used to avoid type pollution: see + // https://micronaut-projects.github.io/micronaut-test/latest/guide/#typePollution + // for details + // + // @since 2.18 + @SuppressWarnings("unchecked") + private static Collection castToCollection(Object o) { + if (o != null) { + // fast path for specific classes to avoid type pollution: + // https://micronaut-projects.github.io/micronaut-test/latest/guide/#typePollution + if (o.getClass() == ArrayList.class) { + return (ArrayList) o; + } + if (o.getClass() == HashSet.class) { + return (HashSet) o; + } + } + return (Collection) o; + } }