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 f13ef35  MemberAccessPolicy now also covers the special case when 
toString() is called to convert and object to string in a template. This was 
added as toString() might shows information that you don't want to be exposed.
f13ef35 is described below

commit f13ef351818acc26d067e295234153f48c13b161
Author: ddekany <[email protected]>
AuthorDate: Tue Jan 14 16:56:50 2020 +0100

    MemberAccessPolicy now also covers the special case when toString() is 
called to convert and object to string in a template. This was added as 
toString() might shows information that you don't want to be exposed.
---
 .../ext/beans/AllowAllMemberAccessPolicy.java      |  5 ++
 .../ext/beans/BlacklistMemberAccessPolicy.java     | 18 +++++
 .../freemarker/ext/beans/ClassIntrospector.java    | 35 ++++++++--
 .../ext/beans/DefaultMemberAccessPolicy.java       | 10 +++
 .../ext/beans/LegacyDefaultMemberAccessPolicy.java |  5 ++
 .../freemarker/ext/beans/MemberAccessPolicy.java   | 10 +++
 .../java/freemarker/ext/beans/StaticModel.java     |  2 +-
 .../java/freemarker/ext/beans/StringModel.java     |  9 ++-
 .../ext/beans/WhitelistMemberAccessPolicy.java     | 18 +++++
 .../ext/beans/DefaultMemberAccessPolicyTest.java   |  6 ++
 ...DefaultObjectWrapperMemberAccessPolicyTest.java | 81 ++++++++++++++++++++++
 .../MemberSelectorListMemberAccessPolicyTest.java  | 45 +++++++++++-
 12 files changed, 235 insertions(+), 9 deletions(-)

diff --git a/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
index dd9a1fc..5b6284f 100644
--- a/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/AllowAllMemberAccessPolicy.java
@@ -50,4 +50,9 @@ final class AllowAllMemberAccessPolicy implements 
MemberAccessPolicy {
     public ClassMemberAccessPolicy forClass(Class<?> contextClass) {
         return CLASS_POLICY_INSTANCE;
     }
+
+    @Override
+    public boolean isToStringAlwaysExposed() {
+        return true;
+    }
 }
diff --git 
a/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
index 69d0410..9c86e1b 100644
--- a/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/BlacklistMemberAccessPolicy.java
@@ -19,6 +19,7 @@
 
 package freemarker.ext.beans;
 
+import java.lang.reflect.Method;
 import java.util.Collection;
 
 /**
@@ -38,6 +39,8 @@ import java.util.Collection;
  */
 public class BlacklistMemberAccessPolicy extends 
MemberSelectorListMemberAccessPolicy {
 
+    private final boolean toStringAlwaysExposed;
+
     /**
      * @param memberSelectors
      *      List of member selectors; see {@link 
MemberSelectorListMemberAccessPolicy} class-level documentation for
@@ -45,6 +48,21 @@ public class BlacklistMemberAccessPolicy extends 
MemberSelectorListMemberAccessP
      */
     public BlacklistMemberAccessPolicy(Collection<? extends MemberSelector> 
memberSelectors) {
         super(memberSelectors, ListType.BLACKLIST, null);
+
+        boolean toStringBlacklistedAnywhere = false;
+        for (MemberSelector memberSelector : memberSelectors) {
+            Method method = memberSelector.getMethod();
+            if (method != null && method.getName().equals("toString") && 
method.getParameterTypes().length == 0) {
+                toStringBlacklistedAnywhere = true;
+                break;
+            }
+        }
+        toStringAlwaysExposed = !toStringBlacklistedAnywhere;
+    }
+
+    @Override
+    public boolean isToStringAlwaysExposed() {
+        return toStringAlwaysExposed;
     }
 
 }
diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java 
b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index aa3bafb..e92226d 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -82,6 +82,8 @@ class ClassIntrospector {
             new ExecutableMemberSignature("get", new Class[] { String.class });
     private static final ExecutableMemberSignature GET_OBJECT_SIGNATURE =
             new ExecutableMemberSignature("get", new Class[] { Object.class });
+    private static final ExecutableMemberSignature TO_STRING_SIGNATURE =
+            new ExecutableMemberSignature("toString", new Class[0]);
 
     /**
      * When this property is true, some things are stricter. This is mostly to 
catch suspicious things in development
@@ -136,6 +138,8 @@ class ClassIntrospector {
     static final Object CONSTRUCTORS_KEY = new Object();
     /** Key in the class info Map to the get(String|Object) Method */
     static final Object GENERIC_GET_KEY = new Object();
+    /** Key in the class info Map to the toString() Method */
+    static final Object TO_STRING_HIDDEN_FLAG_KEY = new Object();
 
     // 
-----------------------------------------------------------------------------------------------------------------
     // Introspection configuration properties:
@@ -272,7 +276,8 @@ class ClassIntrospector {
      */
     private Map<Object, Object> createClassIntrospectionData(Class<?> clazz) {
         final Map<Object, Object> introspData = new HashMap<Object, Object>();
-        ClassMemberAccessPolicy effClassMemberAccessPolicy = 
getEffectiveClassMemberAccessPolicy(clazz);
+        MemberAccessPolicy effMemberAccessPolicy = 
getEffectiveMemberAccessPolicy();
+        ClassMemberAccessPolicy effClassMemberAccessPolicy = 
effMemberAccessPolicy.forClass(clazz);
 
         if (exposeFields) {
             addFieldsToClassIntrospectionData(introspData, clazz, 
effClassMemberAccessPolicy);
@@ -280,6 +285,10 @@ class ClassIntrospector {
 
         final Map<ExecutableMemberSignature, List<Method>> accessibleMethods = 
discoverAccessibleMethods(clazz);
 
+        if (!effMemberAccessPolicy.isToStringAlwaysExposed()) {
+            addToStringHiddenFlagToClassIntrospectionData(introspData, 
accessibleMethods, effClassMemberAccessPolicy);
+        }
+
         addGenericGetToClassIntrospectionData(introspData, accessibleMethods, 
effClassMemberAccessPolicy);
 
         if (exposureLevel != BeansWrapper.EXPOSE_NOTHING) {
@@ -705,6 +714,19 @@ class ClassIntrospector {
         }
     }
 
+    private void addToStringHiddenFlagToClassIntrospectionData(Map<Object, 
Object> introspData,
+            Map<ExecutableMemberSignature, List<Method>> accessibleMethods,
+            ClassMemberAccessPolicy effClassMemberAccessPolicy) {
+        Method toStringMethod = getFirstAccessibleMethod(TO_STRING_SIGNATURE, 
accessibleMethods);
+        if (toStringMethod == null) {
+            throw new BugException("toString() method not found");
+        }
+        // toString() is pretty much always exposed, so we make the negative 
case to take extra memory:
+        if (!effClassMemberAccessPolicy.isMethodExposed(toStringMethod)) {
+            introspData.put(TO_STRING_HIDDEN_FLAG_KEY, true);
+        }
+    }
+
     private void addConstructorsToClassIntrospectionData(final Map<Object, 
Object> introspData,
             Class<?> clazz, ClassMemberAccessPolicy 
effClassMemberAccessPolicy) {
         try {
@@ -828,12 +850,13 @@ class ClassIntrospector {
     }
 
     /**
-     * Returns the {@link ClassMemberAccessPolicy} to actually use, which is 
not just
-     * {@link BeansWrapper#getMemberAccessPolicy()} if {@link 
BeansWrapper#getExposureLevel()} is more allowing than
-     * {@link BeansWrapper#EXPOSE_SAFE}. {@link BeansWrapper#EXPOSE_NOTHING} 
though is not factored in here.
+     * Returns the {@link MemberAccessPolicy} to actually use, which is not 
just
+     * {@link BeansWrapper#getMemberAccessPolicy()} if {@link 
BeansWrapper#getExposureLevel()} is more
+     * allowing than {@link BeansWrapper#EXPOSE_SAFE}. {@link 
BeansWrapper#EXPOSE_NOTHING} though is
+     * not factored in here.
      */
-    ClassMemberAccessPolicy getEffectiveClassMemberAccessPolicy(Class<?> 
containingClass) {
-        return exposureLevel < BeansWrapper.EXPOSE_SAFE ? 
AllowAllMemberAccessPolicy.CLASS_POLICY_INSTANCE : 
memberAccessPolicy.forClass(containingClass);
+    MemberAccessPolicy getEffectiveMemberAccessPolicy() {
+        return exposureLevel < BeansWrapper.EXPOSE_SAFE ? 
AllowAllMemberAccessPolicy.INSTANCE : memberAccessPolicy;
     }
 
     private boolean is2321Bugfixed() {
diff --git a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
index 71067c9..bc8b6d8 100644
--- a/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/DefaultMemberAccessPolicy.java
@@ -50,6 +50,7 @@ public final class DefaultMemberAccessPolicy implements 
MemberAccessPolicy {
     private final Set<Class<?>> whitelistRuleNonFinalClasses;
     private final WhitelistMemberAccessPolicy whitelistMemberAccessPolicy;
     private final BlacklistMemberAccessPolicy blacklistMemberAccessPolicy;
+    private final boolean toStringAlwaysExposed;
 
     /**
      * Returns the singleton that's compatible with the given incompatible 
improvements version.
@@ -147,6 +148,10 @@ public final class DefaultMemberAccessPolicy implements 
MemberAccessPolicy {
                 }
             }
             blacklistMemberAccessPolicy = new 
BlacklistMemberAccessPolicy(blacklistMemberSelectors);
+
+            toStringAlwaysExposed =
+                    whitelistMemberAccessPolicy.isToStringAlwaysExposed()
+                    && blacklistMemberAccessPolicy.isToStringAlwaysExposed();
         } catch (Exception e) {
             throw new IllegalStateException("Couldn't init " + 
this.getClass().getName() + " instance", e);
         }
@@ -179,6 +184,11 @@ public final class DefaultMemberAccessPolicy implements 
MemberAccessPolicy {
         }
     }
 
+    @Override
+    public boolean isToStringAlwaysExposed() {
+        return toStringAlwaysExposed;
+    }
+
     private boolean isTypeWithWhitelistRule(Class<?> contextClass) {
         if (whitelistRuleFinalClasses.contains(contextClass)) {
             return true;
diff --git 
a/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
index 13b6c41..08c7c8f 100644
--- a/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/LegacyDefaultMemberAccessPolicy.java
@@ -93,6 +93,11 @@ public final class LegacyDefaultMemberAccessPolicy 
implements MemberAccessPolicy
         return CLASS_MEMBER_ACCESS_POLICY_INSTANCE;
     }
 
+    @Override
+    public boolean isToStringAlwaysExposed() {
+        return true;
+    }
+
     private static final BlacklistClassMemberAccessPolicy 
CLASS_MEMBER_ACCESS_POLICY_INSTANCE
             = new BlacklistClassMemberAccessPolicy();
     private static class BlacklistClassMemberAccessPolicy implements 
ClassMemberAccessPolicy {
diff --git a/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
index 400f1ce..2ca568d 100644
--- a/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/MemberAccessPolicy.java
@@ -70,4 +70,14 @@ public interface MemberAccessPolicy {
      *      The exact class of object from which members will be get in the 
templates.
      */
     ClassMemberAccessPolicy forClass(Class<?> contextClass);
+
+    /**
+     * If this returns {@code true}, we won't invoke the probably more 
expensive lookup to figure out if
+     * {@link Object#toString()} (including its overridden variants) is 
exposed for a given object. If this returns
+     * {@code false}, then no such optimization is made. This method was 
introduced as {@link Object#toString()} is
+     * called frequently, as it's used whenever an object is converted to 
string, like printed to the output, and it's
+     * not even a reflection-based call (we just call {@link 
Object#toString()} in Java). So we try to avoid the
+     * overhead of a more generic method call.
+     */
+    boolean isToStringAlwaysExposed();
 }
diff --git a/src/main/java/freemarker/ext/beans/StaticModel.java 
b/src/main/java/freemarker/ext/beans/StaticModel.java
index cd1bf97..8e772aa 100644
--- a/src/main/java/freemarker/ext/beans/StaticModel.java
+++ b/src/main/java/freemarker/ext/beans/StaticModel.java
@@ -107,7 +107,7 @@ final class StaticModel implements TemplateHashModelEx {
         }
 
         ClassMemberAccessPolicy effClassMemberAccessPolicy =
-                
wrapper.getClassIntrospector().getEffectiveClassMemberAccessPolicy(clazz);
+                
wrapper.getClassIntrospector().getEffectiveMemberAccessPolicy().forClass(clazz);
 
         Field[] fields = clazz.getFields();
         for (Field field : fields) {
diff --git a/src/main/java/freemarker/ext/beans/StringModel.java 
b/src/main/java/freemarker/ext/beans/StringModel.java
index 719e899..37a4923 100644
--- a/src/main/java/freemarker/ext/beans/StringModel.java
+++ b/src/main/java/freemarker/ext/beans/StringModel.java
@@ -39,6 +39,9 @@ implements TemplateScalarModel {
             }
         };
 
+    // Package visible for testing
+    static final String TO_STRING_NOT_EXPOSED = "[toString not exposed]";
+
     /**
      * Creates a new model that wraps the specified object with BeanModel + 
scalar
      * functionality.
@@ -56,7 +59,11 @@ implements TemplateScalarModel {
      * Returns the result of calling {@link Object#toString()} on the wrapped
      * object.
      */
+    @Override
     public String getAsString() {
-        return object.toString();
+        boolean exposeToString = 
wrapper.getMemberAccessPolicy().isToStringAlwaysExposed()
+                || !wrapper.getClassIntrospector().get(object.getClass())
+                        
.containsKey(ClassIntrospector.TO_STRING_HIDDEN_FLAG_KEY);
+        return exposeToString ? object.toString() : TO_STRING_NOT_EXPOSED;
     }
 }
diff --git 
a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java 
b/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
index f024d37..e72f7bf 100644
--- a/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
+++ b/src/main/java/freemarker/ext/beans/WhitelistMemberAccessPolicy.java
@@ -19,6 +19,7 @@
 
 package freemarker.ext.beans;
 
+import java.lang.reflect.Method;
 import java.util.Collection;
 
 import freemarker.template.ObjectWrapper;
@@ -42,6 +43,17 @@ import freemarker.template.ObjectWrapper;
  */
 public class WhitelistMemberAccessPolicy extends 
MemberSelectorListMemberAccessPolicy {
 
+    private static final Method TO_STRING_METHOD;
+    static {
+        try {
+            TO_STRING_METHOD = Object.class.getMethod("toString");
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private final boolean toStringAlwaysExposed;
+
     /**
      * @param memberSelectors
      *      List of member selectors; see {@link 
MemberSelectorListMemberAccessPolicy} class-level documentation for
@@ -49,6 +61,12 @@ public class WhitelistMemberAccessPolicy extends 
MemberSelectorListMemberAccessP
      */
     public WhitelistMemberAccessPolicy(Collection<? extends MemberSelector> 
memberSelectors) {
         super(memberSelectors, ListType.WHITELIST, TemplateAccessible.class);
+        toStringAlwaysExposed = 
forClass(Object.class).isMethodExposed(TO_STRING_METHOD);
+    }
+
+    @Override
+    public boolean isToStringAlwaysExposed() {
+        return toStringAlwaysExposed;
     }
 
 }
diff --git 
a/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java 
b/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
index 324abd1..4128151 100644
--- a/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
+++ b/src/test/java/freemarker/ext/beans/DefaultMemberAccessPolicyTest.java
@@ -81,6 +81,12 @@ public class DefaultMemberAccessPolicyTest {
     }
 
     @Test
+    public void testToString() throws NoSuchMethodException {
+        assertTrue(POLICY.isToStringAlwaysExposed());
+        
assertTrue(POLICY.forClass(UserClass.class).isMethodExposed(Object.class.getMethod("toString")));
+    }
+
+    @Test
     public void testWellKnownUnsafeMethodsAreBanned() throws 
NoSuchMethodException {
         {
             ClassMemberAccessPolicy classPolicy = POLICY.forClass(Class.class);
diff --git 
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
 
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
index 0938541..2c5ff54 100644
--- 
a/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
+++ 
b/src/test/java/freemarker/ext/beans/DefaultObjectWrapperMemberAccessPolicyTest.java
@@ -48,6 +48,7 @@ import freemarker.template.TemplateMethodModelEx;
 import freemarker.template.TemplateModel;
 import freemarker.template.TemplateModelException;
 import freemarker.template.TemplateNumberModel;
+import freemarker.template.TemplateScalarModel;
 
 public class DefaultObjectWrapperMemberAccessPolicyTest {
 
@@ -247,6 +248,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -286,6 +292,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -315,6 +326,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -342,6 +358,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -386,6 +407,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -429,6 +455,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
 
@@ -478,6 +509,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                     }
                 };
             }
+
+            @Override
+            public boolean isToStringAlwaysExposed() {
+                return true;
+            }
         });
         DefaultObjectWrapper ow = owb.build();
         TemplateHashModel statics = (TemplateHashModel) 
ow.getStaticModels().get(Statics.class.getName());
@@ -530,6 +566,41 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
         }
     }
 
+    @Test
+    public void testToString1() throws TemplateException, 
NoSuchMethodException, NoSuchFieldException,
+            ClassNotFoundException {
+        DefaultObjectWrapperBuilder owb = new 
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+        owb.setMemberAccessPolicy(
+                new WhitelistMemberAccessPolicy(
+                        
MemberSelectorListMemberAccessPolicy.MemberSelector.parse(
+                                
Collections.singleton(CExtended.class.getName() + ".toString()"),
+                                false,
+                                
DefaultObjectWrapperMemberAccessPolicyTest.class.getClassLoader()
+                        )
+                )
+        );
+        DefaultObjectWrapper ow = owb.build();
+
+        assertEquals(StringModel.TO_STRING_NOT_EXPOSED, ((TemplateScalarModel) 
ow.wrap(new C())).getAsString());
+        assertEquals(CExtended.class.getSimpleName(), ((TemplateScalarModel) 
ow.wrap(new CExtended())).getAsString());
+    }
+
+    @Test
+    public void testToString2() throws TemplateException {
+        for (MemberAccessPolicy policy :
+                new MemberAccessPolicy[] {
+                        
DefaultMemberAccessPolicy.getInstance(Configuration.VERSION_2_3_30),
+                        LegacyDefaultMemberAccessPolicy.INSTANCE
+                }) {
+            DefaultObjectWrapperBuilder owb = new 
DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30);
+            owb.setMemberAccessPolicy(policy);
+            DefaultObjectWrapper ow = owb.build();
+
+            assertEquals(
+                    C.class.getSimpleName(), ((TemplateScalarModel) 
ow.wrap(new C())).getAsString());
+        }
+    }
+
     private static Object getHashValue(ObjectWrapperAndUnwrapper ow, 
TemplateHashModel objM, String key)
             throws TemplateModelException {
         return ow.unwrap(objM.get(key));
@@ -599,6 +670,11 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
         }
 
         public static void M1() { }
+
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName();
+        }
     }
 
     public static class CExtended extends C {
@@ -678,5 +754,10 @@ public class DefaultObjectWrapperMemberAccessPolicyTest {
                 }
             };
         }
+
+        @Override
+        public boolean isToStringAlwaysExposed() {
+            return false;
+        }
     }
 }
diff --git 
a/src/test/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicyTest.java
 
b/src/test/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicyTest.java
index 2ba2458..861b5b8 100644
--- 
a/src/test/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicyTest.java
+++ 
b/src/test/java/freemarker/ext/beans/MemberSelectorListMemberAccessPolicyTest.java
@@ -314,7 +314,7 @@ public class MemberSelectorListMemberAccessPolicyTest {
     }
 
     @Test
-    public void testBlacklistIgnoredAnnotation() throws NoSuchMethodException, 
NoSuchFieldException {
+    public void testBlacklistIgnoresAnnotation() throws NoSuchMethodException, 
NoSuchFieldException {
         BlacklistMemberAccessPolicy policy = newBlacklistMemberAccessPolicy(
                 CAnnotationsTest1.class.getName() + ".m5()",
                 CAnnotationsTest1.class.getName() + ".f5",
@@ -328,6 +328,49 @@ public class MemberSelectorListMemberAccessPolicyTest {
     }
 
     @Test
+    public void testBlacklistAndToString() throws NoSuchMethodException {
+        {
+            BlacklistMemberAccessPolicy policy = 
newBlacklistMemberAccessPolicy(
+                    C1.class.getName() + ".m1()",
+                    C1.class.getName() + ".m2()"
+            );
+            assertTrue(policy.isToStringAlwaysExposed());
+            
assertTrue(policy.forClass(C1.class).isMethodExposed(Object.class.getMethod("toString")));
+        }
+        {
+            BlacklistMemberAccessPolicy policy = 
newBlacklistMemberAccessPolicy(
+                    C1.class.getName() + ".m1()",
+                    C2.class.getName() + ".toString()",
+                    C1.class.getName() + ".m2()"
+            );
+            assertFalse(policy.isToStringAlwaysExposed());
+            
assertTrue(policy.forClass(C1.class).isMethodExposed(Object.class.getMethod("toString")));
+            
assertFalse(policy.forClass(C2.class).isMethodExposed(Object.class.getMethod("toString")));
+            
assertFalse(policy.forClass(C3.class).isMethodExposed(Object.class.getMethod("toString")));
+        }
+    }
+
+    @Test
+    public void testWhitelistAndToString() throws NoSuchMethodException {
+        {
+            WhitelistMemberAccessPolicy policy = 
newWhitelistMemberAccessPolicy(
+                    C2.class.getName() + ".toString()"
+            );
+            assertFalse(policy.isToStringAlwaysExposed());
+            
assertFalse(policy.forClass(C1.class).isMethodExposed(Object.class.getMethod("toString")));
+            
assertTrue(policy.forClass(C2.class).isMethodExposed(Object.class.getMethod("toString")));
+            
assertTrue(policy.forClass(C3.class).isMethodExposed(Object.class.getMethod("toString")));
+        }
+        {
+            WhitelistMemberAccessPolicy policy = 
newWhitelistMemberAccessPolicy(
+                    Object.class.getName() + ".toString()"
+            );
+            assertTrue(policy.isToStringAlwaysExposed());
+            
assertTrue(policy.forClass(C1.class).isMethodExposed(Object.class.getMethod("toString")));
+        }
+    }
+
+    @Test
     public void memberSelectorParserIgnoresWhitespace() throws 
NoSuchMethodException {
         WhitelistMemberAccessPolicy policy = newWhitelistMemberAccessPolicy(
                 (CArrayArgs.class.getName() + 
".m1(java.lang.String)").replace(".", "\n\t. "),

Reply via email to