• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.jsontype;
2 
3 import com.fasterxml.jackson.databind.*;
4 import com.fasterxml.jackson.databind.cfg.MapperConfig;
5 
6 /**
7  * Interface for classes that handle validation of class-name - based subtypes used
8  * with Polymorphic Deserialization: both via "default typing" and explicit
9  * {@code @JsonTypeInfo} when using Java Class name as Type Identifier.
10  * The main purpose, initially, is to allow pluggable allow lists to avoid
11  * security problems that occur with unlimited class names
12  * (See <a href="https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062">
13  * this article</a> for full explanation).
14  *<p>
15  * Calls to methods are done as follows:
16  * <ol>
17  *  <li>When a deserializer is needed for a polymorphic property (including root values) -- either
18  *     for explicitly annotated polymorphic type, or "default typing" -- {@link #validateBaseType}
19  *     is called to see if validity can be determined for all possible types: if
20  *     {@link Validity#ALLOWED} is returned no futher checks are made for any subtypes; of
21  *     {@link Validity#DENIED} is returned, an exception will be thrown to indicate invalid polymorphic
22  *     property
23  *   </li>
24  *  <li>If neither deny nor allowed was returned for property with specific base type, first time
25  *     specific Type Id (Class Name) is encountered, method {@link #validateSubClassName} is called
26  *     with resolved class name: it may indicate allowed/denied, resulting in either allowed use or
27  *     denial with exception
28  *   </li>
29  *  <li>If no denial/allowance indicated, class name is resolved to actual {@link Class}, and
30  *  {@link #validateSubType(MapperConfig, JavaType, JavaType)} is called: if
31  *  {@link Validity#ALLOWED} is returned, usage is accepted; otherwise (denied or indeterminate)
32  *  usage is not allowed and exception is thrown
33  *   </li>
34  * </ol>
35  *<p>
36  * Notes on implementations: implementations must be thread-safe and shareable (usually meaning they
37  * are stateless). Determinations for validity are usually effectively cached on per-property
38  * basis (by virtue of subtype deserializers being cached by polymorphic deserializers) so
39  * caching at validator level is usually not needed. If caching is used, however, it must be done
40  * in thread-safe manner as validators are shared within {@link ObjectMapper} as well as possible
41  * across mappers (in case of default/standard validator).
42  *<p>
43  * Also note that it is strongly recommended that all implementations are based on provided
44  * abstract base class, {@link PolymorphicTypeValidator.Base} which contains helper methods
45  * and default implementations for returning {@link Validity#INDETERMINATE} for validation
46  * methods (to allow only overriding relevant methods implementation cares about)
47  *
48  * @since 2.10
49  */
50 public abstract class PolymorphicTypeValidator
51     implements java.io.Serializable
52 {
53     private static final long serialVersionUID = 1L;
54 
55     /**
56      * Definition of return values to indicate determination regarding validity.
57      */
58     public enum Validity {
59         /**
60          * Value that indicates that Class name or Class is allowed for use without further checking
61          */
62         ALLOWED,
63         /**
64          * Value that indicates that Class name or Class is NOT allowed and no further checks are
65          * needed or allowed
66          */
67         DENIED,
68 
69         /**
70          * Value that indicates that Class name or Class validity can not be confirmed by validator
71          * and further checks are needed.
72          *<p>
73          * Typically if validator can not establish validity from Type Id or Class (name), eventual
74          * determination will be {@code DENIED}, for safety reasons.
75          */
76         INDETERMINATE
77         ;
78     }
79 
80     /**
81      * Method called when a property with polymorphic value is encountered, and a
82      * {@code TypeResolverBuilder} is needed. Intent is to allow early determination
83      * of cases where subtyping is completely denied (for example for security reasons),
84      * or, conversely, allowed for allow subtypes (when base type guarantees that all subtypes
85      * are known to be safe). Check can be thought of as both optimization (for latter case)
86      * and eager-fail (for former case) to give better feedback.
87      *
88      * @param config Configuration for resolution: typically will be {@code DeserializationConfig}
89      * @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
90      *   of this type and assignment compatibility is verified by Jackson core
91      *
92      * @return Determination of general validity of all subtypes of given base type; if
93      *    {@link Validity#ALLOWED} returned, all subtypes will automatically be accepted without
94      *    further checks; is {@link Validity#DENIED} returned no subtyping allowed at all
95      *    (caller will usually throw an exception); otherwise (return {@link Validity#INDETERMINATE})
96      *    per sub-type validation calls are made for each new subclass encountered.
97      */
validateBaseType(MapperConfig<?> config, JavaType baseType)98     public abstract Validity validateBaseType(MapperConfig<?> config, JavaType baseType);
99 
100     /**
101      * Method called after intended class name for subtype has been read (and in case of minimal
102      * class name, expanded to fully-qualified class name) but before attempt is made to
103      * look up actual {@link java.lang.Class} or {@link JavaType}.
104      * Validator may be able to
105      * determine validity of eventual type (and return {@link Validity#ALLOWED} or
106      * {@link Validity#DENIED}) or, if not able to, can defer validation to actual
107      * resolved type by returning {@link Validity#INDETERMINATE}.
108      *<p>
109      * Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
110      * (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
111      *
112      * @param config Configuration for resolution: typically will be {@code DeserializationConfig}
113      * @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
114      *   of this type and assignment compatibility is verified by Jackson core
115      * @param subClassName Name of class that will be resolved to {@link java.lang.Class} if
116      *   (and only if) validity check is not denied.
117      *
118      * @return Determination of validity of given class name, as a subtype of given base type:
119      *   should NOT return {@code null}
120      */
validateSubClassName(MapperConfig<?> config, JavaType baseType, String subClassName)121     public abstract Validity validateSubClassName(MapperConfig<?> config, JavaType baseType,
122             String subClassName) throws JsonMappingException;
123 
124     /**
125      * Method called after class name has been resolved to actual type, in cases where previous
126      * call to {@link #validateSubClassName} returned {@link Validity#INDETERMINATE}.
127      * Validator should be able to determine validity and return appropriate {@link Validity}
128      * value, although it may also
129      *<p>
130      * Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
131      * (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
132      *
133      * @param config Configuration for resolution: typically will be {@code DeserializationConfig}
134      * @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
135      *   of this type and assignment compatibility has been verified by Jackson core
136      * @param subType Resolved subtype to validate
137      *
138      * @return Determination of validity of given class name, as a subtype of given base type:
139      *   should NOT return {@code null}
140      */
validateSubType(MapperConfig<?> config, JavaType baseType, JavaType subType)141     public abstract Validity validateSubType(MapperConfig<?> config, JavaType baseType,
142             JavaType subType) throws JsonMappingException;
143 
144     /**
145      * Shared base class with partial implementation (with all validation calls returning
146      * {@link Validity#INDETERMINATE}) and convenience methods for indicating failure reasons.
147      * Use of this base class is strongly recommended over directly implement
148      */
149     public abstract static class Base
150         extends PolymorphicTypeValidator
151         implements java.io.Serializable
152     {
153         private static final long serialVersionUID = 1L;
154 
155         @Override
validateBaseType(MapperConfig<?> config, JavaType baseType)156         public Validity validateBaseType(MapperConfig<?> config, JavaType baseType) {
157             return Validity.INDETERMINATE;
158         }
159 
160         @Override
validateSubClassName(MapperConfig<?> config, JavaType baseType, String subClassName)161         public Validity validateSubClassName(MapperConfig<?> config, JavaType baseType, String subClassName)
162                 throws JsonMappingException {
163             return Validity.INDETERMINATE;
164         }
165 
166         @Override
validateSubType(MapperConfig<?> config, JavaType baseType, JavaType subType)167         public Validity validateSubType(MapperConfig<?> config, JavaType baseType, JavaType subType)
168                 throws JsonMappingException {
169             return Validity.INDETERMINATE;
170         }
171     }
172 }
173