This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch 3 in repository https://gitbox.apache.org/repos/asf/freemarker.git
commit aad5477771df4a7e375945c584bca644fb9f2721 Author: ddekany <[email protected]> AuthorDate: Sat Jan 11 19:27:35 2020 +0100 Forward ported from 2.3-gae: FREEMARKER-124: Changed DefaultMemberAccessPolicy to block more methods. 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 --- .../apache/freemarker/core/ConfigurationTest.java | 28 + .../model/impl/DefaultMemberAccessPolicyTest.java | 143 +++++ ...DefaultObjectWrapperMemberAccessPolicyTest.java | 208 +++++--- ...ava => MemberSelectorListAccessPolicyTest.java} | 116 +++- .../freemarker/core/ProcessingConfiguration.java | 6 + .../model/impl/BlacklistMemberAccessPolicy.java | 48 ++ .../core/model/impl/ConstructorMatcher.java | 5 + .../core/model/impl/DefaultMemberAccessPolicy.java | 204 +++++--- .../freemarker/core/model/impl/FieldMatcher.java | 5 + .../core/model/impl/MemberAccessPolicy.java | 23 +- .../freemarker/core/model/impl/MemberMatcher.java | 14 +- ...a => MemberSelectorListMemberAccessPolicy.java} | 127 +++-- .../freemarker/core/model/impl/MethodMatcher.java | 5 + .../model/impl/WhitelistMemberAccessPolicy.java | 388 +------------- .../model/impl/DefaultMemberAccessPolicy-rules | 582 +++++++++++++++++++++ .../core/model/impl/unsafeMethods.properties | 98 ---- 16 files changed, 1289 insertions(+), 711 deletions(-) diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java index 708bae8..56d5390 100644 --- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java @@ -25,6 +25,7 @@ import static org.apache.freemarker.test.hamcerst.Matchers.*; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; @@ -46,10 +47,14 @@ import java.util.TimeZone; import java.util.TreeSet; import org.apache.freemarker.core.Configuration.*; +import org.apache.freemarker.core.model.TemplateHashModel; import org.apache.freemarker.core.model.TemplateStringModel; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.model.impl.MemberAccessPolicy; +import org.apache.freemarker.core.model.impl.MemberSelectorListMemberAccessPolicy; import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper; import org.apache.freemarker.core.model.impl.SimpleString; +import org.apache.freemarker.core.model.impl.WhitelistMemberAccessPolicy; import org.apache.freemarker.core.outputformat.MarkupOutputFormat; import org.apache.freemarker.core.outputformat.OutputFormat; import org.apache.freemarker.core.outputformat.UnregisteredOutputFormatException; @@ -728,6 +733,29 @@ public class ConfigurationTest { assertEquals(1000L * 60 * 60 * 2, (Object) cfgB.getTemplateUpdateDelayMilliseconds()); } + 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.Builder(Configuration.VERSION_3_0_0) + .setting( + "objectWrapper", + "DefaultObjectWrapper(3.0.0, " + + "memberAccessPolicy=" + + ConfigurationTest.class.getName() + ".CONFIG_TEST_MEMBER_ACCESS_POLICY" + + ")") + .build(); + TemplateHashModel m = (TemplateHashModel) cfg.getObjectWrapper().wrap(new File("x")); + assertNotNull(m.get("getName")); + assertNotNull(m.get("isFile")); + assertNull(m.get("delete")); + } + @Test public void testGetSettingNamesAreSorted() throws Exception { List<String> names = new ArrayList<>(Builder.getSettingNames()); diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicyTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicyTest.java new file mode 100644 index 0000000..42cd905 --- /dev/null +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/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 org.apache.freemarker.core.model.impl; + +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.apache.freemarker.core.Configuration; +import org.junit.Test; + +public class DefaultMemberAccessPolicyTest { + + private static final DefaultMemberAccessPolicy POLICY = + DefaultMemberAccessPolicy.getInstance(Configuration.VERSION_3_0_0); + + @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/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperMemberAccessPolicyTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperMemberAccessPolicyTest.java index 7d4826c..4747f3e 100644 --- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperMemberAccessPolicyTest.java +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperMemberAccessPolicyTest.java @@ -43,16 +43,18 @@ import com.google.common.collect.ImmutableMap; public class DefaultObjectWrapperMemberAccessPolicyTest { + private final DefaultObjectWrapper dow + = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build(); + @Test public void testMethodsWithDefaultMemberAccessPolicy() throws TemplateException { - 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")); @@ -60,12 +62,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 (TemplateException e) { assertThat(e.getMessage(), containsString("wait(int)")); @@ -74,8 +73,7 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { @Test public void testFieldsWithDefaultMemberAccessPolicy() throws TemplateException { - DefaultObjectWrapper ow = createDefaultMemberAccessPolicyObjectWrapper(); - TemplateHashModel objM = (TemplateHashModel) ow.wrap(new C()); + TemplateHashModel objM = (TemplateHashModel) dow.wrap(new C()); assertFieldsNotExposed(objM); } @@ -89,34 +87,39 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { assertNull(objM.get("nonPublicField1")); assertNull(objM.get("nonPublicField2")); - // Strangely, public static fields are banned historically, while static methods aren't. + // Strangely, static fields are banned historically, while static methods aren't. assertNull(objM.get("STATIC_FIELD")); } @Test public void testGenericGetWithDefaultMemberAccessPolicy() throws TemplateException { - DefaultObjectWrapper ow = createDefaultMemberAccessPolicyObjectWrapper(); + TemplateHashModel objM = (TemplateHashModel) dow.wrap(new CWithGenericGet()); - TemplateHashModel objM = (TemplateHashModel) ow.wrap(new CWithGenericGet()); + assertEquals("get(x)", getHashValue(dow, objM, "x")); + } + + @Test + public void testBlacklistRuleWithDefaultMemberAccessPolicy() throws TemplateException { + TemplateHashModel objM = (TemplateHashModel) dow.wrap(new CThread()); - assertEquals("get(x)", getHashValue(ow, objM, "x")); + 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 TemplateException { - DefaultObjectWrapper ow = createDefaultMemberAccessPolicyObjectWrapper(); - assertNonPublicConstructorNotExposed(ow); + assertNonPublicConstructorNotExposed(dow); - assertEquals(CWithConstructor.class, - ow.newInstance(CWithConstructor.class, new TemplateModel[0], null) - .getClass()); + assertEquals(CWithConstructor.class, dow.newInstance(CWithConstructor.class, new TemplateModel[0], null) + .getClass()); assertEquals(CWithOverloadedConstructor.class, - ow.newInstance(CWithOverloadedConstructor.class, new TemplateModel[0], null) + dow.newInstance(CWithOverloadedConstructor.class, new TemplateModel[0], null) .getClass()); assertEquals(CWithOverloadedConstructor.class, - ow.newInstance(CWithOverloadedConstructor.class, new TemplateModel[] {new SimpleNumber(1)}, null) + dow.newInstance(CWithOverloadedConstructor.class, new TemplateModel[] { new SimpleNumber(1) }, null) .getClass()); } @@ -167,8 +170,10 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { public void testMethodsWithCustomMemberAccessPolicy() throws TemplateException { DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override public ClassMemberAccessPolicy forClass(Class<?> contextClass) { return new ClassMemberAccessPolicy() { + @Override public boolean isMethodExposed(Method method) { String name = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); @@ -177,10 +182,12 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { && (paramTypes.length == 0 || paramTypes[0].equals(boolean.class))); } + @Override public boolean isConstructorExposed(Constructor<?> constructor) { return true; } + @Override public boolean isFieldExposed(Field field) { return true; } @@ -209,16 +216,20 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); owb.setExposeFields(true); owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override public ClassMemberAccessPolicy forClass(Class<?> contextClass) { return new ClassMemberAccessPolicy() { + @Override public boolean isMethodExposed(Method method) { return true; } + @Override public boolean isConstructorExposed(Constructor<?> constructor) { return true; } + @Override public boolean isFieldExposed(Field field) { return field.getName().equals("publicField1") || field.getName().equals("nonPublicField1"); @@ -239,16 +250,20 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { public void testGenericGetWithCustomMemberAccessPolicy() throws TemplateException { DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override public ClassMemberAccessPolicy forClass(Class<?> contextClass) { return new ClassMemberAccessPolicy() { + @Override public boolean isMethodExposed(Method method) { return false; } + @Override public boolean isConstructorExposed(Constructor<?> constructor) { return true; } + @Override public boolean isFieldExposed(Field field) { return true; } @@ -265,17 +280,21 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { public void testConstructorsWithCustomMemberAccessPolicy() throws TemplateException { DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override public ClassMemberAccessPolicy forClass(Class<?> contextClass) { return new ClassMemberAccessPolicy() { + @Override public boolean isMethodExposed(Method method) { return true; } + @Override public boolean isConstructorExposed(Constructor<?> constructor) { return constructor.getDeclaringClass() == CWithOverloadedConstructor.class && constructor.getParameterTypes().length == 1; } + @Override public boolean isFieldExposed(Field field) { return true; } @@ -302,54 +321,55 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { } assertEquals(CWithOverloadedConstructor.class, - ow.newInstance(CWithOverloadedConstructor.class, - new TemplateModel[] {new SimpleNumber(1)}, null).getClass()); + ow.newInstance(CWithOverloadedConstructor.class, new TemplateModel[] { new SimpleNumber(1) }, null) + .getClass()); } @Test public void testMemberAccessPolicyAndApiBI() throws IOException, TemplateException { - DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0) - .memberAccessPolicy(new MemberAccessPolicy() { + DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); + owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override + public ClassMemberAccessPolicy forClass(Class<?> contextClass) { + return new ClassMemberAccessPolicy() { @Override - public ClassMemberAccessPolicy forClass(Class<?> contextClass) { - return new ClassMemberAccessPolicy() { - public boolean isMethodExposed(Method method) { - return method.getName().equals("size"); - } - - public boolean isConstructorExposed(Constructor<?> constructor) { - return true; - } - - public boolean isFieldExposed(Field field) { - return true; - } - }; + public boolean isMethodExposed(Method method) { + return method.getName().equals("size"); } - }) - .build(); - Map<String, Object> dataModel = ImmutableMap.<String, Object>of("m", ImmutableMap.of("k", "v")); + @Override + public boolean isConstructorExposed(Constructor<?> constructor) { + return true; + } - String templateSource = "size=${m?api.size()} get=${(m?api.get('k'))!'hidden'}"; + @Override + public boolean isFieldExposed(Field field) { + return true; + } + }; + } + }); + DefaultObjectWrapper ow = owb.build(); + Map<String, Object> dataModel = ImmutableMap.of("m", ImmutableMap.of("k", "v")); + + String templateSourceCode = "size=${m?api.size()} get=${(m?api.get('k'))!'hidden'}"; { Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0) .objectWrapper(ow) .apiBuiltinEnabled(true) .build(); - Template template = new Template(null, templateSource, cfg); + Template template = new Template(null, templateSourceCode, cfg); StringWriter out = new StringWriter(); template.process(dataModel, out); assertEquals("size=1 get=hidden", out.toString()); } - { Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0) .objectWrapper(new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build()) .apiBuiltinEnabled(true) .build(); - Template template = new Template(null, templateSource, cfg); + Template template = new Template(null, templateSourceCode, cfg); StringWriter out = new StringWriter(); template.process(dataModel, out); assertEquals("size=1 get=v", out.toString()); @@ -357,53 +377,62 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { } @Test - public void testMemberAccessPolicyAndNewBI() throws IOException, TemplateException { - DefaultObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0) - .memberAccessPolicy(new MemberAccessPolicy() { - public ClassMemberAccessPolicy forClass(Class<?> contextClass) { - return new ClassMemberAccessPolicy() { - public boolean isMethodExposed(Method method) { - return true; - } - - public boolean isConstructorExposed(Constructor<?> constructor) { - return constructor.getDeclaringClass().equals(CustomModel.class); - } - - public boolean isFieldExposed(Field field) { - return true; - } - }; - } - }) - .build(); - - String templateSource = "${'" + CustomModel.class.getName() + "'?new()} " + public void testMemberAccessPolicyAndNewBI() throws IOException, TemplateException, NoSuchMethodException { + DefaultObjectWrapper.Builder owb = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0); + owb.setMemberAccessPolicy(new MemberAccessPolicy() { + @Override + public ClassMemberAccessPolicy forClass(Class<?> contextClass) { + return new ClassMemberAccessPolicy() { + @Override + public boolean isMethodExposed(Method method) { + return true; + } + + @Override + public boolean isConstructorExposed(Constructor<?> constructor) { + return constructor.getDeclaringClass().equals(CustomModel.class); + } + + @Override + public boolean isFieldExposed(Field field) { + return true; + } + }; + } + }); + DefaultObjectWrapper ow = owb.build(); + + String templateSourceCode = "${'" + CustomModel.class.getName() + "'?new()} " + "<#attempt>${'" + OtherCustomModel.class.getName() + "'?new()}<#recover>failed</#attempt>"; { Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0) .objectWrapper(ow) .apiBuiltinEnabled(true) .build(); - Template template = new Template(null, templateSource, cfg); + Template template = new Template(null, templateSourceCode, cfg); + StringWriter out = new StringWriter(); template.process(null, out); assertEquals("1 failed", out.toString()); } - { - Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0).build(); - Template template = new Template(null, templateSource, cfg); + DefaultObjectWrapper dow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build(); + MemberAccessPolicy pol = dow.getMemberAccessPolicy(); + ClassMemberAccessPolicy cpol = pol.forClass(CustomModel.class); + assertTrue(cpol.isConstructorExposed(CustomModel.class.getConstructor())); + + Configuration cfg = new Configuration.Builder(Configuration.VERSION_3_0_0) + .objectWrapper(dow) + .apiBuiltinEnabled(true) + .build(); + Template template = new Template(null, templateSourceCode, cfg); + StringWriter out = new StringWriter(); template.process(null, out); assertEquals("1 2", out.toString()); } } - private static DefaultObjectWrapper createDefaultMemberAccessPolicyObjectWrapper() { - return new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build(); - } - private static Object getHashValue(ObjectWrapperAndUnwrapper ow, TemplateHashModel objM, String key) throws TemplateException { return ow.unwrap(objM.get(key)); @@ -413,7 +442,8 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { assertThat(objM, instanceOf(TemplateFunctionModel.class)); TemplateModel[] argModels = new TemplateModel[args.length]; for (int i = 0; i < args.length; i++) { - argModels[i] = ow.wrap(args[i]); + Object arg = args[i]; + argModels[i] = ow.wrap(arg); } Object returnValue = ((TemplateFunctionModel) objM).execute(argModels, null, null); return unwrap(ow, returnValue); @@ -423,7 +453,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 +501,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 +518,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() { } @@ -502,14 +534,18 @@ public class DefaultObjectWrapperMemberAccessPolicyTest { } public static class CustomModel implements TemplateNumberModel { + @Override public Number getAsNumber() { return 1; } } public static class OtherCustomModel implements TemplateNumberModel { + @Override public Number getAsNumber() { return 2; } } + } + diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicyTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/MemberSelectorListAccessPolicyTest.java similarity index 84% rename from freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicyTest.java rename to freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/MemberSelectorListAccessPolicyTest.java index 9ed20be..1e667b2 100644 --- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicyTest.java +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/MemberSelectorListAccessPolicyTest.java @@ -25,12 +25,9 @@ import static org.junit.Assert.*; import java.io.Serializable; import java.util.Arrays; -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateHashModel; import org.junit.Test; -public class WhitelistMemberAccessPolicyTest { +public class MemberSelectorListAccessPolicyTest { @Test public void testEmpty() throws NoSuchMethodException, NoSuchFieldException { @@ -91,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"))); @@ -262,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. "), @@ -306,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)"); @@ -360,32 +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.Builder(Configuration.VERSION_3_0_0) - .setting( - "objectWrapper", - "DefaultObjectWrapper(3.0.0, " + - "memberAccessPolicy=" - + WhitelistMemberAccessPolicyTest.class.getName() + ".CONFIG_TEST_MEMBER_ACCESS_POLICY" - + ")") - .build(); - 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) { } @@ -448,6 +504,7 @@ public class WhitelistMemberAccessPolicyTest { } public static class E1 implements I1Sub { + @Override public void m1() { } @@ -550,6 +607,7 @@ public class WhitelistMemberAccessPolicyTest { @TemplateAccessible public void m4() {} + @Override public void m5() {} public void m6() {} diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java index 230dfed..e3215cf 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java @@ -33,6 +33,8 @@ import java.util.TimeZone; import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine; +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.impl.MemberAccessPolicy; import org.apache.freemarker.core.pluggablebuiltin.TruncateBuiltinAlgorithm; import org.apache.freemarker.core.pluggablebuiltin.impl.DefaultTruncateBuiltinAlgorithm; import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory; @@ -508,6 +510,10 @@ public interface ProcessingConfiguration { * called to resolve the <code>"com.example.SomeClassName"</code> string to a class. The default value is {@link * TemplateClassResolver#UNRESTRICTED}. If you allow users to upload templates, it's important to use a * custom restrictive {@link TemplateClassResolver} or {@link TemplateClassResolver#ALLOW_NOTHING}. + * + * <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. */ TemplateClassResolver getNewBuiltinClassResolver(); diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BlacklistMemberAccessPolicy.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BlacklistMemberAccessPolicy.java new file mode 100644 index 0000000..5bb0d62 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BlacklistMemberAccessPolicy.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.freemarker.core.model.impl; + +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 DefaultObjectWrapper} 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. + */ +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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ConstructorMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ConstructorMatcher.java index 0300fd4..6d90470 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ConstructorMatcher.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ConstructorMatcher.java @@ -29,4 +29,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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy.java index 6899efe..4713f2a 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy.java @@ -19,88 +19,36 @@ package org.apache.freemarker.core.model.impl; +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.nio.charset.StandardCharsets; +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 org.apache.freemarker.core.Version; import org.apache.freemarker.core._CoreAPI; -import org.apache.freemarker.core.util._ClassUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.freemarker.core.model.impl.MemberSelectorListMemberAccessPolicy.MemberSelector; /** - * 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; it can't provide safety in practice, if you allow + * untrusted users to edit templates! Use {@link WhitelistMemberAccessPolicy} if you need stricter control. */ public final class DefaultMemberAccessPolicy implements MemberAccessPolicy { - private static final Logger LOG = LoggerFactory.getLogger(DefaultMemberAccessPolicy.class); - 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 = _ClassUtils.loadProperties(DefaultObjectWrapper.class, UNSAFE_METHODS_PROPERTIES); - Set<Method> set = new HashSet<>(props.size() * 4 / 3, 1f); - Map<String, Class<?>> primClasses = createPrimitiveClassesMap(); - for (Object key : props.keySet()) { - try { - set.add(parseMethodSpec((String) key, primClasses)); - } catch (ClassNotFoundException | NoSuchMethodException e) { - LOG.debug("Failed to get unsafe method", 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 = _ClassUtils.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] = _ClassUtils.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 DefaultMemberAccessPolicy() { - } private static final DefaultMemberAccessPolicy INSTANCE = new 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. */ @@ -111,24 +59,124 @@ 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(); - private static final BacklistClassMemberAccessPolicy CLASS_MEMBER_ACCESS_POLICY_INSTANCE - = new BacklistClassMemberAccessPolicy(); - private static class BacklistClassMemberAccessPolicy implements ClassMemberAccessPolicy { + whitelistRuleFinalClasses = new HashSet<>(); + whitelistRuleNonFinalClasses = new HashSet<>(); + Set<Class<?>> typesWithBlacklistUnlistedRule = new HashSet<>(); + List<MemberSelector> whitelistMemberSelectors = new ArrayList<>(); + 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); - public boolean isMethodExposed(Method method) { - return !UNSAFE_METHODS.contains(method); + // 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 isConstructorExposed(Constructor<?> constructor) { - return true; + private static List<String> loadMemberSelectorFileLines() throws IOException { + List<String> whitelist = new ArrayList<String>(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader( + DefaultMemberAccessPolicy.class.getResourceAsStream("DefaultMemberAccessPolicy-rules"), + StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + whitelist.add(line); + } } - public boolean isFieldExposed(Field field) { + return whitelist; + } + + @Override + public ClassMemberAccessPolicy forClass(Class<?> contextClass) { + if (isTypeWithWhitelistRule(contextClass)) { + return whitelistMemberAccessPolicy.forClass(contextClass); + } else { + return blacklistMemberAccessPolicy.forClass(contextClass); + } + } + + 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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FieldMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FieldMatcher.java index dda187f..a61d54c 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FieldMatcher.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/FieldMatcher.java @@ -29,4 +29,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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberAccessPolicy.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberAccessPolicy.java index c5fa8b6..c00dbde 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberAccessPolicy.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberAccessPolicy.java @@ -19,24 +19,29 @@ package org.apache.freemarker.core.model.impl; +import org.apache.freemarker.core.Environment; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.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 DefaultObjectWrapper} 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 DefaultObjectWrapper.Builder#setMemberAccessPolicy(MemberAccessPolicy)} (or if - * you use {@link DefaultObjectWrapper}, with - * {@link DefaultObjectWrapper.Builder#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 DefaultObjectWrapper.Builder#setMemberAccessPolicy(MemberAccessPolicy)} (or if you use + * {@link DefaultObjectWrapper}, with {@link DefaultObjectWrapper.Builder#setMemberAccessPolicy(MemberAccessPolicy)}). * * <p>As {@link DefaultObjectWrapper}, 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 DefaultObjectWrapper} intends to expose on the first place. (Also, while public members - * declared in non-public classes are discovered by {@link DefaultObjectWrapper}, 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 DefaultObjectWrapper}, 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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberMatcher.java index 76080a1..c1fbe7c 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberMatcher.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberMatcher.java @@ -43,6 +43,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. * @@ -83,7 +85,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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberSelectorListMemberAccessPolicy.java similarity index 76% copy from freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java copy to freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberSelectorListMemberAccessPolicy.java index 64ef7a9..48e48e4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MemberSelectorListMemberAccessPolicy.java @@ -19,6 +19,7 @@ package org.apache.freemarker.core.model.impl; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -27,59 +28,64 @@ import java.util.Collection; import java.util.List; import java.util.StringTokenizer; -import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.util._ClassUtils; import org.apache.freemarker.core.util._NullArgumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * 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. */ -public class WhitelistMemberAccessPolicy implements MemberAccessPolicy { - private static final Logger LOG = LoggerFactory.getLogger(WhitelistMemberAccessPolicy.class); +public abstract class MemberSelectorListMemberAccessPolicy implements MemberAccessPolicy { + private static final Logger LOG = LoggerFactory.getLogger(MemberSelectorListMemberAccessPolicy.class); + + 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()}. - * - * @since 2.3.30 */ public final static class MemberSelector { private final Class<?> upperBoundType; @@ -261,7 +267,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); @@ -309,19 +315,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<>(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(); @@ -345,29 +378,47 @@ public class WhitelistMemberAccessPolicy implements MemberAccessPolicy { } @Override - public ClassMemberAccessPolicy forClass(final Class<?> contextClass) { + public final ClassMemberAccessPolicy forClass(final Class<?> contextClass) { return new ClassMemberAccessPolicy() { @Override public boolean isMethodExposed(Method method) { - return methodMatcher.matches(contextClass, method) - || _MethodUtils.getInheritableAnnotation(contextClass, method, TemplateAccessible.class) != null; + return matchResultToIsExposedResult( + methodMatcher.matches(contextClass, method) + || matchAnnotation != null + && _MethodUtils.getInheritableAnnotation(contextClass, method, matchAnnotation) + != null); } @Override public boolean isConstructorExposed(Constructor<?> constructor) { - return constructorMatcher.matches(contextClass, constructor) - || _MethodUtils.getInheritableAnnotation(contextClass, constructor, TemplateAccessible.class) - != null; + return matchResultToIsExposedResult( + constructorMatcher.matches(contextClass, constructor) + || matchAnnotation != null + && _MethodUtils.getInheritableAnnotation(contextClass, constructor, matchAnnotation) + != null); } @Override public boolean isFieldExposed(Field field) { - return fieldMatcher.matches(contextClass, field) - || _MethodUtils.getInheritableAnnotation(contextClass, field, TemplateAccessible.class) != null; + return matchResultToIsExposedResult( + fieldMatcher.matches(contextClass, field) + || matchAnnotation != null + && _MethodUtils.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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MethodMatcher.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MethodMatcher.java index ce66f4c..88711d6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MethodMatcher.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/MethodMatcher.java @@ -33,4 +33,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/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java index 64ef7a9..25e4801 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/WhitelistMemberAccessPolicy.java @@ -19,390 +19,34 @@ package org.apache.freemarker.core.model.impl; -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 org.apache.freemarker.core.model.ObjectWrapper; -import org.apache.freemarker.core.util._ClassUtils; -import org.apache.freemarker.core.util._NullArgumentException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * 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 DefaultObjectWrapper} 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. */ -public class WhitelistMemberAccessPolicy implements MemberAccessPolicy { - private static final Logger LOG = LoggerFactory.getLogger(WhitelistMemberAccessPolicy.class); - - 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 = _ClassUtils.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 | SecurityException e) { - return new MemberSelector(upperBoundClass, e, cleanedStr); - } - } - argTypes[i] = _ClassUtils.getArrayClass(argClass, arrayDimensions); - } - try { - return memberName.equals(upperBoundClass.getSimpleName()) - ? new MemberSelector(upperBoundClass, upperBoundClass.getConstructor(argTypes)) - : new MemberSelector(upperBoundClass, upperBoundClass.getMethod(memberName, argTypes)); - } catch (NoSuchMethodException | SecurityException e) { - return new MemberSelector(upperBoundClass, e, cleanedStr); - } - } else { - try { - return new MemberSelector(upperBoundClass, upperBoundClass.getField(memberName)); - } catch (NoSuchFieldException | 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<>(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(); - } - } - } - - @Override - public ClassMemberAccessPolicy forClass(final Class<?> contextClass) { - return new ClassMemberAccessPolicy() { - @Override - public boolean isMethodExposed(Method method) { - return methodMatcher.matches(contextClass, method) - || _MethodUtils.getInheritableAnnotation(contextClass, method, TemplateAccessible.class) != null; - } - - @Override - public boolean isConstructorExposed(Constructor<?> constructor) { - return constructorMatcher.matches(contextClass, constructor) - || _MethodUtils.getInheritableAnnotation(contextClass, constructor, TemplateAccessible.class) - != null; - } - - @Override - public boolean isFieldExposed(Field field) { - return fieldMatcher.matches(contextClass, field) - || _MethodUtils.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); } -} +} \ No newline at end of file diff --git a/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy-rules b/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/DefaultMemberAccessPolicy-rules new file mode 100644 index 0000000..474b831 --- /dev/null +++ b/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/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. +# 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/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties b/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties deleted file mode 100644 index 05c1981..0000000 --- a/freemarker-core/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties +++ /dev/null @@ -1,98 +0,0 @@ -# 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.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.destroy() -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.stop(java.lang.Throwable) -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.runFinalizersOnExit(boolean) -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)
