Repository: PebbleTemplates/pebble
Pull request #473 (2019-09-06 15:25:20)

#454: Use a list of unsafeMethods and rename allowGetClass to allowUn…
…safeMethods


Patch:
diff --git a/docs/src/orchid/resources/wiki/guide/installation.md b/docs/src/orchid/resources/wiki/guide/installation.md
index bb05b2de..d922580c 100644
--- a/docs/src/orchid/resources/wiki/guide/installation.md
+++ b/docs/src/orchid/resources/wiki/guide/installation.md
@@ -84,6 +84,6 @@ All the settings are set during the construction of the `PebbleEngine` object.
 | `executorService` | An `ExecutorService` that allows the usage of some advanced multithreading features, such as the `parallel` tag. | `null` |
 | `loader` | An implementation of the `Loader` interface which is used to find templates. | An implementation of the `DelegatingLoader` which uses a `ClasspathLoader` and a `FileLoader` behind the scenes. |
 | `strictVariables` | If set to true, Pebble will throw an exception if you try to access a variable or attribute that does not exist (or an attribute of a null variable). If set to false, your template will treat non-existing variables/attributes as null without ever skipping a beat. | `false` |
-| `allowGetClass` | If set to false, Pebble will throw an exception if you try to access the class/getClass attribute. | `true` in 2.x, 'false' in v3.x |
+| `allowUnsafeMethods` | If set to false, Pebble will throw an exception if you try to access unsafe methods. Unsafe methods are defined [here](https://github.com/PebbleTemplates/pebble/tree/master/pebble/src/main/resources/unsafeMethods.properties) | `true` in 2.x, 'false' in v3.x |
 | `literalDecimalTreatedAsInteger` | option for toggling to enable/disable literal decimal treated as integer | `false` |
 | `greedyMatchMethod` | option for toggling to enable/disable greedy matching mode for finding java method. Reduce the limit of the parameter type, try to find other method which has compatible parameter types. | `false` |
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java b/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java
index 4f62f942..442aaa1f 100644
--- a/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/PebbleEngine.java
@@ -39,10 +39,10 @@ import com.mitchellbosecke.pebble.parser.ParserOptions;
 import com.mitchellbosecke.pebble.template.EvaluationOptions;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
 import com.mitchellbosecke.pebble.template.PebbleTemplateImpl;
+
 import java.io.IOException;
 import java.io.Reader;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.ExecutorService;
@@ -276,7 +276,7 @@ public class PebbleEngine {
 
     private EscaperExtension escaperExtension = new EscaperExtension();
 
-    private boolean allowGetClass;
+    private boolean allowUnsafeMethods;
 
     private boolean literalDecimalTreatedAsInteger = false;
 
@@ -470,13 +470,13 @@ public class PebbleEngine {
     }
 
     /**
-     * Enable/disable getClass access for attributes
+     * Enable/disable unsafe methods access for attributes
      *
-     * @param allowGetClass toggle to enable/disable getClass access
+     * @param allowUnsafeMethods toggle to enable/disable unsafe methods access
      * @return This builder object
      */
-    public Builder allowGetClass(boolean allowGetClass) {
-      this.allowGetClass = allowGetClass;
+    public Builder allowUnsafeMethods(boolean allowUnsafeMethods) {
+      this.allowUnsafeMethods = allowUnsafeMethods;
       return this;
     }
 
@@ -525,7 +525,7 @@ public class PebbleEngine {
      */
     public PebbleEngine build() {
 
-      ExtensionRegistry extensionRegistry = buildExtensionRegistry();
+      ExtensionRegistry extensionRegistry = this.buildExtensionRegistry();
 
       // default loader
       if (this.loader == null) {
@@ -563,7 +563,7 @@ public class PebbleEngine {
       parserOptions.setLiteralDecimalTreatedAsInteger(this.literalDecimalTreatedAsInteger);
 
       EvaluationOptions evaluationOptions = new EvaluationOptions();
-      evaluationOptions.setAllowGetClass(this.allowGetClass);
+      evaluationOptions.setAllowUnsafeMethods(this.allowUnsafeMethods);
       evaluationOptions.setGreedyMatchMethod(this.greedyMatchMethod);
 
       return new PebbleEngine(this.loader, this.syntax, this.strictVariables, this.defaultLocale,
@@ -579,7 +579,7 @@ public class PebbleEngine {
       extensionRegistry.addExtension(new I18nExtension());
 
       for (Extension userProvidedExtension : this.userProvidedExtensions) {
-        if (allowOverrideCoreOperators) {
+        if (this.allowOverrideCoreOperators) {
           extensionRegistry.addOperatorOverridingExtension(userProvidedExtension);
         } else {
           extensionRegistry.addExtension(userProvidedExtension);
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/MemberCacheUtils.java b/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/MemberCacheUtils.java
index b3719b31..0068eee0 100644
--- a/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/MemberCacheUtils.java
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/MemberCacheUtils.java
@@ -3,6 +3,7 @@ package com.mitchellbosecke.pebble.attributes;
 import com.mitchellbosecke.pebble.error.ClassAccessException;
 import com.mitchellbosecke.pebble.template.EvaluationContextImpl;
 import com.mitchellbosecke.pebble.template.EvaluationOptions;
+
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
@@ -15,7 +16,7 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 class MemberCacheUtils {
-
+  private final UnsafeMethods unsafeMethods = new UnsafeMethods();
   private final ConcurrentHashMap<MemberCacheKey, Member> memberCache = new ConcurrentHashMap<>(100,
       0.9f, 1);
 
@@ -111,10 +112,6 @@ class MemberCacheUtils {
    */
   private Method findMethod(Class<?> clazz, String name, Class<?>[] requiredTypes, String filename,
       int lineNumber, EvaluationOptions evaluationOptions) {
-    if (!evaluationOptions.isAllowGetClass() && name.equalsIgnoreCase("getClass")) {
-      throw new ClassAccessException(lineNumber, filename);
-    }
-
     List<Method> candidates = this.getCandidates(clazz, name, requiredTypes);
 
     // perfect match
@@ -149,6 +146,7 @@ class MemberCacheUtils {
       }
     }
     if(bestMatch != null) {
+      this.verifyUnsafeMethod(filename, lineNumber, evaluationOptions, bestMatch);
       return bestMatch;
     }
 
@@ -165,6 +163,7 @@ class MemberCacheUtils {
         }
 
         if (compatibleTypes) {
+          this.verifyUnsafeMethod(filename, lineNumber, evaluationOptions, candidate);
           return candidate;
         }
       }
@@ -173,6 +172,12 @@ class MemberCacheUtils {
     return null;
   }
 
+  private void verifyUnsafeMethod(String filename, int lineNumber, EvaluationOptions evaluationOptions, Method method) {
+    if (!evaluationOptions.isAllowUnsafeMethods() && this.unsafeMethods.isUnsafeMethod(method)) {
+      throw new ClassAccessException(lineNumber, filename);
+    }
+  }
+
   /**
    * Performs a widening conversion (primitive to boxed type)
    */
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/UnsafeMethods.java b/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/UnsafeMethods.java
new file mode 100644
index 00000000..3bafa54d
--- /dev/null
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/attributes/UnsafeMethods.java
@@ -0,0 +1,86 @@
+package com.mitchellbosecke.pebble.attributes;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+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;
+
+class UnsafeMethods {
+
+  private static final String UNSAFE_METHODS_PROPERTIES = "/unsafeMethods.properties";
+  private static final Set<Method> UNSAFE_METHODS = createUnsafeMethodsSet();
+
+  UnsafeMethods() { }
+
+  boolean isUnsafeMethod(Method method) {
+    return UNSAFE_METHODS.contains(method);
+  }
+
+  private static final Set<Method> createUnsafeMethodsSet() {
+    try {
+      Properties props = loadProperties(UNSAFE_METHODS_PROPERTIES);
+      Set<Method> set = new HashSet<>(props.size() * 4 / 3, 1f);
+      Map<String, Class> primClasses = createPrimitiveClassesMap();
+      for (Object key : props.keySet()) {
+        set.add(parseMethodSpec((String) key, primClasses));
+      }
+      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 = Class.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] = Class.forName(argClassName);
+      }
+    }
+    return clazz.getMethod(methodName, argTypes);
+  }
+
+  private static Map<String, Class> createPrimitiveClassesMap() {
+    Map<String, Class> map = new HashMap<>();
+    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 Properties loadProperties(String resource) throws IOException {
+    Properties props = new Properties();
+    InputStream is = null;
+    try {
+      is = UnsafeMethods.class.getResourceAsStream(resource);
+      props.load(is);
+    } finally {
+      if (is != null) {
+        is.close();
+      }
+    }
+    return props;
+  }
+}
\ No newline at end of file
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/template/EvaluationOptions.java b/pebble/src/main/java/com/mitchellbosecke/pebble/template/EvaluationOptions.java
index 4b5423ee..412ce052 100644
--- a/pebble/src/main/java/com/mitchellbosecke/pebble/template/EvaluationOptions.java
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/template/EvaluationOptions.java
@@ -8,26 +8,26 @@ package com.mitchellbosecke.pebble.template;
 public class EvaluationOptions {
 
   /**
-   * toggle to enable/disable getClass access
+   * toggle to enable/disable unsafe methods access
    */
-  private boolean allowGetClass;
+  private boolean allowUnsafeMethods;
 
   /**
    * toggle to enable/disable greedy matching mode for finding java method
    */
   private boolean greedyMatchMethod;
 
-  public boolean isAllowGetClass() {
-    return allowGetClass;
+  public boolean isAllowUnsafeMethods() {
+    return this.allowUnsafeMethods;
   }
 
-  public EvaluationOptions setAllowGetClass(boolean allowGetClass) {
-    this.allowGetClass = allowGetClass;
+  public EvaluationOptions setAllowUnsafeMethods(boolean allowUnsafeMethods) {
+    this.allowUnsafeMethods = allowUnsafeMethods;
     return this;
   }
 
   public boolean isGreedyMatchMethod() {
-    return greedyMatchMethod;
+    return this.greedyMatchMethod;
   }
 
   public EvaluationOptions setGreedyMatchMethod(boolean greedyMatchMethod) {
diff --git a/pebble/src/main/resources/unsafeMethods.properties b/pebble/src/main/resources/unsafeMethods.properties
new file mode 100644
index 00000000..095e029e
--- /dev/null
+++ b/pebble/src/main/resources/unsafeMethods.properties
@@ -0,0 +1,96 @@
+# 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.
+
+java.lang.Object.wait()
+java.lang.Object.wait(long)
+java.lang.Object.wait(long,int)
+java.lang.Object.notify()
+java.lang.Object.notifyAll()
+java.lang.Object.getClass()
+
+java.lang.Class.getClassLoader()
+java.lang.Class.newInstance()
+java.lang.Class.forName(java.lang.String)
+java.lang.Class.forName(java.lang.String,boolean,java.lang.ClassLoader)
+
+java.lang.reflect.Constructor.newInstance([Ljava.lang.Object;)
+
+java.lang.reflect.Method.invoke(java.lang.Object,[Ljava.lang.Object;)
+
+java.lang.reflect.Field.set(java.lang.Object,java.lang.Object)
+java.lang.reflect.Field.setBoolean(java.lang.Object,boolean)
+java.lang.reflect.Field.setByte(java.lang.Object,byte)
+java.lang.reflect.Field.setChar(java.lang.Object,char)
+java.lang.reflect.Field.setDouble(java.lang.Object,double)
+java.lang.reflect.Field.setFloat(java.lang.Object,float)
+java.lang.reflect.Field.setInt(java.lang.Object,int)
+java.lang.reflect.Field.setLong(java.lang.Object,long)
+java.lang.reflect.Field.setShort(java.lang.Object,short)
+
+java.lang.reflect.AccessibleObject.setAccessible([Ljava.lang.reflect.AccessibleObject;,boolean)
+java.lang.reflect.AccessibleObject.setAccessible(boolean)
+
+java.lang.Thread.getContextClassLoader()
+java.lang.Thread.interrupt()
+java.lang.Thread.join()
+java.lang.Thread.join(long)
+java.lang.Thread.join(long,int)
+java.lang.Thread.resume()
+java.lang.Thread.run()
+java.lang.Thread.setContextClassLoader(java.lang.ClassLoader)
+java.lang.Thread.setDaemon(boolean)
+java.lang.Thread.setName(java.lang.String)
+java.lang.Thread.setPriority(int)
+java.lang.Thread.sleep(long)
+java.lang.Thread.sleep(long,int)
+java.lang.Thread.start()
+java.lang.Thread.stop()
+java.lang.Thread.suspend()
+
+java.lang.ThreadGroup.allowThreadSuspension(boolean)
+java.lang.ThreadGroup.destroy()
+java.lang.ThreadGroup.interrupt()
+java.lang.ThreadGroup.resume()
+java.lang.ThreadGroup.setDaemon(boolean)
+java.lang.ThreadGroup.setMaxPriority(int)
+java.lang.ThreadGroup.stop()
+java.lang.Thread.suspend()
+
+java.lang.Runtime.addShutdownHook(java.lang.Thread)
+java.lang.Runtime.exec(java.lang.String)
+java.lang.Runtime.exec([Ljava.lang.String;)
+java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;)
+java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;,java.io.File)
+java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;)
+java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;,java.io.File)
+java.lang.Runtime.exit(int)
+java.lang.Runtime.halt(int)
+java.lang.Runtime.load(java.lang.String)
+java.lang.Runtime.loadLibrary(java.lang.String)
+java.lang.Runtime.removeShutdownHook(java.lang.Thread)
+java.lang.Runtime.traceInstructions(boolean)
+java.lang.Runtime.traceMethodCalls(boolean)
+
+java.lang.System.exit(int)
+java.lang.System.load(java.lang.String)
+java.lang.System.loadLibrary(java.lang.String)
+java.lang.System.setErr(java.io.PrintStream)
+java.lang.System.setIn(java.io.InputStream)
+java.lang.System.setOut(java.io.PrintStream)
+java.lang.System.setProperties(java.util.Properties)
+java.lang.System.setProperty(java.lang.String,java.lang.String)
+java.lang.System.setSecurityManager(java.lang.SecurityManager)
\ No newline at end of file


Test patch:
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java b/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
index dffa2dae..7287d8c6 100644
--- a/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
@@ -8,15 +8,15 @@
  */
 package com.mitchellbosecke.pebble;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
 import com.mitchellbosecke.pebble.error.AttributeNotFoundException;
 import com.mitchellbosecke.pebble.error.ClassAccessException;
 import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.error.RootAttributeNotFoundException;
 import com.mitchellbosecke.pebble.loader.StringLoader;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
+
+import org.junit.Test;
+
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
@@ -25,7 +25,9 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 public class GetAttributeTest {
 
@@ -192,23 +194,22 @@ public class GetAttributeTest {
    * Make sure we are properly accounting for getting the class object from an Object in all
    * situations:
    *
-   * | AllowGetClass | Strict Variables | Access Type | Result  |
-   * | ------------- | ---------------- | ----------- | ------- |
-   * | true          | false            | property    | allowed |
-   * | true          | false            | method      | allowed |
-   * | true          | true             | property    | allowed |
-   * | true          | true             | method      | allowed |
-   * | false         | false            | property    | throw   |
-   * | false         | false            | method      | throw   |
-   * | false         | true             | property    | throw   |
-   * | false         | true             | method      | throw   |
+   * | AllowUnsafeMethods | Strict Variables | Access Type | Result  |
+   * | ------------------ | ---------------- | ----------- | ------- |
+   * | true               | false            | property    | allowed |
+   * | true               | false            | method      | allowed |
+   * | true               | true             | property    | allowed |
+   * | true               | true             | method      | allowed |
+   * | false              | false            | property    | throw   |
+   * | false              | false            | method      | throw   |
+   * | false              | true             | property    | throw   |
+   * | false              | true             | method      | throw   |
    */
-
   @Test
-  public void testAccessingClass_AllowGetClassOn_StrictVariableOff_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOn_StrictVariableOff_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(true)
+        .allowUnsafeMethods(true)
         .strictVariables(false)
         .build();
 
@@ -222,10 +223,10 @@ public class GetAttributeTest {
   }
 
   @Test
-  public void testAccessingClass_AllowGetClassOn_StrictVariableOff_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOn_StrictVariableOff_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(true)
+        .allowUnsafeMethods(true)
         .strictVariables(false)
         .build();
 
@@ -239,10 +240,10 @@ public class GetAttributeTest {
   }
 
   @Test
-  public void testAccessingClass_AllowGetClassOn_StrictVariableOn_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOn_StrictVariableOn_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(true)
+        .allowUnsafeMethods(true)
         .strictVariables(true)
         .build();
 
@@ -256,10 +257,10 @@ public class GetAttributeTest {
   }
 
   @Test
-  public void testAccessingClass_AllowGetClassOn_StrictVariableOn_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOn_StrictVariableOn_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(true)
+        .allowUnsafeMethods(true)
         .strictVariables(true)
         .build();
 
@@ -273,10 +274,10 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOff_StrictVariableOff_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOff_StrictVariableOff_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(false)
+        .allowUnsafeMethods(false)
         .strictVariables(false)
         .build();
 
@@ -289,10 +290,10 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOff_StrictVariableOff_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOff_StrictVariableOff_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(false)
+        .allowUnsafeMethods(false)
         .strictVariables(false)
         .build();
 
@@ -305,10 +306,10 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOff_StrictVariableOn_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOff_StrictVariableOn_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(false)
+        .allowUnsafeMethods(false)
         .strictVariables(true)
         .build();
 
@@ -321,10 +322,10 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOff_StrictVariableOn_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOff_StrictVariableOn_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
-        .allowGetClass(false)
+        .allowUnsafeMethods(false)
         .strictVariables(true)
         .build();
 
@@ -337,11 +338,11 @@ public class GetAttributeTest {
   }
 
   @Test
-  public void testAccessingClass_AllowGetClassOnIsCaseInsensitive_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOnIsCaseInsensitive_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder()
       .loader(new StringLoader())
-      .allowGetClass(true)
+        .allowUnsafeMethods(true)
       .build();
 
     PebbleTemplate template = pebble.getTemplate("hello [{{ object.ClAsS }}]");
@@ -354,11 +355,11 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOffIsCaseInsensitive_Property()
+  public void testAccessingClass_AllowUnsafeMethodsOffIsCaseInsensitive_Property()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder()
       .loader(new StringLoader())
-      .allowGetClass(false)
+        .allowUnsafeMethods(false)
       .build();
 
     PebbleTemplate template = pebble.getTemplate("hello [{{ object.ClAsS }}]");
@@ -370,11 +371,11 @@ public class GetAttributeTest {
   }
 
   @Test
-  public void testAccessingClass_AllowGetClassOnIsCaseInsensitive_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOnIsCaseInsensitive_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder()
       .loader(new StringLoader())
-      .allowGetClass(true)
+        .allowUnsafeMethods(true)
       .build();
 
     PebbleTemplate template = pebble.getTemplate("hello [{{ object.GeTcLAsS() }}]");
@@ -387,11 +388,11 @@ public class GetAttributeTest {
   }
 
   @Test(expected = ClassAccessException.class)
-  public void testAccessingClass_AllowGetClassOffIsCaseInsensitive_Method()
+  public void testAccessingClass_AllowUnsafeMethodsOffIsCaseInsensitive_Method()
       throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder()
       .loader(new StringLoader())
-      .allowGetClass(false)
+        .allowUnsafeMethods(false)
       .build();
 
     PebbleTemplate template = pebble.getTemplate("hello [{{ object.GeTcLAsS() }}]");
@@ -402,6 +403,22 @@ public class GetAttributeTest {
     template.evaluate(writer, context);
   }
 
+  @Test(expected = ClassAccessException.class)
+  public void testAccessingClass_AllowUnsafeMethodsOffForMethodNotify_thenThrowException()
+      throws PebbleException, IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder()
+        .loader(new StringLoader())
+        .allowUnsafeMethods(false)
+        .build();
+
+    PebbleTemplate template = pebble.getTemplate("hello [{{ object.notify() }}]");
+    Map<String, Object> context = new HashMap<>();
+    context.put("object", new SimpleObject());
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+  }
+
   /**
    * The GetAttribute expression involves caching, we test with different objects to make sure that
    * the caching doesnt have any negative side effects.
@@ -968,7 +985,7 @@ public class GetAttributeTest {
     }
 
     public String getStringFromLongs(Long first, long second) {
-      return String.valueOf(first) + " " + String.valueOf(second);
+      return first + " " + second;
     }
 
     public String getStringFromBoolean(boolean bool) {