This is an automated email from the ASF dual-hosted git repository.
ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git
The following commit(s) were added to refs/heads/2.3-gae by this push:
new 7a6e6c5 FREEMARKER-124: Changed DefaultMemberAccessPolicy to block
more methods. Added LegacyDefaultMemberAccessPolicy in case an existing
application has problems with the stricter defaults. This caused some
reworkings in other classes too: - Constructor member selectors (in a whitelist
or blacklist) don't match constructors in subclasses anymore. - Added
BlacklistMemberAccessPolicy (by refactoring WhitelistMemberAccessPolicy to
extend MemberSelectorListMemberAccessPolicy). - I [...]
7a6e6c5 is described below
commit 7a6e6c578979186decf9fc3507522a2196e5e236
Author: ddekany <[email protected]>
AuthorDate: Sat Jan 11 15:07:18 2020 +0100
FREEMARKER-124: Changed DefaultMemberAccessPolicy to block more methods.
Added LegacyDefaultMemberAccessPolicy in case an existing application has
problems with the stricter defaults.
This caused some reworkings in other classes too:
- Constructor member selectors (in a whitelist or blacklist) don't match
constructors in subclasses anymore.
- Added BlacklistMemberAccessPolicy (by refactoring
WhitelistMemberAccessPolicy to extend MemberSelectorListMemberAccessPolicy).
- Improved JavaDoc comments in related classes
---
src/main/java/freemarker/core/Configurable.java | 5 +
.../java/freemarker/ext/beans/BeansWrapper.java | 4 +-
.../ext/beans/BeansWrapperConfiguration.java | 1 +
.../ext/beans/BlacklistMemberAccessPolicy.java | 50 ++
.../freemarker/ext/beans/ConstructorMatcher.java | 5 +
.../ext/beans/DefaultMemberAccessPolicy.java | 209 +++++---
.../java/freemarker/ext/beans/FieldMatcher.java | 5 +
...y.java => LegacyDefaultMemberAccessPolicy.java} | 52 +-
.../freemarker/ext/beans/MemberAccessPolicy.java | 23 +-
.../java/freemarker/ext/beans/MemberMatcher.java | 14 +-
...a => MemberSelectorListMemberAccessPolicy.java} | 123 +++--
.../java/freemarker/ext/beans/MethodMatcher.java | 5 +
.../ext/beans/WhitelistMemberAccessPolicy.java | 387 +-------------
.../freemarker/template/DefaultObjectWrapper.java | 9 +-
.../ext/beans/DefaultMemberAccessPolicy-rules | 582 +++++++++++++++++++++
.../freemarker/ext/beans/unsafeMethods.properties | 8 +
src/manual/en_US/book.xml | 25 +-
.../ext/beans/DefaultMemberAccessPolicyTest.java | 143 +++++
...DefaultObjectWrapperMemberAccessPolicyTest.java | 72 +--
.../beans/LegacyDefaultMemberAccessPolicyTest.java | 73 +++
...ava => MemberSelectorListAccessPolicyTest.java} | 114 +++-
.../freemarker/template/ConfigurationTest.java | 38 ++
22 files changed, 1340 insertions(+), 607 deletions(-)
diff --git a/src/main/java/freemarker/core/Configurable.java
b/src/main/java/freemarker/core/Configurable.java
index 89f3445..a0ad656 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -52,6 +52,7 @@ import freemarker.cache.PathRegexMatcher;
import freemarker.cache.TemplateLoader;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperBuilder;
+import freemarker.ext.beans.MemberAccessPolicy;
import freemarker.template.AttemptExceptionReporter;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
@@ -1565,6 +1566,10 @@ public class Configurable {
* FreeMarker 2.3.x, and {@link TemplateClassResolver#SAFER_RESOLVER}
* starting from FreeMarker 2.4.0. If you allow users to upload templates,
* it's important to use a custom restrictive {@link
TemplateClassResolver}.
+ *
+ * <p>Note that the {@link MemberAccessPolicy} used by the {@link
ObjectWrapper} also influences what constructors
+ * are available. Allowing the resolution of the class here is not enough
in itself, as the
+ * {@link MemberAccessPolicy} has to allow exposing the particular
constructor you try to call as well.
*
* @since 2.3.17
*/
diff --git a/src/main/java/freemarker/ext/beans/BeansWrapper.java
b/src/main/java/freemarker/ext/beans/BeansWrapper.java
index d67c3ac..2bda4b9 100644
--- a/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -666,8 +666,8 @@ public class BeansWrapper implements RichObjectWrapper,
WriteProtectable {
}
/**
- * Used to customize what members will be hidden;
- * see {@link
BeansWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)} for more.
+ * Sets the {@link MemberAccessPolicy}; default is {@link
DefaultMemberAccessPolicy#getInstance(Version)}, which
+ * is not appropriate if template editors aren't trusted.
*
* @since 2.3.30
*/
diff --git a/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
b/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
index f791d12..323275e 100644
--- a/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
+++ b/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
@@ -229,6 +229,7 @@ public abstract class BeansWrapperConfiguration implements
Cloneable {
return classIntrospectorBuilder.getMemberAccessPolicy();
}
+ /** See {@link BeansWrapper#setMemberAccessPolicy(MemberAccessPolicy)}. */
public void setMemberAccessPolicy(MemberAccessPolicy memberAccessPolicy) {
classIntrospectorBuilder.setMemberAccessPolicy(memberAccessPolicy);
}
diff --git
a/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
new file mode 100644
index 0000000..f81890c
--- /dev/null
+++ b/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package freemarker.ext.beans;
+
+import java.util.Collection;
+
+/**
+ * Blacklist-based member access policy, that is, members that are matched by
the listing will not be accessible, all
+ * others will be. Note that {@link BeansWrapper} and its subclasses doesn't
discover all members on the first place,
+ * and the {@link MemberAccessPolicy} just removes from that set of members,
never adds to it.
+ *
+ * <p>This class is rarely useful in itself, and mostly meant to be used when
composing a {@link MemberAccessPolicy}
+ * from other {@link MemberAccessPolicy}-es. If you are serious about
security, never use this alone; consider using
+ * {@link WhitelistMemberAccessPolicy} as part of your solution.
+ *
+ * <p>See more about the rules at {@link
MemberSelectorListMemberAccessPolicy}. Unlike
+ * {@link WhitelistMemberAccessPolicy}, {@link BlacklistMemberAccessPolicy}
doesn't have annotations that can be used
+ * to add members to the member selector list.
+ *
+ * @since 2.3.30
+ */
+public class BlacklistMemberAccessPolicy extends
MemberSelectorListMemberAccessPolicy {
+
+ /**
+ * @param memberSelectors
+ * List of member selectors; see {@link
MemberSelectorListMemberAccessPolicy} class-level documentation for
+ * more.
+ */
+ public BlacklistMemberAccessPolicy(Collection<MemberSelector>
memberSelectors) {
+ super(memberSelectors, ListType.BLACKLIST, null);
+ }
+
+}
diff --git a/src/main/java/freemarker/ext/beans/ConstructorMatcher.java
b/src/main/java/freemarker/ext/beans/ConstructorMatcher.java
index 2c94576..0f5548c 100644
--- a/src/main/java/freemarker/ext/beans/ConstructorMatcher.java
+++ b/src/main/java/freemarker/ext/beans/ConstructorMatcher.java
@@ -31,4 +31,9 @@ final class ConstructorMatcher extends
MemberMatcher<Constructor<?>, ExecutableM
protected ExecutableMemberSignature toMemberSignature(Constructor<?>
member) {
return new ExecutableMemberSignature(member);
}
+
+ @Override
+ protected boolean matchInUpperBoundTypeSubtypes() {
+ return false;
+ }
}
diff --git a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
index 8c1186d..c27f662 100644
--- a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
@@ -19,93 +19,37 @@
package freemarker.ext.beans;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.HashMap;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
+import java.util.List;
import java.util.Set;
-import java.util.StringTokenizer;
+import
freemarker.ext.beans.MemberSelectorListMemberAccessPolicy.MemberSelector;
import freemarker.template.Version;
import freemarker.template._TemplateAPI;
-import freemarker.template.utility.ClassUtil;
/**
- * Legacy black list based member access policy, used only to keep old
behavior, as it can't provide meaningful safety.
- * Do not use it if you allow untrusted users to edit templates!
+ * Member access policy, used to implement default behavior that's mostly
compatible with pre-2.3.30 versions, but is
+ * somewhat safer; it still can't provide safety in practice, if you allow
untrusted users to edit templates! Use
+ * {@link WhitelistMemberAccessPolicy} if you need stricter control.
*
* @since 2.3.30
*/
public final class DefaultMemberAccessPolicy implements MemberAccessPolicy {
- private static final String UNSAFE_METHODS_PROPERTIES =
"unsafeMethods.properties";
- private static final Set<Method> UNSAFE_METHODS = createUnsafeMethodsSet();
-
- private static Set<Method> createUnsafeMethodsSet() {
- try {
- Properties props = ClassUtil.loadProperties(BeansWrapper.class,
UNSAFE_METHODS_PROPERTIES);
- Set<Method> set = new HashSet<Method>(props.size() * 4 / 3, 1f);
- Map<String, Class<?>> primClasses = createPrimitiveClassesMap();
- for (Object key : props.keySet()) {
- try {
- set.add(parseMethodSpec((String) key, primClasses));
- } catch (ClassNotFoundException e) {
- if (ClassIntrospector.DEVELOPMENT_MODE) {
- throw e;
- }
- } catch (NoSuchMethodException e) {
- if (ClassIntrospector.DEVELOPMENT_MODE) {
- throw e;
- }
- }
- }
- return set;
- } catch (Exception e) {
- throw new RuntimeException("Could not load unsafe method set", e);
- }
- }
-
- private static Method parseMethodSpec(String methodSpec, Map<String,
Class<?>> primClasses)
- throws ClassNotFoundException,
- NoSuchMethodException {
- int brace = methodSpec.indexOf('(');
- int dot = methodSpec.lastIndexOf('.', brace);
- Class<?> clazz = ClassUtil.forName(methodSpec.substring(0, dot));
- String methodName = methodSpec.substring(dot + 1, brace);
- String argSpec = methodSpec.substring(brace + 1, methodSpec.length() -
1);
- StringTokenizer tok = new StringTokenizer(argSpec, ",");
- int argcount = tok.countTokens();
- Class<?>[] argTypes = new Class[argcount];
- for (int i = 0; i < argcount; i++) {
- String argClassName = tok.nextToken();
- argTypes[i] = primClasses.get(argClassName);
- if (argTypes[i] == null) {
- argTypes[i] = ClassUtil.forName(argClassName);
- }
- }
- return clazz.getMethod(methodName, argTypes);
- }
-
- private static Map<String, Class<?>> createPrimitiveClassesMap() {
- Map<String, Class<?>> map = new HashMap<String, Class<?>>();
- map.put("boolean", Boolean.TYPE);
- map.put("byte", Byte.TYPE);
- map.put("char", Character.TYPE);
- map.put("short", Short.TYPE);
- map.put("int", Integer.TYPE);
- map.put("long", Long.TYPE);
- map.put("float", Float.TYPE);
- map.put("double", Double.TYPE);
- return map;
- }
-
private static final DefaultMemberAccessPolicy INSTANCE = new
DefaultMemberAccessPolicy();
- private DefaultMemberAccessPolicy() {
- }
+ private final Set<Class<?>> whitelistRuleFinalClasses;
+ private final Set<Class<?>> whitelistRuleNonFinalClasses;
+ private final WhitelistMemberAccessPolicy whitelistMemberAccessPolicy;
+ private final BlacklistMemberAccessPolicy blacklistMemberAccessPolicy;
/**
* Returns the singleton that's compatible with the given incompatible
improvements version.
@@ -117,24 +61,127 @@ public final class DefaultMemberAccessPolicy implements
MemberAccessPolicy {
return INSTANCE;
}
- public ClassMemberAccessPolicy forClass(Class<?> containingClass) {
- return CLASS_MEMBER_ACCESS_POLICY_INSTANCE;
- }
+ private DefaultMemberAccessPolicy() {
+ try {
+ ClassLoader classLoader =
DefaultMemberAccessPolicy.class.getClassLoader();
+
+ whitelistRuleFinalClasses = new HashSet<Class<?>>();
+ whitelistRuleNonFinalClasses = new HashSet<Class<?>>();
+ Set<Class<?>> typesWithBlacklistUnlistedRule = new
HashSet<Class<?>>();
+ List<MemberSelector> whitelistMemberSelectors = new
ArrayList<MemberSelector>();
+ for (String line : loadMemberSelectorFileLines()) {
+ line = line.trim();
+ if (!MemberSelector.isIgnoredLine(line)) {
+ if (line.startsWith("@")) {
+ String[] lineParts = line.split("\\s+");
+ if (lineParts.length != 2) {
+ throw new IllegalStateException("Malformed @ line:
" + line);
+ }
+ String typeName = lineParts[1];
+ Class<?> upperBoundType;
+ try {
+ upperBoundType = classLoader.loadClass(typeName);
+ } catch (ClassNotFoundException e) {
+ upperBoundType = null;
+ }
+ String rule = lineParts[0].substring(1);
+ if (rule.equals("whitelistPolicyIfAssignable")) {
+ if (upperBoundType != null) {
+ Set<Class<?>> targetSet =
+ (upperBoundType.getModifiers() &
Modifier.FINAL) != 0
+ ? whitelistRuleFinalClasses
+ : whitelistRuleNonFinalClasses;
+ targetSet.add(upperBoundType);
+ }
+ } else if (rule.equals("blacklistUnlistedMembers")) {
+ if (upperBoundType != null) {
+
typesWithBlacklistUnlistedRule.add(upperBoundType);
+ }
+ } else {
+ throw new IllegalStateException("Unhandled rule: "
+ rule);
+ }
+ } else {
+ MemberSelector memberSelector =
+ MemberSelector.parse(line, classLoader);
+ Class<?> upperBoundType =
memberSelector.getUpperBoundType();
+ if (upperBoundType != null) {
+ if
(!whitelistRuleFinalClasses.contains(upperBoundType)
+ &&
!whitelistRuleNonFinalClasses.contains(upperBoundType)
+ &&
!typesWithBlacklistUnlistedRule.contains(upperBoundType)) {
+ throw new IllegalStateException("Type without
rule: " + upperBoundType.getName());
+ }
+ // We always do the same, as
"blacklistUnlistedMembers" is also defined via a whitelist:
+ whitelistMemberSelectors.add(memberSelector);
+ }
+ }
+ }
+ }
+
+ whitelistMemberAccessPolicy = new
WhitelistMemberAccessPolicy(whitelistMemberSelectors);
- private static final BacklistClassMemberAccessPolicy
CLASS_MEMBER_ACCESS_POLICY_INSTANCE
- = new BacklistClassMemberAccessPolicy();
- private static class BacklistClassMemberAccessPolicy implements
ClassMemberAccessPolicy {
+ // Generate blacklists based on the whitelist and the members of
"blacklistUnlistedMembers" types:
+ List<MemberSelector> blacklistMemberSelectors = new
ArrayList<MemberSelector>();
+ for (Class<?> blacklistUnlistedRuleType :
typesWithBlacklistUnlistedRule) {
+ ClassMemberAccessPolicy classPolicy =
whitelistMemberAccessPolicy.forClass(blacklistUnlistedRuleType);
+ for (Method method : blacklistUnlistedRuleType.getMethods()) {
+ if (!classPolicy.isMethodExposed(method)) {
+ blacklistMemberSelectors.add(new
MemberSelector(blacklistUnlistedRuleType, method));
+ }
+ }
+ for (Constructor<?> constructor :
blacklistUnlistedRuleType.getConstructors()) {
+ if (!classPolicy.isConstructorExposed(constructor)) {
+ blacklistMemberSelectors.add(new
MemberSelector(blacklistUnlistedRuleType, constructor));
+ }
+ }
+ for (Field field : blacklistUnlistedRuleType.getFields()) {
+ if (!classPolicy.isFieldExposed(field)) {
+ blacklistMemberSelectors.add(new
MemberSelector(blacklistUnlistedRuleType, field));
+ }
+ }
+ }
+ blacklistMemberAccessPolicy = new
BlacklistMemberAccessPolicy(blacklistMemberSelectors);
+ } catch (Exception e) {
+ throw new IllegalStateException("Couldn't init " +
this.getClass().getName() + " instance", e);
+ }
+ }
- public boolean isMethodExposed(Method method) {
- return !UNSAFE_METHODS.contains(method);
+ private static List<String> loadMemberSelectorFileLines() throws
IOException {
+ List<String> whitelist = new ArrayList<String>();
+ BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(
+
DefaultMemberAccessPolicy.class.getResourceAsStream("DefaultMemberAccessPolicy-rules"),
+ "UTF-8"));
+ try {
+ String line;
+ while ((line = reader.readLine()) != null){
+ whitelist.add(line);
+ }
+ } finally {
+ reader.close();
}
- public boolean isConstructorExposed(Constructor<?> constructor) {
- return true;
+ return whitelist;
+ }
+
+ public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
+ if (isTypeWithWhitelistRule(contextClass)) {
+ return whitelistMemberAccessPolicy.forClass(contextClass);
+ } else {
+ return blacklistMemberAccessPolicy.forClass(contextClass);
}
+ }
- public boolean isFieldExposed(Field field) {
+ private boolean isTypeWithWhitelistRule(Class<?> contextClass) {
+ if (whitelistRuleFinalClasses.contains(contextClass)) {
return true;
}
+ for (Class<?> nonFinalClass : whitelistRuleNonFinalClasses) {
+ if (nonFinalClass.isAssignableFrom(contextClass)) {
+ return true;
+ }
+ }
+ return false;
}
+
}
diff --git a/src/main/java/freemarker/ext/beans/FieldMatcher.java
b/src/main/java/freemarker/ext/beans/FieldMatcher.java
index f67bf14..179ea26 100644
--- a/src/main/java/freemarker/ext/beans/FieldMatcher.java
+++ b/src/main/java/freemarker/ext/beans/FieldMatcher.java
@@ -31,4 +31,9 @@ final class FieldMatcher extends MemberMatcher<Field, String>
{
protected String toMemberSignature(Field member) {
return member.getName();
}
+
+ @Override
+ protected boolean matchInUpperBoundTypeSubtypes() {
+ return true;
+ }
}
diff --git a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
similarity index 64%
copy from src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
copy to src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
index 8c1186d..13b6c41 100644
--- a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
@@ -22,24 +22,22 @@ package freemarker.ext.beans;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
-import freemarker.template.Version;
-import freemarker.template._TemplateAPI;
import freemarker.template.utility.ClassUtil;
/**
- * Legacy black list based member access policy, used only to keep old
behavior, as it can't provide meaningful safety.
- * Do not use it if you allow untrusted users to edit templates!
+ * Legacy blacklist based member access policy, used only to keep old
behavior, as it can't provide meaningful safety.
+ * Do not use it if you allow untrusted users to edit templates! Use {@link
WhitelistMemberAccessPolicy} then.
*
* @since 2.3.30
*/
-public final class DefaultMemberAccessPolicy implements MemberAccessPolicy {
+public final class LegacyDefaultMemberAccessPolicy implements
MemberAccessPolicy {
+
+ public static final LegacyDefaultMemberAccessPolicy INSTANCE = new
LegacyDefaultMemberAccessPolicy();
private static final String UNSAFE_METHODS_PROPERTIES =
"unsafeMethods.properties";
private static final Set<Method> UNSAFE_METHODS = createUnsafeMethodsSet();
@@ -48,10 +46,9 @@ public final class DefaultMemberAccessPolicy implements
MemberAccessPolicy {
try {
Properties props = ClassUtil.loadProperties(BeansWrapper.class,
UNSAFE_METHODS_PROPERTIES);
Set<Method> set = new HashSet<Method>(props.size() * 4 / 3, 1f);
- Map<String, Class<?>> primClasses = createPrimitiveClassesMap();
for (Object key : props.keySet()) {
try {
- set.add(parseMethodSpec((String) key, primClasses));
+ set.add(parseMethodSpec((String) key));
} catch (ClassNotFoundException e) {
if (ClassIntrospector.DEVELOPMENT_MODE) {
throw e;
@@ -68,7 +65,7 @@ public final class DefaultMemberAccessPolicy implements
MemberAccessPolicy {
}
}
- private static Method parseMethodSpec(String methodSpec, Map<String,
Class<?>> primClasses)
+ private static Method parseMethodSpec(String methodSpec)
throws ClassNotFoundException,
NoSuchMethodException {
int brace = methodSpec.indexOf('(');
@@ -81,7 +78,7 @@ public final class DefaultMemberAccessPolicy implements
MemberAccessPolicy {
Class<?>[] argTypes = new Class[argcount];
for (int i = 0; i < argcount; i++) {
String argClassName = tok.nextToken();
- argTypes[i] = primClasses.get(argClassName);
+ argTypes[i] = ClassUtil.resolveIfPrimitiveTypeName(argClassName);
if (argTypes[i] == null) {
argTypes[i] = ClassUtil.forName(argClassName);
}
@@ -89,41 +86,16 @@ public final class DefaultMemberAccessPolicy implements
MemberAccessPolicy {
return clazz.getMethod(methodName, argTypes);
}
- private static Map<String, Class<?>> createPrimitiveClassesMap() {
- Map<String, Class<?>> map = new HashMap<String, Class<?>>();
- map.put("boolean", Boolean.TYPE);
- map.put("byte", Byte.TYPE);
- map.put("char", Character.TYPE);
- map.put("short", Short.TYPE);
- map.put("int", Integer.TYPE);
- map.put("long", Long.TYPE);
- map.put("float", Float.TYPE);
- map.put("double", Double.TYPE);
- return map;
- }
-
- private static final DefaultMemberAccessPolicy INSTANCE = new
DefaultMemberAccessPolicy();
-
- private DefaultMemberAccessPolicy() {
- }
-
- /**
- * Returns the singleton that's compatible with the given incompatible
improvements version.
- */
- public static DefaultMemberAccessPolicy getInstance(Version
incompatibleImprovements) {
- _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
- // All breakpoints here must occur in
ClassIntrospectorBuilder.normalizeIncompatibleImprovementsVersion!
- // Though currently we don't have any.
- return INSTANCE;
+ private LegacyDefaultMemberAccessPolicy() {
}
public ClassMemberAccessPolicy forClass(Class<?> containingClass) {
return CLASS_MEMBER_ACCESS_POLICY_INSTANCE;
}
- private static final BacklistClassMemberAccessPolicy
CLASS_MEMBER_ACCESS_POLICY_INSTANCE
- = new BacklistClassMemberAccessPolicy();
- private static class BacklistClassMemberAccessPolicy implements
ClassMemberAccessPolicy {
+ private static final BlacklistClassMemberAccessPolicy
CLASS_MEMBER_ACCESS_POLICY_INSTANCE
+ = new BlacklistClassMemberAccessPolicy();
+ private static class BlacklistClassMemberAccessPolicy implements
ClassMemberAccessPolicy {
public boolean isMethodExposed(Method method) {
return !UNSAFE_METHODS.contains(method);
diff --git a/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
index c8de56d..eacb97d 100644
--- a/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
@@ -19,26 +19,31 @@
package freemarker.ext.beans;
+import freemarker.core.Environment;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateModel;
/**
- * Implement this to specify what class members are accessible from templates.
+ * Implement this to restrict what class members (methods, fields,
constructors) are accessible from templates.
+ * Note, however, that {@link BeansWrapper} and its subclasses doesn't
discover all members on the first place, and the
+ * {@link MemberAccessPolicy} just removes from that set of members, never
adds to it. Practically speaking, it's the
+ * last filter in the chain.
*
- * <p>The instance is usually set via {@link
BeansWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)} (or if
- * you use {@link DefaultObjectWrapper}, with
- * {@link
DefaultObjectWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)}).
+ * <p>{@link MemberAccessPolicy}-s meant to be used inside {@link
ObjectWrapper}-s, and their existence is transparent
+ * for the rest of the system. The instance is usually set via
+ * {@link BeansWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)} (or
if you use {@link DefaultObjectWrapper},
+ * with {@link
DefaultObjectWrapperBuilder#setMemberAccessPolicy(MemberAccessPolicy)}).
*
* <p>As {@link BeansWrapper}, and its subclasses like {@link
DefaultObjectWrapper}, only discover public
- * members, it's pointless to whitelist non-public members. An {@link
MemberAccessPolicy} is a filter applied to
- * the set of members that {@link BeansWrapper} intends to expose on the first
place. (Also, while public members
- * declared in non-public classes are discovered by {@link BeansWrapper}, Java
reflection will not allow accessing those
- * normally, so generally it's not useful to whitelist those either.)
+ * members, it's pointless to whitelist non-public members. (Also, while
public members declared in non-public classes
+ * are discovered by {@link BeansWrapper}, Java reflection will not allow
accessing those normally, so generally it's
+ * not useful to whitelist those either.)
*
* <p>Note that if you add {@link TemplateModel}-s directly to the data-model,
those are not wrapped by the
- * {@link ObjectWrapper}, and so the {@link MemberAccessPolicy} won't affect
those.
+ * {@link ObjectWrapper} (from {@link Environment#getObjectWrapper()}), and so
the {@link MemberAccessPolicy} won't
+ * affect those.
*
* <p>Implementations must be thread-safe, and instances generally should be
singletons on JVM level. FreeMarker
* caches its class metadata in a global (static, JVM-scope) cache for shared
use, and the {@link MemberAccessPolicy}
diff --git a/src/main/java/freemarker/ext/beans/MemberMatcher.java
b/src/main/java/freemarker/ext/beans/MemberMatcher.java
index 2b52178..34ccc9c 100644
--- a/src/main/java/freemarker/ext/beans/MemberMatcher.java
+++ b/src/main/java/freemarker/ext/beans/MemberMatcher.java
@@ -45,6 +45,8 @@ abstract class MemberMatcher<M extends Member, S> {
*/
protected abstract S toMemberSignature(M member);
+ protected abstract boolean matchInUpperBoundTypeSubtypes();
+
/**
* Adds a member that this {@link MemberMatcher} will match.
*
@@ -85,7 +87,17 @@ abstract class MemberMatcher<M extends Member, S> {
S memberSignature = toMemberSignature(member);
Types upperBoundTypes =
signaturesToUpperBoundTypes.get(memberSignature);
- return upperBoundTypes != null &&
containsTypeOrSuperType(upperBoundTypes, contextClass);
+ return upperBoundTypes != null
+ && (matchInUpperBoundTypeSubtypes()
+ ? containsTypeOrSuperType(upperBoundTypes,
contextClass)
+ : containsExactType(upperBoundTypes, contextClass));
+ }
+
+ private static boolean containsExactType(Types types, Class<?> c) {
+ if (c == null) {
+ return false;
+ }
+ return types.set.contains(c);
}
private static boolean containsTypeOrSuperType(Types types, Class<?> c) {
diff --git
a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicy.java
similarity index 77%
copy from src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
copy to
src/main/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicy.java
index 5e8945a..93c2788 100644
--- a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
+++
b/src/main/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicy.java
@@ -19,6 +19,7 @@
package freemarker.ext.beans;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -28,55 +29,62 @@ import java.util.List;
import java.util.StringTokenizer;
import freemarker.log.Logger;
-import freemarker.template.ObjectWrapper;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.NullArgumentException;
/**
- * Whitelist-based member access policy, that is, only members that you have
explicitly whitelisted will be accessible.
- * The whitelist content is application specific, and can be significant work
to put together, but it's the only way
- * you can achieve any practical safety if you don't fully trust the users who
can edit templates. Of course, this only
- * can deal with the {@link ObjectWrapper} aspect of safety; please check the
Manual to see what else is needed. Also,
- * since this is related to security, read the documentation of {@link
MemberAccessPolicy}, to know about the
- * pitfalls and edge cases related to {@link MemberAccessPolicy}-es in general.
+ * Superclass for member-selector-list-based member access policies, like
{@link WhitelistMemberAccessPolicy}.
*
- * <p>There are two ways you can add members to the whitelist:
+ * <p>There are two ways you can add members to the member selector list:
* <ul>
* <li>Via a list of member selectors passed to the constructor
- * <li>Via {@link TemplateAccessible} annotation
+ * <li>Via annotation (concrete type depends on subclass)
* </ul>
*
- * <p>When a member is whitelisted, it's identified by the following data
(with the example of
- * {@code com.example.MyClass.myMethod(int, int)} being whitelisted):
+ * <p>Members are identified with the following data (with the example of
+ * {@code com.example.MyClass.myMethod(int, int)}):
* <ul>
* <li>Upper bound class ({@code com.example.MyClass} in the example)
* <li>Member name ({@code myMethod} in the example), except for
constructors where it's unused
* <li>Parameter types ({@code int, int} in the example), except for fields
where it's unused
* </ul>
*
- * <p>Once you have whitelisted a member in the upper bound class, it will be
automatically whitelisted in all
- * subclasses of that, even if the whitelisted member is a field or
constructor (which doesn't support overriding, but
- * it will be treated as such if the field name or constructor parameter types
match).
- * It's called "upper bound" class, because the member will only be
whitelisted in classes that are {@code instanceof}
- * the upper bound class. That restriction stands even if the member was
inherited from another class or
- * interface, and it wasn't even overridden in the upper bound class; the
member won't be whitelisted in the
- * class/interface where it was inherited from, if that type is more generic
than the upper bound class.
+ * <p>If a method or field is matched in the upper bound type, it will be
automatically matched in all subtypes of that.
+ * It's called "upper bound" type, because the member will only be matched in
classes that are {@code instanceof}
+ * the upper bound class. That restriction stands even if the member was
inherited from another type (class or
+ * interface), and it wasn't even overridden in the upper bound type; the
member won't be matched in the
+ * type where it was inherited from, if that type is more generic than the
upper bound type.
*
- * <p>Note that the return type of methods aren't used in any way. So if you
whitelist {@code myMethod(int, int)}, and
- * it has multiple variants with different return types (which is possible on
the bytecode level), then you have
- * whitelisted all variants of it.
+ * <p>The above inheritance rule doesn't apply to constructors. That's
consistent with the fact constructors aren't
+ * inherited in Java (or pretty much any other language). So for example, if
you add {@code com.example.A.A()} to
+ * the member selector list, and {@code B extends A}, then {@code
com.example.B.B()} is still not matched by that list.
+ * If you want it to be matched, you have to add {@code com.example.B.B()} to
list explicitly.
+ *
+ * <p>Note that the return type of methods aren't used in any way. If {@code
myMethod(int, int)} has multiple variants
+ * with different return types (which is possible on the bytecode level) but
the same parameter types, then all
+ * variants of it is matched, or none is. Similarly, the type of fields isn't
used either, only the name of the field
+ * matters.
*
* @since 2.3.30
*/
-public class WhitelistMemberAccessPolicy implements MemberAccessPolicy {
+public abstract class MemberSelectorListMemberAccessPolicy implements
MemberAccessPolicy {
private static final Logger LOG = Logger.getLogger("freemarker.beans");
+ enum ListType {
+ /** Only matched members will be exposed. */
+ WHITELIST,
+ /** Matched members will not be exposed. */
+ BLACKLIST
+ }
+
+ private final ListType listType;
private final MethodMatcher methodMatcher;
private final ConstructorMatcher constructorMatcher;
private final FieldMatcher fieldMatcher;
+ private final Class<? extends Annotation> matchAnnotation;
/**
- * A condition that matches some type members. See {@link
WhitelistMemberAccessPolicy} documentation for more.
+ * A condition that matches some type members. See {@link
MemberSelectorListMemberAccessPolicy} documentation for more.
* Exactly one of these will be non-{@code null}:
* {@link #getMethod()}, {@link #getConstructor()}, {@link #getField()},
{@link #getException()}.
*
@@ -262,7 +270,7 @@ public class WhitelistMemberAccessPolicy implements
MemberAccessPolicy {
if (hasArgList) {
if (cleanedStr.charAt(cleanedStr.length() - 1) != ')') {
- throw new IllegalArgumentException("Malformed whitelist
entry (missing closing ')'): "
+ throw new IllegalArgumentException("Malformed whitelist
entry (should end with ')'): "
+ memberSelectorString);
}
String argsSpec = cleanedStr.substring(postMemberNameIdx + 1,
cleanedStr.length() - 1);
@@ -316,19 +324,46 @@ public class WhitelistMemberAccessPolicy implements
MemberAccessPolicy {
}
/**
- * Convenience method to parse all member selectors in the collection;
see {@link #parse(String, ClassLoader)}.
+ * Convenience method to parse all member selectors in the collection
(see {@link #parse(String, ClassLoader)}),
+ * while also filtering out blank and comment lines; see {@link
#parse(String, ClassLoader)},
+ * and {@link #isIgnoredLine(String)}.
*/
public static List<MemberSelector> parse(Collection<String>
memberSelectors,
ClassLoader classLoader) {
List<MemberSelector> parsedMemberSelectors = new
ArrayList<MemberSelector>(memberSelectors.size());
for (String memberSelector : memberSelectors) {
- parsedMemberSelectors.add(parse(memberSelector, classLoader));
+ if (!isIgnoredLine(memberSelector)) {
+ parsedMemberSelectors.add(parse(memberSelector,
classLoader));
+ }
}
return parsedMemberSelectors;
}
+
+ /**
+ * A line is ignored if it's blank or a comment. A line is be blank if
it doesn't contain non-whitespace
+ * character. A line is a comment if it starts with {@code #}, or
{@code //} (ignoring any amount of
+ * preceding whitespace).
+ */
+ public static boolean isIgnoredLine(String line) {
+ String trimmedLine = line.trim();
+ return trimmedLine.length() == 0 || trimmedLine.startsWith("#") ||
trimmedLine.startsWith("//");
+ }
}
- public WhitelistMemberAccessPolicy(Collection<MemberSelector>
memberSelectors) {
+ /**
+ * @param memberSelectors
+ * List of member selectors; see {@link
MemberSelectorListMemberAccessPolicy} class-level documentation for
+ * more.
+ * @param listType
+ * Decides the "color" of the list
+ * @param matchAnnotation
+ */
+ MemberSelectorListMemberAccessPolicy(
+ Collection<MemberSelector> memberSelectors, ListType listType,
+ Class<? extends Annotation> matchAnnotation) {
+ this.listType = listType;
+ this.matchAnnotation = matchAnnotation;
+
methodMatcher = new MethodMatcher();
constructorMatcher = new ConstructorMatcher();
fieldMatcher = new FieldMatcher();
@@ -351,26 +386,44 @@ public class WhitelistMemberAccessPolicy implements
MemberAccessPolicy {
}
}
- public ClassMemberAccessPolicy forClass(final Class<?> contextClass) {
+ public final ClassMemberAccessPolicy forClass(final Class<?> contextClass)
{
return new ClassMemberAccessPolicy() {
public boolean isMethodExposed(Method method) {
- return methodMatcher.matches(contextClass, method)
- || _MethodUtil.getInheritableAnnotation(contextClass,
method, TemplateAccessible.class) != null;
+ return matchResultToIsExposedResult(
+ methodMatcher.matches(contextClass, method)
+ || matchAnnotation != null
+ &&
_MethodUtil.getInheritableAnnotation(contextClass, method, matchAnnotation)
+ != null);
}
public boolean isConstructorExposed(Constructor<?> constructor) {
- return constructorMatcher.matches(contextClass, constructor)
- || _MethodUtil.getInheritableAnnotation(contextClass,
constructor, TemplateAccessible.class)
- != null;
+ return matchResultToIsExposedResult(
+ constructorMatcher.matches(contextClass, constructor)
+ || matchAnnotation != null
+ &&
_MethodUtil.getInheritableAnnotation(contextClass, constructor, matchAnnotation)
+ != null);
}
public boolean isFieldExposed(Field field) {
- return fieldMatcher.matches(contextClass, field)
- || _MethodUtil.getInheritableAnnotation(contextClass,
field, TemplateAccessible.class) != null;
+ return matchResultToIsExposedResult(
+ fieldMatcher.matches(contextClass, field)
+ || matchAnnotation != null
+ &&
_MethodUtil.getInheritableAnnotation(contextClass, field, matchAnnotation)
+ != null);
}
};
}
+ private boolean matchResultToIsExposedResult(boolean matches) {
+ if (listType == ListType.WHITELIST) {
+ return matches;
+ }
+ if (listType == ListType.BLACKLIST) {
+ return !matches;
+ }
+ throw new AssertionError();
+ }
+
private static boolean isWellFormedClassName(String s) {
if (s.length() == 0) {
return false;
diff --git a/src/main/java/freemarker/ext/beans/MethodMatcher.java
b/src/main/java/freemarker/ext/beans/MethodMatcher.java
index 7df56be..b7a5b07 100644
--- a/src/main/java/freemarker/ext/beans/MethodMatcher.java
+++ b/src/main/java/freemarker/ext/beans/MethodMatcher.java
@@ -35,4 +35,9 @@ final class MethodMatcher extends MemberMatcher<Method,
ExecutableMemberSignatur
protected ExecutableMemberSignature toMemberSignature(Method member) {
return new ExecutableMemberSignature(member);
}
+
+ @Override
+ protected boolean matchInUpperBoundTypeSubtypes() {
+ return true;
+ }
}
diff --git
a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
b/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
index 5e8945a..9c33884 100644
--- a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
@@ -19,393 +19,36 @@
package freemarker.ext.beans;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
-import java.util.StringTokenizer;
-import freemarker.log.Logger;
import freemarker.template.ObjectWrapper;
-import freemarker.template.utility.ClassUtil;
-import freemarker.template.utility.NullArgumentException;
/**
- * Whitelist-based member access policy, that is, only members that you have
explicitly whitelisted will be accessible.
- * The whitelist content is application specific, and can be significant work
to put together, but it's the only way
- * you can achieve any practical safety if you don't fully trust the users who
can edit templates. Of course, this only
- * can deal with the {@link ObjectWrapper} aspect of safety; please check the
Manual to see what else is needed. Also,
- * since this is related to security, read the documentation of {@link
MemberAccessPolicy}, to know about the
- * pitfalls and edge cases related to {@link MemberAccessPolicy}-es in general.
+ * Whitelist-based member access policy, that is, only members that are
matched by the listing will be exposed.
+ * Note that {@link BeansWrapper} and its subclasses doesn't discover all
members on the first place, and the
+ * {@link MemberAccessPolicy} just removes from that set of members, never
adds to it.
*
- * <p>There are two ways you can add members to the whitelist:
- * <ul>
- * <li>Via a list of member selectors passed to the constructor
- * <li>Via {@link TemplateAccessible} annotation
- * </ul>
+ * <p>The whitelist content is usually application specific, and can be
significant work to put together, but it's the
+ * only way you can achieve any practical safety when you don't fully trust
the users who can edit templates.
*
- * <p>When a member is whitelisted, it's identified by the following data
(with the example of
- * {@code com.example.MyClass.myMethod(int, int)} being whitelisted):
- * <ul>
- * <li>Upper bound class ({@code com.example.MyClass} in the example)
- * <li>Member name ({@code myMethod} in the example), except for
constructors where it's unused
- * <li>Parameter types ({@code int, int} in the example), except for fields
where it's unused
- * </ul>
+ * <p>See more about the rules at {@link MemberSelectorListMemberAccessPolicy}.
+ * {@link TemplateAccessible} annotation may be used to add members to the
whitelist.
*
- * <p>Once you have whitelisted a member in the upper bound class, it will be
automatically whitelisted in all
- * subclasses of that, even if the whitelisted member is a field or
constructor (which doesn't support overriding, but
- * it will be treated as such if the field name or constructor parameter types
match).
- * It's called "upper bound" class, because the member will only be
whitelisted in classes that are {@code instanceof}
- * the upper bound class. That restriction stands even if the member was
inherited from another class or
- * interface, and it wasn't even overridden in the upper bound class; the
member won't be whitelisted in the
- * class/interface where it was inherited from, if that type is more generic
than the upper bound class.
- *
- * <p>Note that the return type of methods aren't used in any way. So if you
whitelist {@code myMethod(int, int)}, and
- * it has multiple variants with different return types (which is possible on
the bytecode level), then you have
- * whitelisted all variants of it.
+ * <p>Of course, this only can deal with the {@link ObjectWrapper} aspect of
safety; please check the Manual to see what
+ * else is needed. Also, since this is related to security, read the
documentation of {@link MemberAccessPolicy}, to
+ * know about the pitfalls and edge cases related to {@link
MemberAccessPolicy}-es in general.
*
* @since 2.3.30
*/
-public class WhitelistMemberAccessPolicy implements MemberAccessPolicy {
- private static final Logger LOG = Logger.getLogger("freemarker.beans");
-
- private final MethodMatcher methodMatcher;
- private final ConstructorMatcher constructorMatcher;
- private final FieldMatcher fieldMatcher;
+public class WhitelistMemberAccessPolicy extends
MemberSelectorListMemberAccessPolicy {
/**
- * A condition that matches some type members. See {@link
WhitelistMemberAccessPolicy} documentation for more.
- * Exactly one of these will be non-{@code null}:
- * {@link #getMethod()}, {@link #getConstructor()}, {@link #getField()},
{@link #getException()}.
- *
- * @since 2.3.30
+ * @param memberSelectors
+ * List of member selectors; see {@link
MemberSelectorListMemberAccessPolicy} class-level documentation for
+ * more.
*/
- public final static class MemberSelector {
- private final Class<?> upperBoundType;
- private final Method method;
- private final Constructor<?> constructor;
- private final Field field;
- private final Exception exception;
- private final String exceptionMemberSelectorString;
-
- /**
- * Use if you want to match methods similar to the specified one, in
types that are {@code instanceof} of
- * the specified upper bound type. When methods are matched, only the
name and the parameter types matter.
- */
- public MemberSelector(Class<?> upperBoundType, Method method) {
- NullArgumentException.check("upperBoundType", upperBoundType);
- NullArgumentException.check("method", method);
- this.upperBoundType = upperBoundType;
- this.method = method;
- this.constructor = null;
- this.field = null;
- this.exception = null;
- this.exceptionMemberSelectorString = null;
- }
-
- /**
- * Use if you want to match constructors similar to the specified one,
in types that are {@code instanceof} of
- * the specified upper bound type. When constructors are matched, only
the parameter types matter.
- */
- public MemberSelector(Class<?> upperBoundType, Constructor<?>
constructor) {
- NullArgumentException.check("upperBoundType", upperBoundType);
- NullArgumentException.check("constructor", constructor);
- this.upperBoundType = upperBoundType;
- this.method = null;
- this.constructor = constructor;
- this.field = null;
- this.exception = null;
- this.exceptionMemberSelectorString = null;
- }
-
- /**
- * Use if you want to match fields similar to the specified one, in
types that are {@code instanceof} of
- * the specified upper bound type. When fields are matched, only the
name matters.
- */
- public MemberSelector(Class<?> upperBoundType, Field field) {
- NullArgumentException.check("upperBoundType", upperBoundType);
- NullArgumentException.check("field", field);
- this.upperBoundType = upperBoundType;
- this.method = null;
- this.constructor = null;
- this.field = field;
- this.exception = null;
- this.exceptionMemberSelectorString = null;
- }
-
- /**
- * Used to store the result of a parsing that's failed for a reason
that we can skip on runtime (typically,
- * when a missing class or member was referred).
- *
- * @param upperBoundType {@code null} if resolving the upper bound
type itself failed.
- * @param exception Not {@code null}
- * @param exceptionMemberSelectorString Not {@code null}; the selector
whose resolution has failed, used in
- * the log message.
- */
- public MemberSelector(Class<?> upperBoundType, Exception exception,
String exceptionMemberSelectorString) {
- NullArgumentException.check("exception", exception);
- NullArgumentException.check("exceptionMemberSelectorString",
exceptionMemberSelectorString);
- this.upperBoundType = upperBoundType;
- this.method = null;
- this.constructor = null;
- this.field = null;
- this.exception = exception;
- this.exceptionMemberSelectorString = exceptionMemberSelectorString;
- }
-
- /**
- * Maybe {@code null} if {@link #getException()} is non-{@code null}.
- */
- public Class<?> getUpperBoundType() {
- return upperBoundType;
- }
-
- /**
- * Maybe {@code null};
- * set if the selector matches methods similar to the returned one,
and there was no exception.
- */
- public Method getMethod() {
- return method;
- }
-
- /**
- * Maybe {@code null};
- * set if the selector matches constructors similar to the returned
one, and there was no exception.
- */
- public Constructor<?> getConstructor() {
- return constructor;
- }
-
- /**
- * Maybe {@code null};
- * set if the selector matches fields similar to the returned one, and
there was no exception.
- */
- public Field getField() {
- return field;
- }
-
- /**
- * Maybe {@code null}
- */
- public Exception getException() {
- return exception;
- }
-
- /**
- * Maybe {@code null}
- */
- public String getExceptionMemberSelectorString() {
- return exceptionMemberSelectorString;
- }
-
- /**
- * Parses a member selector that was specified with a string.
- *
- * @param classLoader
- * Used to resolve class names in the member selectors. Generally
you want to pick a class that belongs to
- * you application (not to a 3rd party library, like FreeMarker),
and then call
- * {@link Class#getClassLoader()} on that. Note that the
resolution of the classes is not lazy, and so the
- * {@link ClassLoader} won't be stored after this method returns.
- * @param memberSelectorString
- * Describes the member (method, constructor, field) which you
want to whitelist. Starts with the full
- * qualified name of the member, like {@code
com.example.MyClass.myMember}. Unless it's a field, the
- * name is followed by comma separated list of the parameter
types inside parentheses, like in
- * {@code com.example.MyClass.myMember(java.lang.String,
boolean)}. The parameter type names must be
- * also full qualified names, except primitive type names. Array
types must be indicated with one or
- * more {@code []}-s after the type name. Varargs arguments
shouldn't be marked with {@code ...}, but with
- * {@code []}. In the member name, like {@code
com.example.MyClass.myMember}, the class refers to the so
- * called "upper bound class". Regarding that and inheritance
rules see the class level documentation.
- *
- * @return The {@link MemberSelector}, which might has non-{@code
null} {@link MemberSelector#exception}.
- */
- public static MemberSelector parse(String memberSelectorString,
ClassLoader classLoader) {
- if (memberSelectorString.contains("<") ||
memberSelectorString.contains(">")
- || memberSelectorString.contains("...") ||
memberSelectorString.contains(";")) {
- throw new IllegalArgumentException(
- "Malformed whitelist entry (shouldn't contain \"<\",
\">\", \"...\", or \";\"): "
- + memberSelectorString);
- }
- String cleanedStr =
memberSelectorString.trim().replaceAll("\\s*([\\.,\\(\\)\\[\\]])\\s*", "$1");
-
- int postMemberNameIdx;
- boolean hasArgList;
- {
- int openParenIdx = cleanedStr.indexOf('(');
- hasArgList = openParenIdx != -1;
- postMemberNameIdx = hasArgList ? openParenIdx :
cleanedStr.length();
- }
-
- final int postClassDotIdx = cleanedStr.lastIndexOf('.',
postMemberNameIdx);
- if (postClassDotIdx == -1) {
- throw new IllegalArgumentException("Malformed whitelist entry
(missing dot): " + memberSelectorString);
- }
-
- Class<?> upperBoundClass;
- String upperBoundClassStr = cleanedStr.substring(0,
postClassDotIdx);
- if (!isWellFormedClassName(upperBoundClassStr)) {
- throw new IllegalArgumentException("Malformed whitelist entry
(malformed upper bound class name): "
- + memberSelectorString);
- }
- try {
- upperBoundClass = classLoader.loadClass(upperBoundClassStr);
- } catch (ClassNotFoundException e) {
- return new MemberSelector(null, e, cleanedStr);
- }
-
- String memberName = cleanedStr.substring(postClassDotIdx + 1,
postMemberNameIdx);
- if (!isWellFormedJavaIdentifier(memberName)) {
- throw new IllegalArgumentException(
- "Malformed whitelist entry (malformed member name): "
+ memberSelectorString);
- }
-
- if (hasArgList) {
- if (cleanedStr.charAt(cleanedStr.length() - 1) != ')') {
- throw new IllegalArgumentException("Malformed whitelist
entry (missing closing ')'): "
- + memberSelectorString);
- }
- String argsSpec = cleanedStr.substring(postMemberNameIdx + 1,
cleanedStr.length() - 1);
- StringTokenizer tok = new StringTokenizer(argsSpec, ",");
- int argCount = tok.countTokens();
- Class<?>[] argTypes = new Class[argCount];
- for (int i = 0; i < argCount; i++) {
- String argClassName = tok.nextToken();
- int arrayDimensions = 0;
- while (argClassName.endsWith("[]")) {
- arrayDimensions++;
- argClassName = argClassName.substring(0,
argClassName.length() - 2);
- }
- Class<?> argClass;
- Class<?> primArgClass =
ClassUtil.resolveIfPrimitiveTypeName(argClassName);
- if (primArgClass != null) {
- argClass = primArgClass;
- } else {
- if (!isWellFormedClassName(argClassName)) {
- throw new IllegalArgumentException(
- "Malformed whitelist entry (malformed
argument class name): " + memberSelectorString);
- }
- try {
- argClass = classLoader.loadClass(argClassName);
- } catch (ClassNotFoundException e) {
- return new MemberSelector(upperBoundClass, e,
cleanedStr);
- } catch (SecurityException e) {
- return new MemberSelector(upperBoundClass, e,
cleanedStr);
- }
- }
- argTypes[i] = ClassUtil.getArrayClass(argClass,
arrayDimensions);
- }
- try {
- return memberName.equals(upperBoundClass.getSimpleName())
- ? new MemberSelector(upperBoundClass,
upperBoundClass.getConstructor(argTypes))
- : new MemberSelector(upperBoundClass,
upperBoundClass.getMethod(memberName, argTypes));
- } catch (NoSuchMethodException e) {
- return new MemberSelector(upperBoundClass, e, cleanedStr);
- } catch (SecurityException e) {
- return new MemberSelector(upperBoundClass, e, cleanedStr);
- }
- } else {
- try {
- return new MemberSelector(upperBoundClass,
upperBoundClass.getField(memberName));
- } catch (NoSuchFieldException e) {
- return new MemberSelector(upperBoundClass, e, cleanedStr);
- } catch (SecurityException e) {
- return new MemberSelector(upperBoundClass, e, cleanedStr);
- }
- }
- }
-
- /**
- * Convenience method to parse all member selectors in the collection;
see {@link #parse(String, ClassLoader)}.
- */
- public static List<MemberSelector> parse(Collection<String>
memberSelectors,
- ClassLoader classLoader) {
- List<MemberSelector> parsedMemberSelectors = new
ArrayList<MemberSelector>(memberSelectors.size());
- for (String memberSelector : memberSelectors) {
- parsedMemberSelectors.add(parse(memberSelector, classLoader));
- }
- return parsedMemberSelectors;
- }
- }
-
public WhitelistMemberAccessPolicy(Collection<MemberSelector>
memberSelectors) {
- methodMatcher = new MethodMatcher();
- constructorMatcher = new ConstructorMatcher();
- fieldMatcher = new FieldMatcher();
- for (MemberSelector memberSelector : memberSelectors) {
- Class<?> upperBoundClass = memberSelector.upperBoundType;
- if (memberSelector.exception != null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Member selector ignored due to error: " +
memberSelector.getExceptionMemberSelectorString(),
- memberSelector.exception);
- }
- } else if (memberSelector.constructor != null) {
- constructorMatcher.addMatching(upperBoundClass,
memberSelector.constructor);
- } else if (memberSelector.method != null) {
- methodMatcher.addMatching(upperBoundClass,
memberSelector.method);
- } else if (memberSelector.field != null) {
- fieldMatcher.addMatching(upperBoundClass,
memberSelector.field);
- } else {
- throw new AssertionError();
- }
- }
- }
-
- public ClassMemberAccessPolicy forClass(final Class<?> contextClass) {
- return new ClassMemberAccessPolicy() {
- public boolean isMethodExposed(Method method) {
- return methodMatcher.matches(contextClass, method)
- || _MethodUtil.getInheritableAnnotation(contextClass,
method, TemplateAccessible.class) != null;
- }
-
- public boolean isConstructorExposed(Constructor<?> constructor) {
- return constructorMatcher.matches(contextClass, constructor)
- || _MethodUtil.getInheritableAnnotation(contextClass,
constructor, TemplateAccessible.class)
- != null;
- }
-
- public boolean isFieldExposed(Field field) {
- return fieldMatcher.matches(contextClass, field)
- || _MethodUtil.getInheritableAnnotation(contextClass,
field, TemplateAccessible.class) != null;
- }
- };
- }
-
- private static boolean isWellFormedClassName(String s) {
- if (s.length() == 0) {
- return false;
- }
- int identifierStartIdx = 0;
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (i == identifierStartIdx) {
- if (!Character.isJavaIdentifierStart(c)) {
- return false;
- }
- } else if (c == '.' && i != s.length() - 1) {
- identifierStartIdx = i + 1;
- } else {
- if (!Character.isJavaIdentifierPart(c)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private static boolean isWellFormedJavaIdentifier(String s) {
- if (s.length() == 0) {
- return false;
- }
- if (!Character.isJavaIdentifierStart(s.charAt(0))) {
- return false;
- }
- for (int i = 1; i < s.length(); i++) {
- if (!Character.isJavaIdentifierPart(s.charAt(i))) {
- return false;
- }
- }
- return true;
+ super(memberSelectors, ListType.WHITELIST, TemplateAccessible.class);
}
}
diff --git a/src/main/java/freemarker/template/DefaultObjectWrapper.java
b/src/main/java/freemarker/template/DefaultObjectWrapper.java
index 8333a00..eac0198 100644
--- a/src/main/java/freemarker/template/DefaultObjectWrapper.java
+++ b/src/main/java/freemarker/template/DefaultObjectWrapper.java
@@ -34,6 +34,8 @@ import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.BeansWrapperConfiguration;
import freemarker.ext.beans.DefaultMemberAccessPolicy;
import freemarker.ext.beans.EnumerationModel;
+import freemarker.ext.beans.LegacyDefaultMemberAccessPolicy;
+import freemarker.ext.beans.MemberAccessPolicy;
import freemarker.ext.dom.NodeModel;
import freemarker.log.Logger;
@@ -254,7 +256,8 @@ public class DefaultObjectWrapper extends
freemarker.ext.beans.BeansWrapper {
* or for a W3C DOM node. In its default implementation, W3C {@link
Node}-s will be wrapped as {@link NodeModel}-s
* (allows DOM tree traversal), Jython objects will be delegated to the
{@code JythonWrapper}, others will be
* wrapped using {@link BeansWrapper#wrap(Object)}. Note that if {@link
#getMemberAccessPolicy()} doesn't return
- * a {@link DefaultMemberAccessPolicy}, then Jython wrapper will be
skipped for security reasons.
+ * a {@link DefaultMemberAccessPolicy} or {@link
LegacyDefaultMemberAccessPolicy}, then Jython wrapper will be
+ * skipped for security reasons.
*
* <p>
* When you override this method, you should first decide if you want to
wrap the object in a custom way (and if so
@@ -265,7 +268,9 @@ public class DefaultObjectWrapper extends
freemarker.ext.beans.BeansWrapper {
if (obj instanceof Node) {
return wrapDomNode(obj);
}
- if (getMemberAccessPolicy() instanceof DefaultMemberAccessPolicy) {
+ MemberAccessPolicy memberAccessPolicy = getMemberAccessPolicy();
+ if (memberAccessPolicy instanceof DefaultMemberAccessPolicy
+ || memberAccessPolicy instanceof
LegacyDefaultMemberAccessPolicy) {
if (JYTHON_WRAPPER != null && JYTHON_OBJ_CLASS.isInstance(obj)) {
return JYTHON_WRAPPER.wrap(obj);
}
diff --git
a/src/main/resources/freemarker/ext/beans/DefaultMemberAccessPolicy-rules
b/src/main/resources/freemarker/ext/beans/DefaultMemberAccessPolicy-rules
new file mode 100644
index 0000000..48001c2
--- /dev/null
+++ b/src/main/resources/freemarker/ext/beans/DefaultMemberAccessPolicy-rules
@@ -0,0 +1,582 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Used by DefaultMemberAccessPolicy (not by LegacyDefaultMemberAccessPolicy).
+# It does NOT provide enough safety if template authors aren't as trusted as
the developers; you need to use a custom
+# whitelist then (see WhitelistMemberAccessPolicy).
+
+# Each member entry must have a upper bound type that already has a rule
defined. The rules are associated with the
+# upper bound type in the lines starting with @. The possible rules are:
+# - whitelistPolicyIfAssignable: Members of the type and of its subtypes can
only access members that were whitelisted.
+# Thus, if you extend a such type, and add a new method, it won't be
exposed, as it wasn't whitelisted.
+# - blacklistUnlistedMembers: Members of the type that are not listed will be
blacklisted. Once a member was blacklisted,
+# it will be blacklisted in subtypes as well. If you extend a type that has
tris rule, and add a new method, it will
+# be exposed, as it wasn't blacklisted.
+
+@blacklistUnlistedMembers java.lang.Object
+# Disallowed since 2.3.0: java.lang.Object.wait(long)
+# Disallowed since 2.3.0: java.lang.Object.wait(long,int)
+# Disallowed since 2.3.0: java.lang.Object.wait()
+java.lang.Object.equals(java.lang.Object)
+java.lang.Object.toString()
+java.lang.Object.hashCode()
+java.lang.Object.getClass()
+# Disallowed since 2.3.0: java.lang.Object.notify()
+# Disallowed since 2.3.0: java.lang.Object.notifyAll()
+
+@blacklistUnlistedMembers java.lang.Thread
+java.lang.Thread.getName()
+# Disallowed since 2.3.0, since 2.3.30 even when overridden:
java.lang.Thread.run()
+java.lang.Thread.isInterrupted()
+# Disallowed since 2.3.30: java.lang.Thread.currentThread()
+# Disallowed since 2.3.30: java.lang.Thread.onSpinWait()
+# Disallowed since 2.3.0: java.lang.Thread.join(long,int)
+# Disallowed since 2.3.0: java.lang.Thread.join(long)
+# Disallowed since 2.3.0: java.lang.Thread.join()
+java.lang.Thread.getThreadGroup()
+# Disallowed since 2.3.0:
java.lang.Thread.setContextClassLoader(java.lang.ClassLoader)
+java.lang.Thread.holdsLock(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.Thread.getStackTrace()
+java.lang.Thread.checkAccess()
+# Disallowed since 2.3.30: java.lang.Thread.dumpStack()
+# Disallowed since 2.3.0: java.lang.Thread.setPriority(int)
+# Disallowed since 2.3.0: java.lang.Thread.setDaemon(boolean)
+# Disallowed since 2.3.0: java.lang.Thread.start()
+# Disallowed since 2.3.0: java.lang.Thread.sleep(long)
+# Disallowed since 2.3.0: java.lang.Thread.sleep(long,int)
+java.lang.Thread.isDaemon()
+java.lang.Thread.getPriority()
+# Disallowed since 2.3.0: java.lang.Thread.getContextClassLoader()
+# Disallowed since 2.3.0: java.lang.Thread.resume()
+# Disallowed since 2.3.0: java.lang.Thread.interrupt()
+java.lang.Thread.activeCount()
+# Disallowed since 2.3.30: java.lang.Thread.enumerate(java.lang.Thread[])
+java.lang.Thread.isAlive()
+# Disallowed since 2.3.30:
java.lang.Thread.setDefaultUncaughtExceptionHandler(java.lang.Thread$UncaughtExceptionHandler)
+# Disallowed since 2.3.30: java.lang.Thread.getUncaughtExceptionHandler()
+# Disallowed since 2.3.30: java.lang.Thread.yield()
+# Disallowed since 2.3.0: java.lang.Thread.stop()
+java.lang.Thread.interrupted()
+# Disallowed since 2.3.0: java.lang.Thread.suspend()
+# Disallowed since 2.3.0: java.lang.Thread.setName(java.lang.String)
+java.lang.Thread.countStackFrames()
+# Disallowed since 2.3.30: java.lang.Thread.getAllStackTraces()
+java.lang.Thread.getId()
+java.lang.Thread.getState()
+# Disallowed since 2.3.30:
java.lang.Thread.getDefaultUncaughtExceptionHandler()
+# Disallowed since 2.3.30:
java.lang.Thread.setUncaughtExceptionHandler(java.lang.Thread$UncaughtExceptionHandler)
+
+@whitelistPolicyIfAssignable java.lang.ThreadGroup
+java.lang.ThreadGroup.getName()
+# Disallowed since 2.3.30: java.lang.ThreadGroup.list()
+java.lang.ThreadGroup.getParent()
+java.lang.ThreadGroup.checkAccess()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.setDaemon(boolean)
+java.lang.ThreadGroup.isDaemon()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.resume()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.interrupt()
+java.lang.ThreadGroup.getMaxPriority()
+java.lang.ThreadGroup.activeCount()
+# Disallowed since 2.3.30:
java.lang.ThreadGroup.enumerate(java.lang.ThreadGroup[],boolean)
+# Disallowed since 2.3.30:
java.lang.ThreadGroup.enumerate(java.lang.ThreadGroup[])
+# Disallowed since 2.3.30: java.lang.ThreadGroup.enumerate(java.lang.Thread[])
+# Disallowed since 2.3.30:
java.lang.ThreadGroup.enumerate(java.lang.Thread[],boolean)
+# Disallowed since 2.3.30:
java.lang.ThreadGroup.uncaughtException(java.lang.Thread,java.lang.Throwable)
+# Disallowed since 2.3.0: java.lang.ThreadGroup.stop()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.suspend()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.setMaxPriority(int)
+java.lang.ThreadGroup.activeGroupCount()
+# Disallowed since 2.3.0: java.lang.ThreadGroup.destroy()
+java.lang.ThreadGroup.isDestroyed()
+java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
+# Disallowed since 2.3.0: java.lang.ThreadGroup.allowThreadSuspension(boolean)
+
+@whitelistPolicyIfAssignable java.lang.Runtime
+# Disallowed since 2.3.30: java.lang.Runtime.getRuntime()
+# Disallowed since 2.3.0: java.lang.Runtime.exit(int)
+# Disallowed since 2.3.30: java.lang.Runtime.runFinalization()
+java.lang.Runtime.version()
+# Disallowed since 2.3.0: java.lang.Runtime.loadLibrary(java.lang.String)
+# Disallowed since 2.3.30: java.lang.Runtime.gc()
+# Disallowed since 2.3.0: java.lang.Runtime.load(java.lang.String)
+java.lang.Runtime.freeMemory()
+java.lang.Runtime.maxMemory()
+java.lang.Runtime.availableProcessors()
+# Disallowed since 2.3.0: java.lang.Runtime.halt(int)
+# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String[])
+# Disallowed since 2.3.0:
java.lang.Runtime.exec(java.lang.String,java.lang.String[],java.io.File)
+# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String)
+# Disallowed since 2.3.0:
java.lang.Runtime.exec(java.lang.String[],java.lang.String[])
+# Disallowed since 2.3.0:
java.lang.Runtime.exec(java.lang.String[],java.lang.String[],java.io.File)
+# Disallowed since 2.3.0:
java.lang.Runtime.exec(java.lang.String,java.lang.String[])
+# Disallowed since 2.3.0: java.lang.Runtime.addShutdownHook(java.lang.Thread)
+# Disallowed since 2.3.0:
java.lang.Runtime.removeShutdownHook(java.lang.Thread)
+java.lang.Runtime.totalMemory()
+# Disallowed since 2.3.0: java.lang.Runtime.traceInstructions(boolean)
+# Disallowed since 2.3.0: java.lang.Runtime.traceMethodCalls(boolean)
+
+@whitelistPolicyIfAssignable java.lang.System
+# Disallowed since 2.3.0: java.lang.System.exit(int)
+# Disallowed since 2.3.0: java.lang.System.runFinalization()
+# Disallowed since 2.3.0: java.lang.System.runFinalizersOnExit(boolean)
+java.lang.System.getProperty(java.lang.String)
+java.lang.System.getProperty(java.lang.String,java.lang.String)
+java.lang.System.identityHashCode(java.lang.Object)
+java.lang.System.currentTimeMillis()
+java.lang.System.nanoTime()
+# Disallowed since 2.3.30:
java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+# Disallowed since 2.3.30: java.lang.System.getSecurityManager()
+java.lang.System.mapLibraryName(java.lang.String)
+# Disallowed since 2.3.0: java.lang.System.loadLibrary(java.lang.String)
+# Disallowed since 2.3.30: java.lang.System.console()
+# Disallowed since 2.3.30: java.lang.System.inheritedChannel()
+# Disallowed since 2.3.0:
java.lang.System.setSecurityManager(java.lang.SecurityManager)
+java.lang.System.lineSeparator()
+# Disallowed since 2.3.0:
java.lang.System.setProperty(java.lang.String,java.lang.String)
+java.lang.System.getenv(java.lang.String)
+java.lang.System.getenv()
+# Disallowed since 2.3.30:
java.lang.System.getLogger(java.lang.String,java.util.ResourceBundle)
+# Disallowed since 2.3.30: java.lang.System.getLogger(java.lang.String)
+# Disallowed since 2.3.30: java.lang.System.gc()
+# Disallowed since 2.3.0: java.lang.System.setIn(java.io.InputStream)
+# Disallowed since 2.3.0: java.lang.System.setOut(java.io.PrintStream)
+# Disallowed since 2.3.0: java.lang.System.setErr(java.io.PrintStream)
+java.lang.System.getProperties()
+# Disallowed since 2.3.0: java.lang.System.setProperties(java.util.Properties)
+# Disallowed since 2.3.0: java.lang.System.clearProperty(java.lang.String)
+# Disallowed since 2.3.0: java.lang.System.load(java.lang.String)
+
+@whitelistPolicyIfAssignable java.lang.ClassLoader
+java.lang.ClassLoader.getName()
+# Disallowed since 2.3.30: java.lang.ClassLoader.loadClass(java.lang.String)
+# Disallowed since 2.3.30: java.lang.ClassLoader.getPlatformClassLoader()
+# Disallowed since 2.3.30: java.lang.ClassLoader.getSystemClassLoader()
+# Disallowed since 2.3.30:
java.lang.ClassLoader.getSystemResourceAsStream(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.getResourceAsStream(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.getSystemResource(java.lang.String)
+# Disallowed since 2.3.30: java.lang.ClassLoader.getResource(java.lang.String)
+# Disallowed since 2.3.30: java.lang.ClassLoader.getResources(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.getDefinedPackage(java.lang.String)
+# Disallowed since 2.3.30: java.lang.ClassLoader.resources(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.isRegisteredAsParallelCapable()
+# Disallowed since 2.3.30:
java.lang.ClassLoader.getSystemResources(java.lang.String)
+# Disallowed since 2.3.30: java.lang.ClassLoader.getParent()
+# Disallowed since 2.3.30: java.lang.ClassLoader.getUnnamedModule()
+# Disallowed since 2.3.30: java.lang.ClassLoader.getDefinedPackages()
+# Disallowed since 2.3.30:
java.lang.ClassLoader.setDefaultAssertionStatus(boolean)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.setPackageAssertionStatus(java.lang.String,boolean)
+# Disallowed since 2.3.30:
java.lang.ClassLoader.setClassAssertionStatus(java.lang.String,boolean)
+# Disallowed since 2.3.30: java.lang.ClassLoader.clearAssertionStatus()
+
+@whitelistPolicyIfAssignable java.security.ProtectionDomain
+# Disallowed since 2.3.30: java.security.ProtectionDomain.getClassLoader()
+# Disallowed since 2.3.30: java.security.ProtectionDomain.getCodeSource()
+# Disallowed since 2.3.30:
java.security.ProtectionDomain.implies(java.security.Permission)
+# Disallowed since 2.3.30: java.security.ProtectionDomain.getPermissions()
+# Disallowed since 2.3.30: java.security.ProtectionDomain.getPrincipals()
+# Disallowed since 2.3.30:
java.security.ProtectionDomain.staticPermissionsOnly()
+
+@whitelistPolicyIfAssignable java.lang.Class
+java.lang.Class.getName()
+# Disallowed since 2.3.30:
java.lang.Class.forName(java.lang.Module,java.lang.String)
+# Disallowed since 2.3.0:
java.lang.Class.forName(java.lang.String,boolean,java.lang.ClassLoader)
+# Disallowed since 2.3.0: java.lang.Class.forName(java.lang.String)
+# Disallowed since 2.3.30: java.lang.Class.getModule()
+java.lang.Class.getProtectionDomain()
+java.lang.Class.isAssignableFrom(java.lang.Class)
+java.lang.Class.isInstance(java.lang.Object)
+java.lang.Class.getModifiers()
+java.lang.Class.isInterface()
+java.lang.Class.isArray()
+java.lang.Class.isPrimitive()
+java.lang.Class.getSuperclass()
+java.lang.Class.cast(java.lang.Object)
+java.lang.Class.componentType()
+java.lang.Class.componentType()
+java.lang.Class.describeConstable()
+java.lang.Class.getComponentType()
+java.lang.Class.isAnnotation()
+java.lang.Class.isEnum()
+java.lang.Class.getTypeParameters()
+# Disallowed since 2.3.0: java.lang.Class.getClassLoader()
+# Disallowed since 2.3.0: java.lang.Class.newInstance()
+java.lang.Class.getInterfaces()
+java.lang.Class.getEnclosingClass()
+java.lang.Class.getSimpleName()
+java.lang.Class.getCanonicalName()
+# Disallowed since 2.3.30:
java.lang.Class.getResourceAsStream(java.lang.String)
+# Disallowed since 2.3.30: java.lang.Class.getResource(java.lang.String)
+java.lang.Class.getPackageName()
+java.lang.Class.desiredAssertionStatus()
+java.lang.Class.getMethod(java.lang.String,java.lang.Class[])
+java.lang.Class.isAnnotationPresent(java.lang.Class)
+java.lang.Class.descriptorString()
+java.lang.Class.arrayType()
+java.lang.Class.toGenericString()
+java.lang.Class.isSynthetic()
+java.lang.Class.getGenericSuperclass()
+java.lang.Class.getPackage()
+java.lang.Class.getGenericInterfaces()
+# Disallowed since 2.3.30: java.lang.Class.getSigners()
+java.lang.Class.getEnclosingMethod()
+java.lang.Class.getEnclosingConstructor()
+java.lang.Class.getDeclaringClass()
+java.lang.Class.getTypeName()
+java.lang.Class.isAnonymousClass()
+java.lang.Class.isLocalClass()
+java.lang.Class.isMemberClass()
+java.lang.Class.getClasses()
+java.lang.Class.getFields()
+java.lang.Class.getMethods()
+java.lang.Class.getConstructors()
+java.lang.Class.getField(java.lang.String)
+java.lang.Class.getConstructor(java.lang.Class[])
+java.lang.Class.getDeclaredClasses()
+java.lang.Class.getDeclaredFields()
+java.lang.Class.getDeclaredMethods()
+java.lang.Class.getDeclaredConstructors()
+java.lang.Class.getDeclaredField(java.lang.String)
+java.lang.Class.getDeclaredMethod(java.lang.String,java.lang.Class[])
+java.lang.Class.getDeclaredConstructor(java.lang.Class[])
+java.lang.Class.getEnumConstants()
+java.lang.Class.asSubclass(java.lang.Class)
+java.lang.Class.getAnnotation(java.lang.Class)
+java.lang.Class.getAnnotationsByType(java.lang.Class)
+java.lang.Class.getAnnotations()
+java.lang.Class.getDeclaredAnnotation(java.lang.Class)
+java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.Class.getDeclaredAnnotations()
+java.lang.Class.getAnnotatedSuperclass()
+java.lang.Class.getAnnotatedInterfaces()
+java.lang.Class.getNestHost()
+java.lang.Class.isNestmateOf(java.lang.Class)
+java.lang.Class.getNestMembers()
+
+@whitelistPolicyIfAssignable java.lang.Package
+java.lang.Package.getName()
+java.lang.Package.isAnnotationPresent(java.lang.Class)
+java.lang.Package.getPackage(java.lang.String)
+java.lang.Package.getAnnotation(java.lang.Class)
+java.lang.Package.getAnnotationsByType(java.lang.Class)
+java.lang.Package.getAnnotations()
+java.lang.Package.getDeclaredAnnotation(java.lang.Class)
+java.lang.Package.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.Package.getDeclaredAnnotations()
+java.lang.Package.getPackages()
+java.lang.Package.isSealed()
+java.lang.Package.isSealed(java.net.URL)
+java.lang.Package.getSpecificationTitle()
+java.lang.Package.getSpecificationVersion()
+java.lang.Package.getSpecificationVendor()
+java.lang.Package.getImplementationTitle()
+java.lang.Package.getImplementationVersion()
+java.lang.Package.getImplementationVendor()
+java.lang.Package.isCompatibleWith(java.lang.String)
+
+@whitelistPolicyIfAssignable java.lang.reflect.Method
+# Disallowed since 2.3.0:
java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[])
+java.lang.reflect.Method.getName()
+java.lang.reflect.Method.getModifiers()
+java.lang.reflect.Method.getTypeParameters()
+java.lang.reflect.Method.getReturnType()
+java.lang.reflect.Method.getParameterTypes()
+java.lang.reflect.Method.toGenericString()
+java.lang.reflect.Method.isSynthetic()
+java.lang.reflect.Method.getDeclaringClass()
+java.lang.reflect.Method.getAnnotation(java.lang.Class)
+java.lang.reflect.Method.getDeclaredAnnotations()
+# Disallowed since 2.3.0: java.lang.reflect.Method.setAccessible(boolean)
+java.lang.reflect.Method.isVarArgs()
+java.lang.reflect.Method.getParameterCount()
+java.lang.reflect.Method.getParameterAnnotations()
+java.lang.reflect.Method.getGenericParameterTypes()
+java.lang.reflect.Method.getGenericExceptionTypes()
+java.lang.reflect.Method.isDefault()
+java.lang.reflect.Method.getGenericReturnType()
+java.lang.reflect.Method.getExceptionTypes()
+java.lang.reflect.Method.isBridge()
+java.lang.reflect.Method.getDefaultValue()
+java.lang.reflect.Method.getAnnotatedReturnType()
+java.lang.reflect.Method.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.Method.getAnnotatedParameterTypes()
+java.lang.reflect.Method.getParameters()
+java.lang.reflect.Method.getAnnotatedReceiverType()
+java.lang.reflect.Method.getAnnotatedExceptionTypes()
+java.lang.reflect.Method.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.Method.getAnnotations()
+java.lang.reflect.Method.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.Method.getDeclaredAnnotationsByType(java.lang.Class)
+# Disallowed since 2.3.0:
java.lang.reflect.Method.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
+# Disallowed since 2.3.0: java.lang.reflect.Method.trySetAccessible()
+java.lang.reflect.Method.isAccessible()
+java.lang.reflect.Method.canAccess(java.lang.Object)
+
+@whitelistPolicyIfAssignable java.lang.reflect.Constructor
+java.lang.reflect.Constructor.getName()
+java.lang.reflect.Constructor.getModifiers()
+java.lang.reflect.Constructor.getTypeParameters()
+# Disallowed since 2.3.0:
java.lang.reflect.Constructor.newInstance(java.lang.Object[])
+java.lang.reflect.Constructor.getParameterTypes()
+java.lang.reflect.Constructor.toGenericString()
+java.lang.reflect.Constructor.isSynthetic()
+java.lang.reflect.Constructor.getDeclaringClass()
+java.lang.reflect.Constructor.getAnnotation(java.lang.Class)
+java.lang.reflect.Constructor.getDeclaredAnnotations()
+# Disallowed since 2.3.0: java.lang.reflect.Constructor.setAccessible(boolean)
+java.lang.reflect.Constructor.isVarArgs()
+java.lang.reflect.Constructor.getParameterCount()
+java.lang.reflect.Constructor.getParameterAnnotations()
+java.lang.reflect.Constructor.getGenericParameterTypes()
+java.lang.reflect.Constructor.getGenericExceptionTypes()
+java.lang.reflect.Constructor.getExceptionTypes()
+java.lang.reflect.Constructor.getAnnotatedReturnType()
+java.lang.reflect.Constructor.getAnnotatedReceiverType()
+java.lang.reflect.Constructor.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.Constructor.getAnnotatedParameterTypes()
+java.lang.reflect.Constructor.getParameters()
+java.lang.reflect.Constructor.getAnnotatedExceptionTypes()
+java.lang.reflect.Constructor.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.Constructor.getAnnotations()
+java.lang.reflect.Constructor.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.Constructor.getDeclaredAnnotationsByType(java.lang.Class)
+# Disallowed since 2.3.0:
java.lang.reflect.Constructor.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
+# Disallowed since 2.3.0: java.lang.reflect.Constructor.trySetAccessible()
+java.lang.reflect.Constructor.isAccessible()
+java.lang.reflect.Constructor.canAccess(java.lang.Object)
+
+@whitelistPolicyIfAssignable java.lang.reflect.Field
+java.lang.reflect.Field.getName()
+java.lang.reflect.Field.getModifiers()
+# Disallowed since 2.3.30: java.lang.reflect.Field.get(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getBoolean(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getByte(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getShort(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getChar(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getInt(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getLong(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getFloat(java.lang.Object)
+# Disallowed since 2.3.30: java.lang.reflect.Field.getDouble(java.lang.Object)
+java.lang.reflect.Field.toGenericString()
+java.lang.reflect.Field.isSynthetic()
+java.lang.reflect.Field.getDeclaringClass()
+java.lang.reflect.Field.getAnnotation(java.lang.Class)
+java.lang.reflect.Field.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.Field.getDeclaredAnnotations()
+# Disallowed since 2.3.0:
java.lang.reflect.Field.set(java.lang.Object,java.lang.Object)
+# Disallowed since 2.3.0: java.lang.reflect.Field.setAccessible(boolean)
+java.lang.reflect.Field.getGenericType()
+java.lang.reflect.Field.getType()
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setBoolean(java.lang.Object,boolean)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setByte(java.lang.Object,byte)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setChar(java.lang.Object,char)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setShort(java.lang.Object,short)
+# Disallowed since 2.3.0: java.lang.reflect.Field.setInt(java.lang.Object,int)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setLong(java.lang.Object,long)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setFloat(java.lang.Object,float)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setDouble(java.lang.Object,double)
+java.lang.reflect.Field.isEnumConstant()
+java.lang.reflect.Field.getAnnotatedType()
+java.lang.reflect.Field.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.Field.getAnnotations()
+java.lang.reflect.Field.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.Field.getDeclaredAnnotationsByType(java.lang.Class)
+# Disallowed since 2.3.0:
java.lang.reflect.Field.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
+# Disallowed since 2.3.0: java.lang.reflect.Field.trySetAccessible()
+java.lang.reflect.Field.isAccessible()
+java.lang.reflect.Field.canAccess(java.lang.Object)
+
+@blacklistUnlistedMembers java.lang.reflect.AccessibleObject
+java.lang.reflect.AccessibleObject.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.AccessibleObject.getAnnotation(java.lang.Class)
+java.lang.reflect.AccessibleObject.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.AccessibleObject.getAnnotations()
+java.lang.reflect.AccessibleObject.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.AccessibleObject.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.reflect.AccessibleObject.getDeclaredAnnotations()
+# Disallowed since 2.3.0:
java.lang.reflect.AccessibleObject.setAccessible(boolean)
+# Disallowed since 2.3.0:
java.lang.reflect.AccessibleObject.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
+# Disallowed since 2.3.30:
java.lang.reflect.AccessibleObject.trySetAccessible()
+java.lang.reflect.AccessibleObject.isAccessible()
+java.lang.reflect.AccessibleObject.canAccess(java.lang.Object)
+
+@whitelistPolicyIfAssignable java.lang.reflect.Member
+java.lang.reflect.Member.getName()
+java.lang.reflect.Member.getModifiers()
+java.lang.reflect.Member.isSynthetic()
+java.lang.reflect.Member.getDeclaringClass()
+
+@whitelistPolicyIfAssignable java.lang.reflect.GenericDeclaration
+java.lang.reflect.GenericDeclaration.getTypeParameters()
+java.lang.reflect.GenericDeclaration.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.GenericDeclaration.getAnnotation(java.lang.Class)
+java.lang.reflect.GenericDeclaration.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.GenericDeclaration.getAnnotations()
+java.lang.reflect.GenericDeclaration.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.GenericDeclaration.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.reflect.GenericDeclaration.getDeclaredAnnotations()
+
+@whitelistPolicyIfAssignable java.lang.reflect.Executable
+java.lang.reflect.Executable.getName()
+java.lang.reflect.Executable.getModifiers()
+java.lang.reflect.Executable.getTypeParameters()
+java.lang.reflect.Executable.getParameterTypes()
+java.lang.reflect.Executable.toGenericString()
+java.lang.reflect.Executable.isSynthetic()
+java.lang.reflect.Executable.getDeclaringClass()
+java.lang.reflect.Executable.getAnnotation(java.lang.Class)
+java.lang.reflect.Executable.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.Executable.getDeclaredAnnotations()
+java.lang.reflect.Executable.isVarArgs()
+java.lang.reflect.Executable.getAnnotatedParameterTypes()
+java.lang.reflect.Executable.getParameterCount()
+java.lang.reflect.Executable.getParameterAnnotations()
+java.lang.reflect.Executable.getGenericParameterTypes()
+java.lang.reflect.Executable.getGenericExceptionTypes()
+java.lang.reflect.Executable.getExceptionTypes()
+java.lang.reflect.Executable.getAnnotatedReturnType()
+java.lang.reflect.Executable.getParameters()
+java.lang.reflect.Executable.getAnnotatedReceiverType()
+java.lang.reflect.Executable.getAnnotatedExceptionTypes()
+java.lang.reflect.Executable.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.Executable.getAnnotations()
+java.lang.reflect.Executable.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.Executable.getDeclaredAnnotationsByType(java.lang.Class)
+# Disallowed since 2.3.0: java.lang.reflect.Executable.setAccessible(boolean)
+# Disallowed since 2.3.0:
java.lang.reflect.Executable.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
+# Disallowed since 2.3.0: java.lang.reflect.Executable.trySetAccessible()
+java.lang.reflect.Executable.isAccessible()
+java.lang.reflect.Executable.canAccess(java.lang.Object)
+
+@whitelistPolicyIfAssignable java.lang.reflect.TypeVariable
+java.lang.reflect.TypeVariable.getName()
+java.lang.reflect.TypeVariable.getBounds()
+java.lang.reflect.TypeVariable.getGenericDeclaration()
+java.lang.reflect.TypeVariable.getAnnotatedBounds()
+java.lang.reflect.TypeVariable.getTypeName()
+java.lang.reflect.TypeVariable.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.TypeVariable.getAnnotation(java.lang.Class)
+java.lang.reflect.TypeVariable.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.TypeVariable.getAnnotations()
+java.lang.reflect.TypeVariable.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.TypeVariable.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.reflect.TypeVariable.getDeclaredAnnotations()
+
+@whitelistPolicyIfAssignable java.lang.reflect.AnnotatedType
+java.lang.reflect.AnnotatedType.getType()
+java.lang.reflect.AnnotatedType.getAnnotatedOwnerType()
+java.lang.reflect.AnnotatedType.isAnnotationPresent(java.lang.Class)
+java.lang.reflect.AnnotatedType.getAnnotation(java.lang.Class)
+java.lang.reflect.AnnotatedType.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.AnnotatedType.getAnnotations()
+java.lang.reflect.AnnotatedType.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.AnnotatedType.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.reflect.AnnotatedType.getDeclaredAnnotations()
+
+@whitelistPolicyIfAssignable java.lang.reflect.Type
+java.lang.reflect.Type.getTypeName()
+
+@whitelistPolicyIfAssignable java.lang.reflect.Parameter
+java.lang.reflect.Parameter.getName()
+java.lang.reflect.Parameter.getModifiers()
+java.lang.reflect.Parameter.isSynthetic()
+java.lang.reflect.Parameter.getAnnotation(java.lang.Class)
+java.lang.reflect.Parameter.getAnnotationsByType(java.lang.Class)
+java.lang.reflect.Parameter.getAnnotations()
+java.lang.reflect.Parameter.getDeclaredAnnotation(java.lang.Class)
+java.lang.reflect.Parameter.getDeclaredAnnotationsByType(java.lang.Class)
+java.lang.reflect.Parameter.getDeclaredAnnotations()
+java.lang.reflect.Parameter.getType()
+java.lang.reflect.Parameter.getAnnotatedType()
+java.lang.reflect.Parameter.getParameterizedType()
+java.lang.reflect.Parameter.isVarArgs()
+java.lang.reflect.Parameter.isNamePresent()
+java.lang.reflect.Parameter.getDeclaringExecutable()
+java.lang.reflect.Parameter.isImplicit()
+java.lang.reflect.Parameter.isAnnotationPresent(java.lang.Class)
+
+@whitelistPolicyIfAssignable java.lang.annotation.Annotation
+java.lang.annotation.Annotation.annotationType()
+
+@whitelistPolicyIfAssignable java.lang.constant.ClassDesc
+java.lang.constant.ClassDesc.isArray()
+java.lang.constant.ClassDesc.isPrimitive()
+java.lang.constant.ClassDesc.componentType()
+# Disallowed since 2.3.30: java.lang.constant.ClassDesc.of(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.constant.ClassDesc.of(java.lang.String,java.lang.String)
+java.lang.constant.ClassDesc.packageName()
+java.lang.constant.ClassDesc.descriptorString()
+# Disallowed since 2.3.30:
java.lang.constant.ClassDesc.ofDescriptor(java.lang.String)
+java.lang.constant.ClassDesc.arrayType()
+java.lang.constant.ClassDesc.arrayType()
+java.lang.constant.ClassDesc.arrayType(int)
+java.lang.constant.ClassDesc.displayName()
+java.lang.constant.ClassDesc.isClassOrInterface()
+# Disallowed since 2.3.30:
java.lang.constant.ClassDesc.nested(java.lang.String,java.lang.String[])
+# Disallowed since 2.3.30:
java.lang.constant.ClassDesc.nested(java.lang.String)
+# Disallowed since 2.3.30:
java.lang.constant.ClassDesc.resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup)
+
+@whitelistPolicyIfAssignable java.net.URL
+# Disallowed since 2.3.30: java.net.URL.openStream()
+java.net.URL.getHost()
+java.net.URL.getPort()
+java.net.URL.getDefaultPort()
+java.net.URL.sameFile(java.net.URL)
+java.net.URL.toExternalForm()
+# Disallowed since 2.3.30: java.net.URL.openConnection()
+# Disallowed since 2.3.30: java.net.URL.openConnection(java.net.Proxy)
+# Disallowed since 2.3.30: java.net.URL.getContent()
+# Disallowed since 2.3.30: java.net.URL.getContent(java.lang.Class[])
+java.net.URL.getProtocol()
+java.net.URL.getAuthority()
+java.net.URL.getFile()
+java.net.URL.getRef()
+java.net.URL.getQuery()
+java.net.URL.getPath()
+java.net.URL.getUserInfo()
+java.net.URL.toURI()
+# Disallowed since 2.3.30:
java.net.URL.setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)
+
+@whitelistPolicyIfAssignable java.lang.constant.ClassDesc
+
+@whitelistPolicyIfAssignable java.net.URI
+java.net.URI.getRawAuthority()
+java.net.URI.compareTo(java.lang.Object)
+java.net.URI.compareTo(java.net.URI)
+java.net.URI.isAbsolute()
+java.net.URI.resolve(java.net.URI)
+java.net.URI.resolve(java.lang.String)
+java.net.URI.normalize()
+java.net.URI.getScheme()
+java.net.URI.isOpaque()
+java.net.URI.getRawFragment()
+java.net.URI.getRawQuery()
+java.net.URI.getRawPath()
+java.net.URI.getHost()
+java.net.URI.getPort()
+java.net.URI.create(java.lang.String)
+java.net.URI.getAuthority()
+java.net.URI.getQuery()
+java.net.URI.getPath()
+java.net.URI.getUserInfo()
+java.net.URI.toURL()
+java.net.URI.relativize(java.net.URI)
+java.net.URI.getRawSchemeSpecificPart()
+java.net.URI.parseServerAuthority()
+java.net.URI.getSchemeSpecificPart()
+java.net.URI.getRawUserInfo()
+java.net.URI.getFragment()
+java.net.URI.toASCIIString()
diff --git a/src/main/resources/freemarker/ext/beans/unsafeMethods.properties
b/src/main/resources/freemarker/ext/beans/unsafeMethods.properties
index 05c1981..a8025af 100644
--- a/src/main/resources/freemarker/ext/beans/unsafeMethods.properties
+++ b/src/main/resources/freemarker/ext/beans/unsafeMethods.properties
@@ -15,6 +15,14 @@
# specific language governing permissions and limitations
# under the License.
+# Used by LegacyDefaultMemberAccessPolicy (not by DefaultMemberAccessPolicy).
+# It does NOT provide enough safety if template authors aren't as trusted as
the developers; you need to use a custom
+# whitelist then (see WhitelistMemberAccessPolicy).
+
+# This is a blacklist, that is, methods mentioned here will be not be
accessible, but everything else will be.
+# Furthermore, overridden version of the blacklisted methods will be
accessible (which is strange, but we kept backward
+# compatibility).
+
java.lang.Object.wait()
java.lang.Object.wait(long)
java.lang.Object.wait(long,int)
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index f736f8a..433ac9a 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -28953,8 +28953,7 @@ TemplateModel x = env.getVariable("x"); // get
variable x</programlisting>
by the <literal>MemberAccessPolicy</literal> also won't be
visible with <literal>?api</literal> (assuming you are using a
well behaving <literal>ObjectWrapper</literal>, like
- <literal>DefaultObjectWrapper</literal> is, hopefully.)
- </para>
+ <literal>DefaultObjectWrapper</literal> is, hopefully.)</para>
<para>Last not least, some maybe aware of that the standard
object wrappers filters out some well known
@@ -29013,7 +29012,7 @@ TemplateModel x = env.getVariable("x"); // get
variable x</programlisting>
<literal>BeansWrapper</literal> or a subclass of it (typically
<literal>DefaultObjectWrapper</literal>), constructors not
allowed by the <literal>MemberAccessPolicy</literal> also
- won't be accessible for <literal>?new</literal>. </para>
+ won't be accessible for <literal>?new</literal>.</para>
</listitem>
<listitem>
@@ -29213,6 +29212,26 @@ TemplateModel x = env.getVariable("x"); // get
variable x</programlisting>
<itemizedlist>
<listitem>
+ <para><link
+
xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-124">FREEMARKER-124</link>:
+ Made the default filtering of class members more restrictive
+ (when you are using <literal>BeansWrapper</literal>, or its
+ subclasses like <literal>DefaultObjectWrapper</literal>). This
+ is not strictly backward compatible, but unlikely to break any
+ real-world applications; see
+
<literal>src/main/resources/freemarker/ext/beans/DefaultMemberAccessPolicy-rules</literal>
+ to see what was changed. This change was made for security
+ reasons, but the default behavior will never be safe enough if
+ untrusted users will edit templates; see <link
+ linkend="faq_template_uploading_security">in the FAQ</link>. In
+ the unlikely case this change breaks your application, then you
+ can still use the old behavior by setting the
+ <literal>memberAccessPolicy</literal> property of the object
+ wrapper to
+
<literal>LegacyDefaultMemberAccessPolicy.INSTANCE</literal>.</para>
+ </listitem>
+
+ <listitem>
<para>Added
<literal>freemarker.ext.beans.MemberAccessPolicy</literal>
interface, and the <literal>memberAccessPolicy</literal>
diff --git
a/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
b/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
new file mode 100644
index 0000000..324abd1
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package freemarker.ext.beans;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import freemarker.template.Configuration;
+
+public class DefaultMemberAccessPolicyTest {
+
+ private static final DefaultMemberAccessPolicy POLICY =
DefaultMemberAccessPolicy.getInstance(Configuration.VERSION_2_3_30);
+
+ @Test
+ public void testWhitelistRuleWithNoMembers() throws NoSuchMethodException {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(ProtectionDomain.class);
+
assertFalse(classPolicy.isMethodExposed(ProtectionDomain.class.getMethod("getClassLoader")));
+ }
+
+ @Test
+ public void testWhitelistRuleWithSomeMembers() throws
NoSuchMethodException {
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(URL.class);
+
assertFalse(classPolicy.isMethodExposed(URL.class.getMethod("openStream")));
+
assertFalse(classPolicy.isMethodExposed(URL.class.getMethod("getContent",
Class[].class)));
+
assertTrue(classPolicy.isMethodExposed(URL.class.getMethod("getHost")));
+ assertTrue(classPolicy.isMethodExposed(URL.class.getMethod("sameFile",
URL.class)));
+ }
+
+ @Test
+ public void testWhitelistRuleOnSubclass() throws NoSuchMethodException {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(CustomClassLoader.class);
+
assertFalse(classPolicy.isMethodExposed(CustomClassLoader.class.getMethod("loadClass",
String.class)));
+
assertFalse(classPolicy.isMethodExposed(CustomClassLoader.class.getMethod("m1")));
+ }
+
+ @Test
+ public void testBlacklistUnlistedRule() throws NoSuchMethodException {
+ for (Class<?> testedClass : Arrays.asList(
+ Object.class, Thread.class, ThreadSubclass.class,
ProtectionDomain.class, CustomClassLoader.class,
+ UserClass.class)) {
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(testedClass);
+
assertFalse(classPolicy.isMethodExposed(testedClass.getMethod("wait")));
+ assertTrue(testedClass.getName(),
classPolicy.isMethodExposed(testedClass.getMethod("toString")));
+ }
+
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(UserClass.class);
+
assertTrue(classPolicy.isMethodExposed(UserClass.class.getMethod("foo")));
+ }
+
+ @Test
+ public void testBlacklistUnlistedRuleOnSubclass() throws
NoSuchMethodException {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(ThreadSubclass.class);
+
assertFalse(classPolicy.isMethodExposed(ThreadSubclass.class.getMethod("run")));
+
assertTrue(classPolicy.isMethodExposed(ThreadSubclass.class.getMethod("getName")));
+
assertTrue(classPolicy.isMethodExposed(ThreadSubclass.class.getMethod("m1")));
+ }
+
+ @Test
+ public void testWellKnownUnsafeMethodsAreBanned() throws
NoSuchMethodException {
+ {
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(Class.class);
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("forName",
String.class)));
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("newInstance")));
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("getClassLoader")));
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("getResourceAsStream",
String.class)));
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("getResource",
String.class)));
+
assertTrue(classPolicy.isMethodExposed(Class.class.getMethod("getProtectionDomain")));
// Allowed
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(ProtectionDomain.class);
+
assertFalse(classPolicy.isMethodExposed(ProtectionDomain.class.getMethod("getClassLoader")));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(ClassLoader.class);
+
assertFalse(classPolicy.isMethodExposed(ClassLoader.class.getMethod("loadClass",
String.class)));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(Method.class);
+
assertFalse(classPolicy.isMethodExposed(Method.class.getMethod("invoke",
Object.class, Object[].class)));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(Constructor.class);
+
assertFalse(classPolicy.isMethodExposed(Constructor.class.getMethod("newInstance",
Object[].class)));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(Field.class);
+
assertFalse(classPolicy.isMethodExposed(Field.class.getMethod("get",
Object.class)));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
POLICY.forClass(Object.class);
+
assertTrue(classPolicy.isMethodExposed(Field.class.getMethod("getClass"))); //
Allowed by design
+
assertFalse(classPolicy.isMethodExposed(Field.class.getMethod("wait")));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy = POLICY.forClass(URL.class);
+
assertFalse(classPolicy.isMethodExposed(URL.class.getMethod("openConnection")));
+ }
+ }
+
+ public static class ThreadSubclass extends Thread {
+ @Override
+ public void run() {
+ super.run();
+ }
+
+ public void m1() {}
+ }
+
+ public static class UserClass {
+ public void foo() {
+ }
+ }
+
+ public static class CustomClassLoader extends ClassLoader {
+ public void m1() {}
+ }
+
+}
diff --git
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
index eafbaf2..f9fcf47 100644
---
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
+++
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
@@ -51,16 +51,18 @@ import freemarker.template.TemplateNumberModel;
public class DefaultObjectWrapperMemberAccessPolicyTest {
+ private final DefaultObjectWrapper dow
+ = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build();
+
@Test
public void testMethodsWithDefaultMemberAccessPolicy() throws
TemplateModelException {
- DefaultObjectWrapper ow =
createDefaultMemberAccessPolicyObjectWrapper();
- TemplateHashModel objM = (TemplateHashModel) ow.wrap(new C());
+ TemplateHashModel objM = (TemplateHashModel) dow.wrap(new C());
assertNotNull(objM.get("m1"));
- assertEquals("m2(true)", exec(ow, objM.get("m2"), true));
- assertEquals("staticM()", exec(ow, objM.get("staticM")));
+ assertEquals("m2(true)", exec(dow, objM.get("m2"), true));
+ assertEquals("staticM()", exec(dow, objM.get("staticM")));
- assertEquals("x", getHashValue(ow, objM, "x"));
+ assertEquals("x", getHashValue(dow, objM, "x"));
assertNotNull(objM.get("getX"));
assertNotNull(objM.get("setX"));
@@ -68,12 +70,9 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
assertNull(objM.get("notify"));
- // Because it was overridden, we allow it historically.
- assertNotNull(objM.get("run"));
-
- assertEquals("safe wait(1)", exec(ow, objM.get("wait"), 1L));
+ assertEquals("safe wait(1)", exec(dow, objM.get("wait"), 1L));
try {
- exec(ow, objM.get("wait")); // 0 arg overload is not visible, a
it's "unsafe"
+ exec(dow, objM.get("wait")); // 0 arg overload is not visible, a
it's "unsafe"
fail();
} catch (TemplateModelException e) {
assertThat(e.getMessage(), containsString("wait(int)"));
@@ -82,8 +81,7 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
@Test
public void testFieldsWithDefaultMemberAccessPolicy() throws
TemplateModelException {
- DefaultObjectWrapper ow =
createDefaultMemberAccessPolicyObjectWrapper();
- TemplateHashModel objM = (TemplateHashModel) ow.wrap(new C());
+ TemplateHashModel objM = (TemplateHashModel) dow.wrap(new C());
assertFieldsNotExposed(objM);
}
@@ -103,27 +101,33 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
@Test
public void testGenericGetWithDefaultMemberAccessPolicy() throws
TemplateModelException {
- DefaultObjectWrapper ow =
createDefaultMemberAccessPolicyObjectWrapper();
+ TemplateHashModel objM = (TemplateHashModel) dow.wrap(new
CWithGenericGet());
- TemplateHashModel objM = (TemplateHashModel) ow.wrap(new
CWithGenericGet());
+ assertEquals("get(x)", getHashValue(dow, objM, "x"));
+ }
- assertEquals("get(x)", getHashValue(ow, objM, "x"));
+ @Test
+ public void testBlacklistRuleWithDefaultMemberAccessPolicy() throws
TemplateModelException {
+ TemplateHashModel objM = (TemplateHashModel) dow.wrap(new CThread());
+
+ assertNull(getHashValue(dow, objM, "run")); // blacklisted in Thread
+ assertNotNull(getHashValue(dow, objM, "m1")); // As Thread doesn't use
whitelisted rule
+ assertNotNull(getHashValue(dow, objM, "toString"));
}
@Test
public void testConstructorsWithDefaultMemberAccessPolicy() throws
TemplateModelException {
- DefaultObjectWrapper ow =
createDefaultMemberAccessPolicyObjectWrapper();
- assertNonPublicConstructorNotExposed(ow);
+ assertNonPublicConstructorNotExposed(dow);
- assertEquals(CWithConstructor.class,
ow.newInstance(CWithConstructor.class, Collections.emptyList())
+ assertEquals(CWithConstructor.class,
dow.newInstance(CWithConstructor.class, Collections.emptyList())
.getClass());
assertEquals(CWithOverloadedConstructor.class,
- ow.newInstance(CWithOverloadedConstructor.class,
Collections.emptyList())
+ dow.newInstance(CWithOverloadedConstructor.class,
Collections.emptyList())
.getClass());
assertEquals(CWithOverloadedConstructor.class,
- ow.newInstance(CWithOverloadedConstructor.class,
Collections.singletonList(new SimpleNumber(1)))
+ dow.newInstance(CWithOverloadedConstructor.class,
Collections.singletonList(new SimpleNumber(1)))
.getClass());
}
@@ -357,7 +361,7 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
@Test
- public void testMemberAccessPolicyAndNewBI() throws IOException,
TemplateException {
+ public void testMemberAccessPolicyAndNewBI() throws IOException,
TemplateException, NoSuchMethodException {
DefaultObjectWrapperBuilder owb = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
owb.setMemberAccessPolicy(new MemberAccessPolicy() {
public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
@@ -392,7 +396,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
assertEquals("1 failed", out.toString());
}
- cfg.setObjectWrapper(new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build());
+ DefaultObjectWrapper dow = new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build();
+ MemberAccessPolicy pol = dow.getMemberAccessPolicy();
+ ClassMemberAccessPolicy cpol = pol.forClass(CustomModel.class);
+
assertTrue(cpol.isConstructorExposed(CustomModel.class.getConstructor()));
+ cfg.setObjectWrapper(dow);
{
StringWriter out = new StringWriter();
template.process(null, out);
@@ -400,10 +408,6 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
}
- private static DefaultObjectWrapper
createDefaultMemberAccessPolicyObjectWrapper() {
- return new
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build();
- }
-
private static Object getHashValue(ObjectWrapperAndUnwrapper ow,
TemplateHashModel objM, String key)
throws TemplateModelException {
return ow.unwrap(objM.get(key));
@@ -423,7 +427,7 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
return returnValue instanceof TemplateModel ?
ow.unwrap((TemplateModel) returnValue) : returnValue;
}
- public static class C extends Thread {
+ public static class C {
public static final int STATIC_FIELD = 1;
public int publicField1 = 1;
public int publicField2 = 2;
@@ -471,18 +475,13 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
public String wait(int otherOverload) {
return "safe wait(" + otherOverload + ")";
}
-
- @Override
- public void run() {
- return;
- }
}
public static class CExtended extends C {
public int publicField3 = 3;
}
- public static class CWithGenericGet extends Thread {
+ public static class CWithGenericGet {
public String get(String key) {
return "get(" + key + ")";
}
@@ -493,6 +492,13 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
}
}
+ public static class CThread extends Thread {
+ @Override
+ public void run() {}
+
+ public void m1() {}
+ }
+
public static class CWithOverloadedConstructor implements TemplateModel {
public CWithOverloadedConstructor() {
}
diff --git
a/src/test/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicyTest.java
b/src/test/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicyTest.java
new file mode 100644
index 0000000..e301ddf
--- /dev/null
+++
b/src/test/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicyTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package freemarker.ext.beans;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class LegacyDefaultMemberAccessPolicyTest {
+
+ @Test
+ public void testBasic() throws NoSuchMethodException, NoSuchFieldException
{
+ ClassMemberAccessPolicy classPolicy =
LegacyDefaultMemberAccessPolicy.INSTANCE.forClass(UserClass.class);
+
assertFalse(classPolicy.isMethodExposed(UserClass.class.getMethod("wait")));
+
assertTrue(classPolicy.isMethodExposed(UserClass.class.getMethod("foo")));
+
assertTrue(classPolicy.isConstructorExposed(UserClass.class.getConstructor()));
+ assertTrue(classPolicy.isFieldExposed(UserClass.class.getField("f1")));
+ }
+
+ @Test
+ public void testThread() throws NoSuchMethodException,
NoSuchFieldException {
+ {
+ ClassMemberAccessPolicy classPolicy =
LegacyDefaultMemberAccessPolicy.INSTANCE.forClass(Thread.class);
+
assertFalse(classPolicy.isMethodExposed(Thread.class.getMethod("run")));
+ }
+ {
+ ClassMemberAccessPolicy classPolicy =
LegacyDefaultMemberAccessPolicy.INSTANCE.forClass(ThreadSubclass.class);
+ // Strange glitch that we reproduce for backward compatibility:
+
assertTrue(classPolicy.isMethodExposed(ThreadSubclass.class.getMethod("run")));
+ }
+ }
+
+ @Test
+ public void testClass() throws NoSuchMethodException, NoSuchFieldException
{
+ ClassMemberAccessPolicy classPolicy =
LegacyDefaultMemberAccessPolicy.INSTANCE.forClass(Class.class);
+
assertFalse(classPolicy.isMethodExposed(Class.class.getMethod("getClassLoader")));
+
assertTrue(classPolicy.isMethodExposed(Class.class.getMethod("getName")));
+ }
+
+ public static class ThreadSubclass extends Thread {
+ @Override
+ public void run() {
+ super.run();
+ }
+
+ public void m1() {}
+ }
+
+ public static class UserClass {
+ public String f1;
+
+ public void foo() {
+ }
+ }
+
+}
diff --git
a/src/test/java/freemarker/ext/beans/WhitelistMemberAccessPolicyTest.java
b/src/test/java/freemarker/ext/beans/MemberSelectorListAccessPolicyTest.java
similarity index 84%
rename from
src/test/java/freemarker/ext/beans/WhitelistMemberAccessPolicyTest.java
rename to
src/test/java/freemarker/ext/beans/MemberSelectorListAccessPolicyTest.java
index 0edee07..1250c2a 100644
--- a/src/test/java/freemarker/ext/beans/WhitelistMemberAccessPolicyTest.java
+++ b/src/test/java/freemarker/ext/beans/MemberSelectorListAccessPolicyTest.java
@@ -27,11 +27,7 @@ import java.util.Arrays;
import org.junit.Test;
-import freemarker.template.Configuration;
-import freemarker.template.TemplateException;
-import freemarker.template.TemplateHashModel;
-
-public class WhitelistMemberAccessPolicyTest {
+public class MemberSelectorListAccessPolicyTest {
@Test
public void testEmpty() throws NoSuchMethodException, NoSuchFieldException
{
@@ -92,7 +88,7 @@ public class WhitelistMemberAccessPolicyTest {
assertFalse(c1Policy.isConstructorExposed(C1.class.getConstructor(int.class)));
assertTrue(c2Policy.isConstructorExposed(C2.class.getConstructor(int.class)));
-
assertTrue(c3Policy.isConstructorExposed(C3.class.getConstructor(int.class)));
+
assertFalse(c3Policy.isConstructorExposed(C3.class.getConstructor(int.class)));
// Not inherited
assertFalse(c1Policy.isMethodExposed(C1.class.getMethod("m1")));
assertTrue(c2Policy.isMethodExposed(C2.class.getMethod("m1")));
@@ -263,6 +259,75 @@ public class WhitelistMemberAccessPolicyTest {
}
@Test
+ public void testBlacklist1() throws NoSuchMethodException,
NoSuchFieldException {
+ BlacklistMemberAccessPolicy policy = newBlacklistMemberAccessPolicy(
+ C1.class.getName() + ".m1()",
+ C1.class.getName() + ".f1",
+ C1.class.getName() + "." + C1.class.getSimpleName() + "()"
+ );
+
+ for (Class<?> cl : new Class[] { C1.class, C2.class, C3.class }) {
+ ClassMemberAccessPolicy classPolicy = policy.forClass(cl);
+ assertFalse(classPolicy.isMethodExposed(cl.getMethod("m1")));
+ assertTrue(classPolicy.isMethodExposed(cl.getMethod("m2",
int.class)));
+ assertTrue(classPolicy.isMethodExposed(cl.getMethod("m3")));
+ assertFalse(classPolicy.isFieldExposed(cl.getField("f1")));
+ assertTrue(classPolicy.isFieldExposed(cl.getField("f2")));
+ if (cl != C2.class) {
+ assertEquals(cl != C1.class,
classPolicy.isConstructorExposed(cl.getConstructor()));
+ }
+
assertTrue(classPolicy.isConstructorExposed(cl.getConstructor(int.class)));
+ }
+ }
+
+ @Test
+ public void testBlacklist2() throws NoSuchMethodException,
NoSuchFieldException {
+ BlacklistMemberAccessPolicy policy = newBlacklistMemberAccessPolicy(
+ C2.class.getName() + ".m1()",
+ C2.class.getName() + ".f1",
+ C2.class.getName() + "." + C2.class.getSimpleName() + "(int)"
+ );
+
+ {
+ Class<C1> lc = C1.class;
+ ClassMemberAccessPolicy classPolicy = policy.forClass(lc);
+ assertTrue(classPolicy.isMethodExposed(lc.getMethod("m1")));
+ assertTrue(classPolicy.isFieldExposed(lc.getField("f1")));
+
assertTrue(classPolicy.isConstructorExposed(lc.getConstructor(int.class)));
+ }
+
+ {
+ Class<C2> lc = C2.class;
+ ClassMemberAccessPolicy classPolicy = policy.forClass(lc);
+ assertFalse(classPolicy.isMethodExposed(lc.getMethod("m1")));
+ assertFalse(classPolicy.isFieldExposed(lc.getField("f1")));
+
assertFalse(classPolicy.isConstructorExposed(lc.getConstructor(int.class)));
+ }
+
+ {
+ Class<C3> lc = C3.class;
+ ClassMemberAccessPolicy classPolicy = policy.forClass(lc);
+ assertFalse(classPolicy.isMethodExposed(lc.getMethod("m1")));
+ assertFalse(classPolicy.isFieldExposed(lc.getField("f1")));
+
assertTrue(classPolicy.isConstructorExposed(lc.getConstructor(int.class)));
+ }
+ }
+
+ @Test
+ public void testBlacklistIgnoredAnnotation() throws NoSuchMethodException,
NoSuchFieldException {
+ BlacklistMemberAccessPolicy policy = newBlacklistMemberAccessPolicy(
+ CAnnotationsTest1.class.getName() + ".m5()",
+ CAnnotationsTest1.class.getName() + ".f5",
+ CAnnotationsTest1.class.getName() + "." +
CAnnotationsTest1.class.getSimpleName() + "()"
+ );
+
+ ClassMemberAccessPolicy classPolicy =
policy.forClass(CAnnotationsTest1.class);
+
assertFalse(classPolicy.isMethodExposed(CAnnotationsTest1.class.getMethod("m5")));
+
assertFalse(classPolicy.isFieldExposed(CAnnotationsTest1.class.getField("f5")));
+
assertFalse(classPolicy.isConstructorExposed(CAnnotationsTest1.class.getConstructor()));
+ }
+
+ @Test
public void memberSelectorParserIgnoresWhitespace() throws
NoSuchMethodException {
WhitelistMemberAccessPolicy policy = newWhitelistMemberAccessPolicy(
(CArrayArgs.class.getName() +
".m1(java.lang.String)").replace(".", "\n\t. "),
@@ -307,7 +372,7 @@ public class WhitelistMemberAccessPolicyTest {
newWhitelistMemberAccessPolicy("java.util.Date.toString(");
fail();
} catch (IllegalArgumentException e) {
- assertThat(e.getMessage(), containsString("missing closing ')'"));
+ assertThat(e.getMessage(), containsString("should end with ')'"));
}
try {
newWhitelistMemberAccessPolicy("java.util.Date.m(com.x-y)");
@@ -361,31 +426,18 @@ public class WhitelistMemberAccessPolicyTest {
CAnnotationsTest2.class.getConstructor(int.class, int.class,
int.class, int.class)));
}
- public static final MemberAccessPolicy CONFIG_TEST_MEMBER_ACCESS_POLICY =
- newWhitelistMemberAccessPolicy(
- C1.class.getName() + ".m1()",
- C1.class.getName() + ".m3()");
-
- @Test
- public void stringBasedConfigurationTest() throws TemplateException {
- Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
- cfg.setSetting(
- "objectWrapper",
- "DefaultObjectWrapper(2.3.30, " +
- "memberAccessPolicy="
- + WhitelistMemberAccessPolicyTest.class.getName() +
".CONFIG_TEST_MEMBER_ACCESS_POLICY"
- + ")");
- TemplateHashModel m = (TemplateHashModel)
cfg.getObjectWrapper().wrap(new C1());
- assertNotNull(m.get("m1"));
- assertNull(m.get("m2"));
- assertNotNull(m.get("m3"));
- }
-
private static WhitelistMemberAccessPolicy
newWhitelistMemberAccessPolicy(String... memberSelectors) {
return new WhitelistMemberAccessPolicy(
- WhitelistMemberAccessPolicy.MemberSelector.parse(
+ MemberSelectorListMemberAccessPolicy.MemberSelector.parse(
Arrays.asList(memberSelectors),
-
WhitelistMemberAccessPolicyTest.class.getClassLoader()));
+
MemberSelectorListAccessPolicyTest.class.getClassLoader()));
+ }
+
+ private static BlacklistMemberAccessPolicy
newBlacklistMemberAccessPolicy(String... memberSelectors) {
+ return new BlacklistMemberAccessPolicy(
+ MemberSelectorListMemberAccessPolicy.MemberSelector.parse(
+ Arrays.asList(memberSelectors),
+
MemberSelectorListAccessPolicyTest.class.getClassLoader()));
}
public static class C1 {
@@ -421,6 +473,10 @@ public class WhitelistMemberAccessPolicyTest {
super(x);
}
+ @Override
+ public void m2(int x) {
+ }
+
public void m2(boolean x) {
}
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java
b/src/test/java/freemarker/template/ConfigurationTest.java
index c18e47c..4b77eef 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -22,6 +22,7 @@ package freemarker.template;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
@@ -80,7 +81,11 @@ import freemarker.core.XHTMLOutputFormat;
import freemarker.core.XMLOutputFormat;
import freemarker.core._CoreStringUtils;
import freemarker.ext.beans.BeansWrapperBuilder;
+import freemarker.ext.beans.LegacyDefaultMemberAccessPolicy;
+import freemarker.ext.beans.MemberAccessPolicy;
+import freemarker.ext.beans.MemberSelectorListMemberAccessPolicy;
import freemarker.ext.beans.StringModel;
+import freemarker.ext.beans.WhitelistMemberAccessPolicy;
import freemarker.template.utility.DateUtil;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.NullWriter;
@@ -1896,6 +1901,39 @@ public class ConfigurationTest extends TestCase {
assertFalse(cfg.getFallbackOnNullLoopVariable());
}
+ public static final MemberAccessPolicy CONFIG_TEST_MEMBER_ACCESS_POLICY =
+ new
WhitelistMemberAccessPolicy(MemberSelectorListMemberAccessPolicy.MemberSelector.parse(
+ ImmutableList.<String>of(
+ File.class.getName() + ".getName()",
+ File.class.getName() + ".isFile()"),
+ ConfigurationTest.class.getClassLoader()));
+
+ @Test
+ public void testMemberAccessPolicySetting() throws TemplateException {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
+ cfg.setSetting(
+ "objectWrapper",
+ "DefaultObjectWrapper(2.3.30, "
+ + "memberAccessPolicy="
+ + ConfigurationTest.class.getName() +
".CONFIG_TEST_MEMBER_ACCESS_POLICY"
+ + ")");
+ TemplateHashModel m = (TemplateHashModel)
cfg.getObjectWrapper().wrap(new File("x"));
+ assertNotNull(m.get("getName"));
+ assertNotNull(m.get("isFile"));
+ assertNull(m.get("delete"));
+ }
+
+ @Test
+ public void testMemberAccessPolicySetting2() throws TemplateException {
+ Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
+ cfg.setSetting(
+ "objectWrapper",
+ "DefaultObjectWrapper(2.3.30, "
+ + "memberAccessPolicy=" +
LegacyDefaultMemberAccessPolicy.class.getName() + "())");
+ assertSame(((DefaultObjectWrapper)
cfg.getObjectWrapper()).getMemberAccessPolicy(),
+ LegacyDefaultMemberAccessPolicy.INSTANCE);
+ }
+
@Test
public void testGetSettingNamesAreSorted() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);