Skip to content

Commit 3aa4253

Browse files
committed
Merge branch '3.1' into 3.2
2 parents 330eb2c + bcdf30e commit 3aa4253

7 files changed

Lines changed: 455 additions & 71 deletions

File tree

‎release-notes/VERSION‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ No changes since 3.2
5151
NOTE: type parameters (and element types of array type ids) are now validated
5252
like direct type ids; base type approval (`allowIfBaseType()`) only covers
5353
ones that are subtypes of the base type; `allowIfSubTypeIsArray()` no longer
54-
accepts abstract or interface element types without allow-listing (except `Number`)
54+
accepts abstract or interface element types without allow-listing (except `Number`);
55+
approval of an array type covers its element type only if given as exactly that
56+
class (`allowIfSubType(Bar[].class)`, new
57+
`PolymorphicTypeValidator.isArrayTypeAllowedExplicitly()`), a name-based rule
58+
does not; base type approval of a declared array type covers subtypes of its
59+
element type unless that is `Object`, `Serializable` or `Cloneable`
5560
(reported by @AdamKorcz, @martinfrancois)
56-
(fix by @cowtowncoder, w/ Claude code)
61+
(fix by @cowtowncoder, @martinfrancois)
5762

5863
3.2.3 (21-Sep-2026)
5964

@@ -410,9 +415,14 @@ No changes since 3.2
410415
NOTE: type parameters (and element types of array type ids) are now validated
411416
like direct type ids; base type approval (`allowIfBaseType()`) only covers
412417
ones that are subtypes of the base type; `allowIfSubTypeIsArray()` no longer
413-
accepts abstract or interface element types without allow-listing (except `Number`)
418+
accepts abstract or interface element types without allow-listing (except `Number`);
419+
approval of an array type covers its element type only if given as exactly that
420+
class (`allowIfSubType(Bar[].class)`, new
421+
`PolymorphicTypeValidator.isArrayTypeAllowedExplicitly()`), a name-based rule
422+
does not; base type approval of a declared array type covers subtypes of its
423+
element type unless that is `Object`, `Serializable` or `Cloneable`
414424
(reported by @AdamKorcz, @martinfrancois)
415-
(fix by @cowtowncoder, w/ Claude code)
425+
(fix by @cowtowncoder, @martinfrancois)
416426

417427
3.1.7 (21-Sep-2026)
418428

‎release-notes/VERSION-2.x‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@ Project: jackson-databind
2828
like direct type ids; base type approval (`allowIfBaseType()`) only covers
2929
ones that are subtypes of the base type (also without configured validator);
3030
`allowIfSubTypeIsArray()` no longer accepts abstract or interface element types
31-
without allow-listing (except `Number`)
31+
without allow-listing (except `Number`); approval of an array type covers its
32+
element type only if given as exactly that class (`allowIfSubType(Bar[].class)`,
33+
new `PolymorphicTypeValidator.isArrayTypeAllowedExplicitly()`), a name-based
34+
rule does not; base type approval of a declared array type covers subtypes of
35+
its element type unless that is `Object`, `Serializable` or `Cloneable`
3236
(reported by @AdamKorcz, @martinfrancois)
33-
(fix by @cowtowncoder, w/ Claude code)
37+
(fix by @cowtowncoder, @martinfrancois)
3438

3539
2.22.3 (21-Sep-2026)
3640

@@ -111,9 +115,13 @@ Project: jackson-databind
111115
like direct type ids; base type approval (`allowIfBaseType()`) only covers
112116
ones that are subtypes of the base type (also without configured validator);
113117
`allowIfSubTypeIsArray()` no longer accepts abstract or interface element types
114-
without allow-listing (except `Number`)
118+
without allow-listing (except `Number`); approval of an array type covers its
119+
element type only if given as exactly that class (`allowIfSubType(Bar[].class)`,
120+
new `PolymorphicTypeValidator.isArrayTypeAllowedExplicitly()`), a name-based
121+
rule does not; base type approval of a declared array type covers subtypes of
122+
its element type unless that is `Object`, `Serializable` or `Cloneable`
115123
(reported by @AdamKorcz, @martinfrancois)
116-
(fix by @cowtowncoder, w/ Claude code)
124+
(fix by @cowtowncoder, @martinfrancois)
117125

118126
2.21.7 (21-Sep-2026)
119127

@@ -467,9 +475,13 @@ No changes since 2.19.2
467475
like direct type ids; base type approval (`allowIfBaseType()`) only covers
468476
ones that are subtypes of the base type (also without configured validator);
469477
`allowIfSubTypeIsArray()` no longer accepts abstract or interface element types
470-
without allow-listing (except `Number`)
478+
without allow-listing (except `Number`); approval of an array type covers its
479+
element type only if given as exactly that class (`allowIfSubType(Bar[].class)`,
480+
new `PolymorphicTypeValidator.isArrayTypeAllowedExplicitly()`), a name-based
481+
rule does not; base type approval of a declared array type covers subtypes of
482+
its element type unless that is `Object`, `Serializable` or `Cloneable`
471483
(reported by @AdamKorcz, @martinfrancois)
472-
(fix by @cowtowncoder, w/ Claude code)
484+
(fix by @cowtowncoder, @martinfrancois)
473485

474486
2.18.11 (20-Sep-2026)
475487

‎src/main/java/tools/jackson/databind/DatabindContext.java‎

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import tools.jackson.databind.jsontype.PolymorphicTypeValidator.Validity;
2424
import tools.jackson.databind.jsontype.impl.ApprovedBaseTypeValidator;
2525
import tools.jackson.databind.jsontype.impl.SubTypeValidator;
26-
import tools.jackson.databind.type.ArrayType;
2726
import tools.jackson.databind.type.TypeFactory;
2827
import tools.jackson.databind.util.ClassUtil;
2928
import tools.jackson.databind.util.Converter;
@@ -44,9 +43,6 @@ public abstract class DatabindContext
4443
*/
4544
private final static int MAX_ERROR_STR_LEN = 500;
4645

47-
// [CVE-2026-110879]: for checking whether validator approves all array types
48-
private final static JavaType OBJECT_ARRAY_TYPE = ArrayType.construct(TypeFactory.unknownType(), null);
49-
5046
/*
5147
/**********************************************************************
5248
/* Generic config access
@@ -253,7 +249,8 @@ public JavaType resolveAndValidateSubType(JavaType baseType, String subClass,
253249
// [CVE-2026-110879]: element type of an array type id is attacker-selected
254250
// as well and needs validation like type parameters of generic type ids
255251
// (approval of array type, by base type or by sub-type, does not extend to it)
256-
if (subType.isArrayType()) {
252+
if (subType.isArrayType()
253+
&& !_configuredValidator(ptv).isArrayTypeAllowedExplicitly(this, baseType, subType)) {
257254
_validateArrayElementType(baseType, subClass, baseType, subType, ptv);
258255
}
259256
return subType;
@@ -370,10 +367,11 @@ private void _validateContainedType(JavaType baseType, String typeId, JavaType d
370367
vldBaseType = baseType;
371368
vld = Validity.INDETERMINATE;
372369
} else if ((ptv instanceof ApprovedBaseTypeValidator)
373-
&& (declaredType != null) && !declaredType.isJavaLangObject()
370+
&& (declaredType != null) && !ClassUtil.isArraySuperType(declaredType.getRawClass())
374371
&& param.isTypeOrSubTypeOf(declaredType.getRawClass())) {
375372
// Approval of base type (declared array type) covers subtypes of its
376-
// element type (except for `Object[]`, which would cover everything)
373+
// element type (except for `Object[]`, `Serializable[]` and
374+
// `Cloneable[]`, which would cover everything)
377375
vldr = ptv;
378376
vldBaseType = declaredType;
379377
vld = Validity.ALLOWED;
@@ -419,9 +417,11 @@ && _isApprovedSubType(vldr, vldBaseType, param)) {
419417
/**
420418
* Helper for validating element type of an array type: like other contained types
421419
* (including abstract types: they may still be deserializable, via
422-
* {@code @JsonDeserialize(as=...)} or factory methods), unless the configured
423-
* validator specifically approves the array type itself (see
424-
* {@link #_isSpecificallyApprovedArrayType}).
420+
* {@code @JsonDeserialize(as=...)} or factory methods). Approval of the array
421+
* type does not by itself cover the element type: only the validator can say
422+
* it does (see {@link PolymorphicTypeValidator#isArrayTypeAllowedExplicitly},
423+
* checked by the caller for array types named by type id). Inner dimensions
424+
* and array type parameters always get here.
425425
*
426426
* @param declaredType Declared type of the array, if any ({@code null} if none)
427427
*
@@ -431,31 +431,10 @@ private void _validateArrayElementType(JavaType baseType, String typeId,
431431
JavaType declaredType, JavaType arrayType,
432432
PolymorphicTypeValidator ptv)
433433
{
434-
if (_isSpecificallyApprovedArrayType(declaredType, arrayType, ptv)) {
435-
return;
436-
}
437434
_validateContainedType(baseType, typeId, _declaredContentType(declaredType),
438435
arrayType.getContentType(), ptv);
439436
}
440437

441-
/**
442-
* Helper for checking whether configured validator approves given array type
443-
* specifically (like {@code allowIfSubType(Bar[].class)} does for {@code Bar[]}),
444-
* in which case approval covers its element type too. Approval that also covers
445-
* {@code Object[]} (like with {@code allowIfSubType(Serializable.class)}, which
446-
* matches all array types) does not count, nor does approval of base type.
447-
*
448-
* @since 3.1.8
449-
*/
450-
private boolean _isSpecificallyApprovedArrayType(JavaType declaredType,
451-
JavaType arrayType, PolymorphicTypeValidator ptv)
452-
{
453-
final PolymorphicTypeValidator vldr = _configuredValidator(ptv);
454-
final JavaType vldBaseType = (declaredType == null) ? TypeFactory.unknownType() : declaredType;
455-
return _isApprovedSubType(vldr, vldBaseType, arrayType)
456-
&& !_isApprovedSubType(vldr, TypeFactory.unknownType(), OBJECT_ARRAY_TYPE);
457-
}
458-
459438
private boolean _isApprovedSubType(PolymorphicTypeValidator vldr,
460439
JavaType baseType, JavaType subType)
461440
{

‎src/main/java/tools/jackson/databind/jsontype/BasicPolymorphicTypeValidator.java‎

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tools.jackson.databind.DatabindContext;
88
import tools.jackson.databind.DeserializationContext;
99
import tools.jackson.databind.JavaType;
10+
import tools.jackson.databind.util.ClassUtil;
1011

1112
/**
1213
* Standard {@link BasicPolymorphicTypeValidator} implementation that users may want
@@ -108,6 +109,14 @@ public static class Builder {
108109
*/
109110
protected boolean _acceptArrayTypes = false;
110111

112+
/**
113+
* Array types passed to {@link #allowIfSubType(Class)}, approval of which
114+
* covers their element types (see {@link PolymorphicTypeValidator#isArrayTypeAllowedExplicitly}).
115+
*
116+
* @since 3.1.8
117+
*/
118+
protected Set<Class<?>> _explicitArrayTypes;
119+
111120
protected Builder() { }
112121

113122
// // Methods for checking solely by base type (before subtype even considered)
@@ -225,6 +234,12 @@ public Builder denyForExactBaseType(final Class<?> baseTypeToDeny) {
225234
* would be allowed.
226235
*/
227236
public Builder allowIfSubType(final Class<?> subTypeBase) {
237+
if (subTypeBase.isArray()) {
238+
if (_explicitArrayTypes == null) {
239+
_explicitArrayTypes = new HashSet<>();
240+
}
241+
_explicitArrayTypes.add(subTypeBase);
242+
}
228243
return _appendSubClassMatcher(new TypeMatcher() {
229244
@Override
230245
public boolean match(DatabindContext ctxt, Class<?> clazz) {
@@ -314,9 +329,10 @@ public Builder allowIfSubType(final TypeMatcher matcher) {
314329
* have already approved the array's class name. But since 3.1.8
315330
* ([CVE-2026-110879]) element types of array type ids are also validated
316331
* separately (by {@link tools.jackson.databind.DatabindContext}),
317-
* unless the array type itself is approved specifically: that is, by a
318-
* matcher that does not also match {@code Object[]} (as, for example,
319-
* {@code allowIfSubType(Serializable.class)} does).
332+
* unless the array type was passed as such to {@link #allowIfSubType(Class)}
333+
* ({@code allowIfSubType(Bar[].class)} covers {@code Bar}; a name-based rule
334+
* does not). Note that with this method enabled, sub-class matchers see the
335+
* innermost element type, so use {@code allowIfSubType(Bar.class)} instead.
320336
*<p>
321337
* NOTE (behavior change in 3.1.4 for [databind#5981]): prior versions added a
322338
* matcher that approved every array regardless of element type, which let
@@ -368,7 +384,9 @@ public BasicPolymorphicTypeValidator build() {
368384
(_baseTypeMatchers == null) ? null : _baseTypeMatchers.toArray(new TypeMatcher[0]),
369385
(_subTypeNameMatchers == null) ? null : _subTypeNameMatchers.toArray(new NameMatcher[0]),
370386
(_subTypeClassMatchers == null) ? null : _subTypeClassMatchers.toArray(new TypeMatcher[0]),
371-
_acceptArrayTypes // [databind#5981]
387+
_acceptArrayTypes, // [databind#5981]
388+
// (copied: the builder may go on being used)
389+
(_explicitArrayTypes == null) ? null : new HashSet<>(_explicitArrayTypes)
372390
);
373391
}
374392

@@ -436,6 +454,22 @@ protected Builder _appendSubClassMatcher(TypeMatcher matcher) {
436454
*/
437455
protected final boolean _acceptArrayTypes;
438456

457+
/**
458+
* Array types approval of which covers their element types, see
459+
* {@link #isArrayTypeAllowedExplicitly}; {@code null} if none.
460+
*
461+
* @since 3.1.8
462+
*/
463+
protected final Set<Class<?>> _explicitArrayTypes;
464+
465+
/**
466+
* Those of {@link #_explicitArrayTypes} whose element type is not
467+
* {@code Object}, {@code Serializable} or {@code Cloneable}; {@code null} if none.
468+
*
469+
* @since 3.1.8
470+
*/
471+
private final List<Class<?>> _explicitArrayTypesToMatch;
472+
439473
protected BasicPolymorphicTypeValidator(Set<Class<?>> invalidBaseTypes,
440474
TypeMatcher[] baseTypeMatchers,
441475
NameMatcher[] subTypeNameMatchers, TypeMatcher[] subClassMatchers) {
@@ -447,11 +481,71 @@ protected BasicPolymorphicTypeValidator(Set<Class<?>> invalidBaseTypes,
447481
TypeMatcher[] baseTypeMatchers,
448482
NameMatcher[] subTypeNameMatchers, TypeMatcher[] subClassMatchers,
449483
boolean acceptArrayTypes) {
484+
this(invalidBaseTypes, baseTypeMatchers, subTypeNameMatchers, subClassMatchers,
485+
acceptArrayTypes, null);
486+
}
487+
488+
/**
489+
* @since 3.1.8
490+
*/
491+
protected BasicPolymorphicTypeValidator(Set<Class<?>> invalidBaseTypes,
492+
TypeMatcher[] baseTypeMatchers,
493+
NameMatcher[] subTypeNameMatchers, TypeMatcher[] subClassMatchers,
494+
boolean acceptArrayTypes, Set<Class<?>> explicitArrayTypes) {
450495
_invalidBaseTypes = invalidBaseTypes;
451496
_baseTypeMatchers = baseTypeMatchers;
452497
_subTypeNameMatchers = subTypeNameMatchers;
453498
_subClassMatchers = subClassMatchers;
454499
_acceptArrayTypes = acceptArrayTypes;
500+
_explicitArrayTypes = explicitArrayTypes;
501+
_explicitArrayTypesToMatch = _explicitArrayTypesToMatch(explicitArrayTypes);
502+
}
503+
504+
private static List<Class<?>> _explicitArrayTypesToMatch(Set<Class<?>> arrayTypes)
505+
{
506+
if (arrayTypes == null) {
507+
return null;
508+
}
509+
List<Class<?>> result = new ArrayList<>();
510+
for (Class<?> arrayType : arrayTypes) {
511+
Class<?> element = arrayType;
512+
while (element.isArray()) {
513+
element = element.getComponentType();
514+
}
515+
if (!ClassUtil.isArraySuperType(element)) {
516+
result.add(arrayType);
517+
}
518+
}
519+
return result.isEmpty() ? null : result;
520+
}
521+
522+
/**
523+
* An array type counts as allowed explicitly if it was passed as such to
524+
* {@link Builder#allowIfSubType(Class)}, or if an array type of the same depth
525+
* with a supertype of its element type was ({@code allowIfSubType(Bar[].class)}
526+
* covers {@code SubBar[]}; but {@code Object[]}, {@code Serializable[]} and
527+
* {@code Cloneable[]} only cover themselves). Name-based rules do not count:
528+
* they cannot be told apart from ones matching all arrays of some package or depth.
529+
*/
530+
@Override
531+
public boolean isArrayTypeAllowedExplicitly(DatabindContext ctxt, JavaType baseType,
532+
JavaType arrayType) {
533+
final Class<?> raw = arrayType.getRawClass();
534+
// The array class named by the rule itself always counts, `Serializable[]`
535+
// included: its elements are of an abstract type and get type ids of their own
536+
if ((_explicitArrayTypes != null) && _explicitArrayTypes.contains(raw)) {
537+
return true;
538+
}
539+
if (_explicitArrayTypesToMatch != null) {
540+
// No array is an instance of their element types, so `Bar[][]` is only
541+
// assignable from 2-dimensional arrays of `Bar` and its subtypes
542+
for (Class<?> allowed : _explicitArrayTypesToMatch) {
543+
if (allowed.isAssignableFrom(raw)) {
544+
return true;
545+
}
546+
}
547+
}
548+
return false;
455549
}
456550

457551
public static Builder builder() {

‎src/main/java/tools/jackson/databind/jsontype/PolymorphicTypeValidator.java‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,38 @@ public abstract Validity validateSubClassName(DatabindContext ctxt, JavaType bas
141141
public abstract Validity validateSubType(DatabindContext ctxt, JavaType baseType,
142142
JavaType subType);
143143

144+
/**
145+
* Method called for an array type named directly by a type id (like
146+
* {@code [Lcom.foo.Bar;}), once approved as a subtype of the base type, to
147+
* check whether this validator allows that array type explicitly, so that the
148+
* approval covers its element type ({@code Bar}) as well. If not, the element
149+
* type is validated on its own: approval by a rule that matches other arrays
150+
* too (every {@code Serializable}, every array of some depth or package) says
151+
* nothing about the element type. Not called for inner dimensions of arrays
152+
* nor for array type parameters: their element types are always validated.
153+
*<p>
154+
* Default implementation returns {@code false}. {@link BasicPolymorphicTypeValidator}
155+
* returns {@code true} for array types passed to
156+
* {@link BasicPolymorphicTypeValidator.Builder#allowIfSubType(Class)}, and for arrays
157+
* of the same depth with subtypes of their element type (unless that is
158+
* {@code Object}, {@code Serializable} or {@code Cloneable}).
159+
*<p>
160+
* Validator may also choose to indicate denial by throwing a {@link DatabindException}
161+
* (such as {@link tools.jackson.databind.exc.InvalidTypeIdException})
162+
*
163+
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
164+
* @param baseType Nominal base type the array type was resolved for
165+
* @param arrayType Array type named by the type id, already approved as subtype
166+
*
167+
* @return {@code true} if the approval of the array type covers its element type
168+
*
169+
* @since 3.1.8
170+
*/
171+
public boolean isArrayTypeAllowedExplicitly(DatabindContext ctxt, JavaType baseType,
172+
JavaType arrayType) {
173+
return false;
174+
}
175+
144176
/**
145177
* Shared base class with partial implementation (with all validation calls returning
146178
* {@link Validity#INDETERMINATE}) and convenience methods for indicating failure reasons.

‎src/main/java/tools/jackson/databind/util/ClassUtil.java‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ public static boolean isObjectOrPrimitive(Class<?> cls) {
250250
return (cls == CLS_OBJECT) || cls.isPrimitive();
251251
}
252252

253+
/**
254+
* Whether given type is one that all array types are assignable to:
255+
* {@code Object}, {@code Serializable} or {@code Cloneable}.
256+
*
257+
* @since 3.1.8
258+
*/
259+
public static boolean isArraySuperType(Class<?> cls) {
260+
return (cls == CLS_OBJECT) || (cls == java.io.Serializable.class)
261+
|| (cls == Cloneable.class);
262+
}
263+
253264
public static boolean hasClass(Object inst, Class<?> raw) {
254265
// 10-Nov-2016, tatu: Could use `Class.isInstance()` if we didn't care
255266
// about being exactly that type

0 commit comments

Comments
 (0)