/* * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package freemarker.template; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.io.IOException; import java.io.StringWriter; import java.util.Map; import java.util.Set; import org.junit.Test; import freemarker.core.Environment; import freemarker.core.Macro; import freemarker.template.utility.NullWriter; /** * These are things that users shouldn't do, but we shouldn't break backward compatibility without knowing about it. */ public class MistakenlyPublicMacroAPIsTest { private final Configuration cfg = new Configuration(Configuration.VERSION_2_3_0); /** * Getting the macros from one template, and adding them to another. */ @Test public void testMacroCopyingExploit() throws IOException, TemplateException { Template tMacros = new Template(null, "<#macro m1>1</#macro><#macro m2>2</#macro>", cfg); Map<String, Macro> macros = tMacros.getMacros(); Template t = new Template(null, "<@m1/><@m2/><@m3/>" + "<#macro m1>1b</#macro><#macro m3>3b</#macro> " + "<@m1/><@m2/><@m3/>", cfg); t.addMacro(macros.get("m1")); t.addMacro(macros.get("m2")); assertEquals("123b 1b23b", getTemplateOutput(t)); } /** * Same as {@link #testMacroCopyingExploit()}, but to make it worse, it adds the macros directly through the macro * {@link Map}. */ @Test public void testMacroCopyingExploitWithMapModification() throws IOException, TemplateException { Template tMacros = new Template(null, "<#macro m1>1</#macro><#macro m2>2</#macro>", cfg); Map<String, Macro> macros = tMacros.getMacros(); Template t = new Template(null, "<@m1/><@m2/><@m3/>" + "<#macro m1>1b</#macro><#macro m3>3b</#macro> " + "<@m1/><@m2/><@m3/>", cfg); t.getMacros().put("m1", macros.get("m1")); t.getMacros().put("whatever", macros.get("m2")); // Legacy bug: Map key doesn't mater, only original macro name assertEquals("123b 1b23b", getTemplateOutput(t)); } @Test public void testMacroCopyingExploitAndNamespaces() throws IOException, TemplateException { Template tMacros = new Template(null, "<#assign x = 0><#macro m1>${x}</#macro>", cfg); Template t = new Template(null, "<#assign x = 1><@m1/>", cfg); t.addMacro((Macro) tMacros.getMacros().get("m1")); assertEquals("1", getTemplateOutput(t)); } @Test public void testMacroCopyingFromFTLVariable() throws IOException, TemplateException { Template tMacros = new Template(null, "<#assign x = 0><#macro m1>${x}</#macro>", cfg); Environment env = tMacros.createProcessingEnvironment(null, NullWriter.INSTANCE); env.process(); TemplateModel m1 = env.getVariable("m1"); assertThat(m1, instanceOf(Macro.class)); Template t = new Template(null, "<#assign x = 1><@m1/>", cfg); t.addMacro((Macro) m1); assertEquals("1", getTemplateOutput(t)); } /** * Same as {@link #testMacroCopyingFromFTLVariable()}, but to make it worse, it adds the macros directly through the * {@link Map}. */ @Test public void testMacroCopyingFromFTLVariableWithMapModification() throws IOException, TemplateException { Template tMacros = new Template(null, "<#assign x = 0><#macro m1>${x}</#macro>", cfg); Environment env = tMacros.createProcessingEnvironment(null, NullWriter.INSTANCE); env.process(); TemplateModel m1 = env.getVariable("m1"); assertThat(m1, instanceOf(Macro.class)); for (int variation : new int[] { 1, 2 }) { Template t = new Template(null, "<#assign x = 1><@m1/>", cfg); if (variation == 1) { t.getMacros().put("m1", m1); } else { t.getMacros().put("m1", null); // Just so it appears in the Entry Set. for (Map.Entry<String, Macro> ent : (Set<Map.Entry>) t.getMacros().entrySet()) { if (ent.getKey().equals("m1")) { ent.setValue((Macro) m1); } } } assertEquals("1", getTemplateOutput(t)); } } private String getTemplateOutput(Template t) throws TemplateException, IOException { StringWriter sw = new StringWriter(); t.process(null, sw); return sw.toString(); } }