77import tools .jackson .databind .DatabindContext ;
88import tools .jackson .databind .DeserializationContext ;
99import 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 () {
0 commit comments