Repository: incubator-freemarker Updated Branches: refs/heads/3 ef9687577 -> dcb0e0634
Moved OptInTemplateClassResolver to core.util Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/7308532a Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/7308532a Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/7308532a Branch: refs/heads/3 Commit: 7308532af749ce827c4610c873626d3d68047d36 Parents: ef96875 Author: ddekany <[email protected]> Authored: Sat Feb 25 07:46:32 2017 +0100 Committer: ddekany <[email protected]> Committed: Sat Feb 25 07:46:52 2017 +0100 ---------------------------------------------------------------------- .../core/OptInTemplateClassResolver.java | 156 ------------------- .../core/util/OptInTemplateClassResolver.java | 155 ++++++++++++++++++ .../core/OptInTemplateClassResolverTest.java | 1 + 3 files changed, 156 insertions(+), 156 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7308532a/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java b/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java deleted file mode 100644 index 50e79d9..0000000 --- a/src/main/java/org/apache/freemarker/core/OptInTemplateClassResolver.java +++ /dev/null @@ -1,156 +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. - */ - -package org.apache.freemarker.core; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import org.apache.freemarker.core.util._ClassUtil; -import org.apache.freemarker.core.util._StringUtil; - -/** - * A {@link TemplateClassResolver} that resolves only the classes whose name - * was specified in the constructor. - */ -public class OptInTemplateClassResolver implements TemplateClassResolver { - - private final Set/*<String>*/ allowedClasses; - private final List/*<String>*/ trustedTemplatePrefixes; - private final Set/*<String>*/ trustedTemplateNames; - - /** - * Creates a new instance. - * - * @param allowedClasses the {@link Set} of {@link String}-s that contains - * the full-qualified names of the allowed classes. - * Can be <code>null</code> (means not class is allowed). - * @param trustedTemplates the {@link List} of {@link String}-s that contains - * template names (i.e., template root directory relative paths) - * and prefix patterns (like <code>"include/*"</code>) of templates - * for which {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} will be - * used (which is not as safe as {@link OptInTemplateClassResolver}). - * The list items need not start with <code>"/"</code> (if they are, it - * will be removed). List items ending with <code>"*"</code> are treated - * as prefixes (i.e. <code>"foo*"</code> matches <code>"foobar"</code>, - * <code>"foo/bar/baaz"</code>, <code>"foowhatever/bar/baaz"</code>, - * etc.). The <code>"*"</code> has no special meaning anywhere else. - * The matched template name is the name (template root directory - * relative path) of the template that directly (lexically) contains the - * operation (like <code>?new</code>) that wants to get the class. Thus, - * if a trusted template includes a non-trusted template, the - * <code>allowedClasses</code> restriction will apply in the included - * template. - * This parameter can be <code>null</code> (means no trusted templates). - */ - public OptInTemplateClassResolver( - Set allowedClasses, List<String> trustedTemplates) { - this.allowedClasses = allowedClasses != null ? allowedClasses : Collections.EMPTY_SET; - if (trustedTemplates != null) { - trustedTemplateNames = new HashSet(); - trustedTemplatePrefixes = new ArrayList(); - - Iterator<String> it = trustedTemplates.iterator(); - while (it.hasNext()) { - String li = it.next(); - if (li.startsWith("/")) li = li.substring(1); - if (li.endsWith("*")) { - trustedTemplatePrefixes.add(li.substring(0, li.length() - 1)); - } else { - trustedTemplateNames.add(li); - } - } - } else { - trustedTemplateNames = Collections.EMPTY_SET; - trustedTemplatePrefixes = Collections.EMPTY_LIST; - } - } - - @Override - public Class resolve(String className, Environment env, Template template) - throws TemplateException { - String templateName = safeGetTemplateName(template); - - if (templateName != null - && (trustedTemplateNames.contains(templateName) - || hasMatchingPrefix(templateName))) { - return TemplateClassResolver.UNRESTRICTED_RESOLVER.resolve(className, env, template); - } else { - if (!allowedClasses.contains(className)) { - throw new _MiscTemplateException(env, - "Instantiating ", className, " is not allowed in the template for security reasons. (If you " - + "run into this problem when using ?new in a template, you may want to check the \"", - Configurable.NEW_BUILTIN_CLASS_RESOLVER_KEY, - "\" setting in the FreeMarker configuration.)"); - } else { - try { - return _ClassUtil.forName(className); - } catch (ClassNotFoundException e) { - throw new _MiscTemplateException(e, env); - } - } - } - } - - /** - * Extract the template name from the template object which will be matched - * against the trusted template names and pattern. - */ - protected String safeGetTemplateName(Template template) { - if (template == null) return null; - - String name = template.getName(); - if (name == null) return null; - - // Detect exploits, return null if one is suspected: - String decodedName = name; - if (decodedName.indexOf('%') != -1) { - decodedName = _StringUtil.replace(decodedName, "%2e", ".", false, false); - decodedName = _StringUtil.replace(decodedName, "%2E", ".", false, false); - decodedName = _StringUtil.replace(decodedName, "%2f", "/", false, false); - decodedName = _StringUtil.replace(decodedName, "%2F", "/", false, false); - decodedName = _StringUtil.replace(decodedName, "%5c", "\\", false, false); - decodedName = _StringUtil.replace(decodedName, "%5C", "\\", false, false); - } - int dotDotIdx = decodedName.indexOf(".."); - if (dotDotIdx != -1) { - int before = dotDotIdx - 1 >= 0 ? decodedName.charAt(dotDotIdx - 1) : -1; - int after = dotDotIdx + 2 < decodedName.length() ? decodedName.charAt(dotDotIdx + 2) : -1; - if ((before == -1 || before == '/' || before == '\\') - && (after == -1 || after == '/' || after == '\\')) { - return null; - } - } - - return name.startsWith("/") ? name.substring(1) : name; - } - - private boolean hasMatchingPrefix(String name) { - for (int i = 0; i < trustedTemplatePrefixes.size(); i++) { - String prefix = (String) trustedTemplatePrefixes.get(i); - if (name.startsWith(prefix)) return true; - } - return false; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7308532a/src/main/java/org/apache/freemarker/core/util/OptInTemplateClassResolver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/util/OptInTemplateClassResolver.java b/src/main/java/org/apache/freemarker/core/util/OptInTemplateClassResolver.java new file mode 100644 index 0000000..5cd4427 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/util/OptInTemplateClassResolver.java @@ -0,0 +1,155 @@ +/* + * 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.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.freemarker.core.*; + +/** + * A {@link TemplateClassResolver} that resolves only the classes whose name + * was specified in the constructor. + */ +public class OptInTemplateClassResolver implements TemplateClassResolver { + + private final Set/*<String>*/ allowedClasses; + private final List/*<String>*/ trustedTemplatePrefixes; + private final Set/*<String>*/ trustedTemplateNames; + + /** + * Creates a new instance. + * + * @param allowedClasses the {@link Set} of {@link String}-s that contains + * the full-qualified names of the allowed classes. + * Can be <code>null</code> (means not class is allowed). + * @param trustedTemplates the {@link List} of {@link String}-s that contains + * template names (i.e., template root directory relative paths) + * and prefix patterns (like <code>"include/*"</code>) of templates + * for which {@link TemplateClassResolver#UNRESTRICTED_RESOLVER} will be + * used (which is not as safe as {@link OptInTemplateClassResolver}). + * The list items need not start with <code>"/"</code> (if they are, it + * will be removed). List items ending with <code>"*"</code> are treated + * as prefixes (i.e. <code>"foo*"</code> matches <code>"foobar"</code>, + * <code>"foo/bar/baaz"</code>, <code>"foowhatever/bar/baaz"</code>, + * etc.). The <code>"*"</code> has no special meaning anywhere else. + * The matched template name is the name (template root directory + * relative path) of the template that directly (lexically) contains the + * operation (like <code>?new</code>) that wants to get the class. Thus, + * if a trusted template includes a non-trusted template, the + * <code>allowedClasses</code> restriction will apply in the included + * template. + * This parameter can be <code>null</code> (means no trusted templates). + */ + public OptInTemplateClassResolver( + Set allowedClasses, List<String> trustedTemplates) { + this.allowedClasses = allowedClasses != null ? allowedClasses : Collections.EMPTY_SET; + if (trustedTemplates != null) { + trustedTemplateNames = new HashSet(); + trustedTemplatePrefixes = new ArrayList(); + + Iterator<String> it = trustedTemplates.iterator(); + while (it.hasNext()) { + String li = it.next(); + if (li.startsWith("/")) li = li.substring(1); + if (li.endsWith("*")) { + trustedTemplatePrefixes.add(li.substring(0, li.length() - 1)); + } else { + trustedTemplateNames.add(li); + } + } + } else { + trustedTemplateNames = Collections.EMPTY_SET; + trustedTemplatePrefixes = Collections.EMPTY_LIST; + } + } + + @Override + public Class resolve(String className, Environment env, Template template) + throws TemplateException { + String templateName = safeGetTemplateName(template); + + if (templateName != null + && (trustedTemplateNames.contains(templateName) + || hasMatchingPrefix(templateName))) { + return TemplateClassResolver.UNRESTRICTED_RESOLVER.resolve(className, env, template); + } else { + if (!allowedClasses.contains(className)) { + throw new _MiscTemplateException(env, + "Instantiating ", className, " is not allowed in the template for security reasons. (If you " + + "run into this problem when using ?new in a template, you may want to check the \"", + Configurable.NEW_BUILTIN_CLASS_RESOLVER_KEY, + "\" setting in the FreeMarker configuration.)"); + } else { + try { + return _ClassUtil.forName(className); + } catch (ClassNotFoundException e) { + throw new _MiscTemplateException(e, env); + } + } + } + } + + /** + * Extract the template name from the template object which will be matched + * against the trusted template names and pattern. + */ + protected String safeGetTemplateName(Template template) { + if (template == null) return null; + + String name = template.getName(); + if (name == null) return null; + + // Detect exploits, return null if one is suspected: + String decodedName = name; + if (decodedName.indexOf('%') != -1) { + decodedName = _StringUtil.replace(decodedName, "%2e", ".", false, false); + decodedName = _StringUtil.replace(decodedName, "%2E", ".", false, false); + decodedName = _StringUtil.replace(decodedName, "%2f", "/", false, false); + decodedName = _StringUtil.replace(decodedName, "%2F", "/", false, false); + decodedName = _StringUtil.replace(decodedName, "%5c", "\\", false, false); + decodedName = _StringUtil.replace(decodedName, "%5C", "\\", false, false); + } + int dotDotIdx = decodedName.indexOf(".."); + if (dotDotIdx != -1) { + int before = dotDotIdx - 1 >= 0 ? decodedName.charAt(dotDotIdx - 1) : -1; + int after = dotDotIdx + 2 < decodedName.length() ? decodedName.charAt(dotDotIdx + 2) : -1; + if ((before == -1 || before == '/' || before == '\\') + && (after == -1 || after == '/' || after == '\\')) { + return null; + } + } + + return name.startsWith("/") ? name.substring(1) : name; + } + + private boolean hasMatchingPrefix(String name) { + for (int i = 0; i < trustedTemplatePrefixes.size(); i++) { + String prefix = (String) trustedTemplatePrefixes.get(i); + if (name.startsWith(prefix)) return true; + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7308532a/src/test/java/org/apache/freemarker/core/OptInTemplateClassResolverTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/OptInTemplateClassResolverTest.java b/src/test/java/org/apache/freemarker/core/OptInTemplateClassResolverTest.java index 6e4c3cc..669facb 100644 --- a/src/test/java/org/apache/freemarker/core/OptInTemplateClassResolverTest.java +++ b/src/test/java/org/apache/freemarker/core/OptInTemplateClassResolverTest.java @@ -26,6 +26,7 @@ import java.util.Set; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.apache.freemarker.core.util.OptInTemplateClassResolver; public class OptInTemplateClassResolverTest extends TestCase {
