aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/fasterxml/jackson/databind/jsontype/DefaultBaseTypeLimitingValidator.java
blob: 86505dec5ed03726a342bae8bba8f8bbc931a6c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.fasterxml.jackson.databind.jsontype;

import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.cfg.MapperConfig;

/**
 * {@link PolymorphicTypeValidator} that will only allow polymorphic handling if
 * the base type is NOT one of potential dangerous base types (see {@link #isUnsafeBaseType}
 * for specific list of such base types). No further validation is performed on subtype.
 *<p>
 * Note that when using potentially unsafe base type like {@link java.lang.Object} a custom
 * implementation (or subtype with override) is needed. Most commonly subclasses would
 * override both {@link #isUnsafeBaseType} and {@link #isSafeSubType}: former to allow
 * all (or just more) base types, and latter to add actual validation of subtype.
 *
 * @since 2.11
 */
public class DefaultBaseTypeLimitingValidator
    extends PolymorphicTypeValidator
    implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;

    @Override
    public Validity validateBaseType(MapperConfig<?> config, JavaType baseType)
    {
        // Immediately block potentially unsafe base types
        if (isUnsafeBaseType(config, baseType)) {
            return Validity.DENIED;
        }
        // otherwise indicate that type may be ok (so further calls are made --
        // does not matter with base implementation but allows easier sub-classing)
        return Validity.INDETERMINATE;
    }

    @Override
    public Validity validateSubClassName(MapperConfig<?> config,
            JavaType baseType, String subClassName) {
        // return INDETERMINATE just for easier sub-classing
        return Validity.INDETERMINATE;
    }

    @Override
    public Validity validateSubType(MapperConfig<?> config, JavaType baseType,
            JavaType subType)
    {
        return isSafeSubType(config, baseType, subType)
                ? Validity.ALLOWED
                : Validity.DENIED;
    }

    /**
     * Helper method called to determine if the given base type is known to be
     * problematic regarding possible "gadget types".
     * Currently includes following types:
     *<ul>
     *  <li>{@link java.lang.Object}</li>
     *  <li>{@link java.io.Closeable}</li>
     *  <li>{@link java.io.Serializable}</li>
     *  <li>{@link java.lang.AutoCloseable}</li>
     *  <li>{@link java.lang.Cloneable}</li>
     *  <li>{@link java.util.logging.Handler}</li>
     *  <li>{@link javax.naming.Referenceable}</li>
     *  <li>{@link javax.sql.DataSource}</li>
     *</ul>
     * which are JDK-included super types of at least one gadget type (not necessarily
     * included in JDK)
     *
     * @param config Current mapper configuration
     * @param baseType Base type to test
     */
    protected boolean isUnsafeBaseType(MapperConfig<?> config, JavaType baseType)
    {
        return UnsafeBaseTypes.instance.isUnsafeBaseType(baseType.getRawClass());
    }

    /**
     * Helper called to determine whether given actual subtype is considered safe
     * to process: this will only be called if subtype was considered acceptable
     * earlier.
     *
     * @param config Current mapper configuration
     * @param baseType Base type of sub type (validated earlier)
     * @param subType Sub type to test
     */
    protected boolean isSafeSubType(MapperConfig<?> config,
            JavaType baseType, JavaType subType)
    {
        return true;
    }

    private final static class UnsafeBaseTypes {
        public final static UnsafeBaseTypes instance = new UnsafeBaseTypes();

        private final Set<String> UNSAFE = new HashSet<>();
        {
            // first add names of types in `java.base`
            UNSAFE.add(Object.class.getName());
            UNSAFE.add(java.io.Closeable.class.getName());
            UNSAFE.add(java.io.Serializable.class.getName());
            UNSAFE.add(AutoCloseable.class.getName());
            UNSAFE.add(Cloneable.class.getName());

            // and then couple others typically included in JDK, but that we
            // prefer not adding direct reference to
            UNSAFE.add("java.util.logging.Handler");
            UNSAFE.add("javax.naming.Referenceable");
            UNSAFE.add("javax.sql.DataSource");
        }
        
        public boolean isUnsafeBaseType(Class<?> rawBaseType)
        {
            return UNSAFE.contains(rawBaseType.getName());
        }
    }
}