Skip to content

Commit

Permalink
Yet more work for #2195
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 28, 2019
1 parent 9228b5f commit c456a08
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ public JavaType resolveAndValidateSubType(JavaType baseType, String subClass,
return _throwNotASubtype(baseType, subClass);
}
final JavaType subType = config.getTypeFactory().constructSpecializedType(baseType, cls);
// May skip check if type was allowed by subclass name already
if (vld != Validity.ALLOWED) {
if (ptv.validateSubType(config, baseType, subType) != Validity.ALLOWED) {
return _throwSubtypeClassNotAllowed(baseType, subClass, ptv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,13 @@ protected final static class LaissezFaireValidator
private static final long serialVersionUID = 1L;

public final static LaissezFaireValidator instance = new LaissezFaireValidator();


@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType)
throws JsonMappingException {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt,
JavaType baseType, String subClassName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ public static Builder builder() {
return new Builder();
}

// !!! TODO
@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType) {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt, JavaType baseType, String subClassName)
throws JsonMappingException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
/**
* Interface for classes that handle validation of class name-based subtypes used
* with Polymorphic Deserialization: both via "default typing" and explicit
* {@code @JsonTypeInfo} when using class name as Type Identifier.
* {@code @JsonTypeInfo} when using Java Class name as Type Identifier.
* The main purpose, initially, is to allow pluggable allow/deny lists to avoid
* security problems that occur with unlimited class names
* (See <a href="https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062">
* this article</a> for full explanation).
*<p>
* Call fal
*<p>
* Notes on implementations: implementations must be thread-safe and shareable (usually meaning they
* are stateless). Determinations for validity are usually effectively cached on per-property
* basis (by virtue of subtype deserializers being cached by polymorphic deserializers) so
Expand Down Expand Up @@ -51,6 +53,27 @@ public enum Validity {
;
}

/**
* Method called when a property with polymorphic value is encountered, and a
* {@code TypeResolverBuilder} is needed. Intent is to allow early determination
* of cases where subtyping is completely denied (for example for security reasons),
* or, conversely, allowed for allow subtypes (when base type guarantees that all subtypes
* are known to be safe). Check can be thought of as both optimization (for latter case)
* and eager-fail (for former case) to give better feedback.
*
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
* of this type and assignment compatibility is verified by Jackson core
*
* @return Determination of general validity of all subtypes of given base type; if
* {@link Validity#ALLOWED} returned, all subtypes will automatically be accepted without
* further checks; is {@link Validity#DENIED} returned no subtyping allowed at all
* (caller will usually throw an exception); otherwise (return {@link Validity#INDETERMINATE})
* per sub-type validation calls are made for each new subclass encountered.
*/
public abstract Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType)
throws JsonMappingException;

/**
* Method called after intended class name for subtype has been read (and in case of minimal
* class name, expanded to fully-qualified class name) but before attempt is made to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,14 @@ public class ClassNameIdResolver
{
private final static String JAVA_UTIL_PKG = "java.util.";

/**
* @since 2.10
*/
protected final PolymorphicTypeValidator _subTypeValidator;

/**
* @deprecated since 2.10
*/
@Deprecated
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) {
this(baseType, typeFactory, null);
}

public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
PolymorphicTypeValidator ptv) {
super(baseType, typeFactory);
_subTypeValidator = ptv;
}

/**
* @since 2.10
*/
public static ClassNameIdResolver construct(JavaType baseType, MapperConfig<?> config,
PolymorphicTypeValidator ptv) {
return new ClassNameIdResolver(baseType, config.getTypeFactory(), ptv);
Expand Down Expand Up @@ -72,7 +58,7 @@ public JavaType typeFromId(DatabindContext context, String id) throws IOExceptio
protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
{
// 24-Apr-2019, tatu: [databind#2195] validate as well as resolve:
JavaType t = ctxt.resolveAndValidateSubType(_baseType, id);
JavaType t = ctxt.resolveAndValidateSubType(_baseType, id, _subTypeValidator);
if (t == null) {
if (ctxt instanceof DeserializationContext) {
// First: we may have problem handlers that can deal with it?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ protected MinimalClassNameIdResolver(JavaType baseType, TypeFactory typeFactory,
}
}

/**
* @since 2.10
*/
public static MinimalClassNameIdResolver construct(JavaType baseType, MapperConfig<?> config,
PolymorphicTypeValidator ptv) {
return new MinimalClassNameIdResolver(baseType, config.getTypeFactory(), ptv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config,
if (baseType.isPrimitive()) {
return null;
}
TypeIdResolver idRes = idResolver(config, baseType, subtypes, true, false);
TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator(config),
subtypes, true, false);
switch (_includeAs) {
case WRAPPER_ARRAY:
return new AsArrayTypeSerializer(idRes, null);
Expand Down Expand Up @@ -118,7 +119,11 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
return null;
}

TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
// 27-Apr-2019, tatu: Part of [databind#2195]; must first check whether any subtypes
// of basetypes might be denied or allowed
final PolymorphicTypeValidator subTypeValidator = verifyBaseTypeValidity(config, baseType);

TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator, subtypes, false, true);

JavaType defaultImpl = defineDefaultImpl(config, baseType);

Expand Down Expand Up @@ -236,10 +241,10 @@ public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {

public String getTypeProperty() { return _typeProperty; }
public boolean isTypeIdVisible() { return _typeIdVisible; }

/*
/**********************************************************
/* Internal/subtype methods
/* Internal/subtype factory methods
/**********************************************************
*/

Expand All @@ -249,16 +254,17 @@ public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {
* given configuration.
*/
protected TypeIdResolver idResolver(MapperConfig<?> config,
JavaType baseType, Collection<NamedType> subtypes, boolean forSer, boolean forDeser)
JavaType baseType, PolymorphicTypeValidator subtypeValidator,
Collection<NamedType> subtypes, boolean forSer, boolean forDeser)
{
// Custom id resolver?
if (_customIdResolver != null) { return _customIdResolver; }
if (_idType == null) throw new IllegalStateException("Cannot build, 'init()' not yet called");
switch (_idType) {
case CLASS:
return ClassNameIdResolver.construct(baseType, config, subTypeValidator(config));
return ClassNameIdResolver.construct(baseType, config, subtypeValidator);
case MINIMAL_CLASS:
return MinimalClassNameIdResolver.construct(baseType, config, subTypeValidator(config));
return MinimalClassNameIdResolver.construct(baseType, config, subtypeValidator);
case NAME:
return TypeNameIdResolver.construct(config, baseType, subtypes, forSer, forDeser);
case NONE: // hmmh. should never get this far with 'none'
Expand All @@ -268,6 +274,19 @@ protected TypeIdResolver idResolver(MapperConfig<?> config,
throw new IllegalStateException("Do not know how to construct standard type id resolver for idType: "+_idType);
}

/*
/**********************************************************
/* Internal/subtype factory methods
/**********************************************************
*/


protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config,
JavaType baseType)
{
return subTypeValidator(config);
}

/**
* Overridable helper method for determining actual validator to use when constructing
* type serializers and type deserializers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

/**
* Tests to verify working of customizable {@PolymorphicTypeValidator},
* see [databind#2195]
* see [databind#2195], regarding verification of subtype instances.
*
* @since 2.10
*/
Expand All @@ -33,7 +33,7 @@ static class GoodValue extends BaseValue { }
static class MehValue extends BaseValue { }

// // // Wrapper types

static class AnnotatedWrapper {
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public BaseValue value;
Expand Down Expand Up @@ -62,6 +62,12 @@ protected DefTypeWrapper() { }
static class SimpleNameBasedValidator extends PolymorphicTypeValidator {
private static final long serialVersionUID = 1L;

// No categoric determination, depends on subtype
@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType) {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt, JavaType baseType, String subClassName) {
if (subClassName.equals(BadValue.class.getName())) {
Expand All @@ -82,6 +88,11 @@ public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType, JavaTyp
static class SimpleClassBasedValidator extends PolymorphicTypeValidator {
private static final long serialVersionUID = 1L;

@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType) {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt, JavaType baseType, String subClassName) {
return Validity.INDETERMINATE;
Expand All @@ -96,8 +107,7 @@ public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType, JavaTyp
return Validity.ALLOWED;
}
// defaults to denied, then:
return Validity.INDETERMINATE;
}
return Validity.INDETERMINATE; }
}

// // // Mappers with Default Typing
Expand All @@ -121,7 +131,7 @@ public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType, JavaTyp
private final ObjectMapper MAPPER_EXPLICIT_CLASS_CHECK = jsonMapperBuilder()
.polymorphicTypeValidator(new SimpleClassBasedValidator())
.build();

/*
/**********************************************************************
/* Test methods: default typing
Expand Down

0 comments on commit c456a08

Please sign in to comment.