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