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
The following commit(s) were added to refs/heads/3 by this push:
new f3c7dfbc Forward ported from 2.3-gae: PR #89 TemplateProcessingTracer
f3c7dfbc is described below
commit f3c7dfbc420ac59b184a819a1a8d29ef46868e5f
Author: ddekany <[email protected]>
AuthorDate: Tue Dec 19 21:01:55 2023 +0100
Forward ported from 2.3-gae: PR #89 TemplateProcessingTracer
---
.../apache/freemarker/core/OutputFormatTest.java | 46 ++++++++++++++++++++++
.../apache/freemarker/core/AutoEscapingPolicy.java | 4 +-
.../core/BuiltInBannedWhenForcedAutoEscaping.java | 27 +++++++++++++
.../core/BuiltInsForOutputFormatRelated.java | 2 +-
.../MutableParsingAndProcessingConfiguration.java | 12 +++---
.../core/MutableProcessingConfiguration.java | 4 +-
.../main/javacc/org/apache/freemarker/core/FTL.jj | 39 +++++++++++++++++-
.../org/apache/freemarker/test/TemplateTest.java | 37 +++++++----------
8 files changed, 139 insertions(+), 32 deletions(-)
diff --git
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
index 12338661..02dd6bd0 100644
---
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
+++
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/OutputFormatTest.java
@@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
+import java.util.function.Consumer;
import static org.apache.freemarker.core.AutoEscapingPolicy.*;
import static org.junit.Assert.assertEquals;
@@ -816,6 +817,51 @@ public class OutputFormatTest extends TemplateTest {
.build();
}
+ @Test
+ public void testForcedAutoEsc() throws Exception {
+ String commonFTL = "${'.'} ${.autoEsc?c}";
+ String esced = "\\. true";
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(SeldomEscapedOutputFormat.INSTANCE));
+ assertOutput(commonFTL, esced);
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(DummyOutputFormat.INSTANCE));
+ assertOutput(commonFTL, esced);
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(PlainTextOutputFormat.INSTANCE));
+ assertOutput("<#ftl outputFormat='seldomEscaped'>" + commonFTL, esced);
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(HTMLOutputFormat.INSTANCE));
+ assertOutput("<#outputFormat 'seldomEscaped'>" + commonFTL +
"</#outputFormat>", esced);
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(PlainTextOutputFormat.INSTANCE));
+ assertErrorContains("", IllegalArgumentException.class,
+ "plainText", "autoEscapingPolicy", "force");
+
+ setConfigurationForTestForceAutoEsc(cfgBuilder ->
cfgBuilder.setOutputFormat(DummyOutputFormat.INSTANCE));
+ assertErrorContains("<#ftl autoEsc=false>", ParseException.class,
+ "autoEsc=false", "autoEscapingPolicy", "force");
+ assertErrorContains("<#outputFormat 'plainText'></#outputformat>",
ParseException.class,
+ "plainText", "autoEscapingPolicy", "force");
+ assertErrorContains("<#noAutoEsc></#noAutoEsc>", ParseException.class,
+ "noAutoEsc", "autoEscapingPolicy", "force");
+ assertErrorContains("<#noAutoEsc></#noautoesc>", ParseException.class,
+ "noAutoEsc", "autoEscapingPolicy", "force");
+ assertErrorContains("<#assign foo='bar'>${foo?noEsc}",
ParseException.class,
+ "?noEsc", "autoEscapingPolicy", "force");
+ assertErrorContains("<#assign foo='bar'>${foo?noEsc}",
ParseException.class,
+ "?noEsc", "autoEscapingPolicy", "force");
+ }
+
+ private void
setConfigurationForTestForceAutoEsc(Consumer<Configuration.ExtendableBuilder<?>>
cfgBuilderAdjuster) {
+ setConfiguration(cfgBuilder -> {
+ cfgBuilder.setRegisteredCustomOutputFormats(ImmutableList.of(
+ SeldomEscapedOutputFormat.INSTANCE,
DummyOutputFormat.INSTANCE));
+ cfgBuilder.setAutoEscapingPolicy(AutoEscapingPolicy.FORCE);
+ cfgBuilderAdjuster.accept(cfgBuilder);
+ });
+ }
+
@Test
public void testDynamicParsingBIsInherticContextOutputFormat() throws
Exception {
// Dynamic parser BI-s are supposed to use the ParsingConfiguration of
the calling template, and ignore anything
diff --git
a/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
b/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
index c221559a..4958308a 100644
---
a/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
+++
b/freemarker-core/src/main/java/org/apache/freemarker/core/AutoEscapingPolicy.java
@@ -40,5 +40,7 @@ public enum AutoEscapingPolicy {
ENABLE_IF_DEFAULT,
/** Enable auto-escaping if the {@link OutputFormat} supports it. */
- ENABLE_IF_SUPPORTED
+ ENABLE_IF_SUPPORTED,
+
+ FORCE
}
diff --git
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
new file mode 100644
index 00000000..d87c0a3e
--- /dev/null
+++
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInBannedWhenForcedAutoEscaping.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * A built-in whose usage is banned when auto-escaping is in the "forced"
state.
+ * This is just a marker; the actual checking is in {@code FTL.jj}.
+ */
+interface BuiltInBannedWhenForcedAutoEscaping {
+}
diff --git
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
index 5fa65582..8f44653c 100644
---
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
+++
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForOutputFormatRelated.java
@@ -24,7 +24,7 @@ import
org.apache.freemarker.core.outputformat.MarkupOutputFormat;
class BuiltInsForOutputFormatRelated {
- static class no_escBI extends AbstractConverterBI {
+ static class no_escBI extends AbstractConverterBI implements
BuiltInBannedWhenForcedAutoEscaping {
@Override
protected TemplateModel calculateResult(String lho, MarkupOutputFormat
outputFormat, Environment env)
diff --git
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
index 1c05c5da..284abb06 100644
---
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
+++
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
@@ -19,10 +19,6 @@
package org.apache.freemarker.core;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.Set;
-
import org.apache.freemarker.core.outputformat.OutputFormat;
import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -31,6 +27,10 @@ import org.apache.freemarker.core.util._SortedArraySet;
import org.apache.freemarker.core.util._StringUtils;
import org.apache.freemarker.core.util._UnmodifiableCompositeSet;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Set;
+
public abstract class MutableParsingAndProcessingConfiguration<
SelfT extends MutableParsingAndProcessingConfiguration<SelfT>>
extends MutableProcessingConfiguration<SelfT>
@@ -98,6 +98,8 @@ public abstract class
MutableParsingAndProcessingConfiguration<
setAutoEscapingPolicy(AutoEscapingPolicy.ENABLE_IF_DEFAULT);
} else if ("enableIfSupported".equals(value)) {
setAutoEscapingPolicy(AutoEscapingPolicy.ENABLE_IF_SUPPORTED);
+ } else if ("force".equals(value)) {
+ setAutoEscapingPolicy(AutoEscapingPolicy.FORCE);
} else if ("disable".equals(value)) {
setAutoEscapingPolicy(AutoEscapingPolicy.DISABLE);
} else {
@@ -237,7 +239,7 @@ public abstract class
MutableParsingAndProcessingConfiguration<
}
/**
- * * Setter pair of {@link #getAutoEscapingPolicy()}.
+ * Setter pair of {@link #getAutoEscapingPolicy()}.
*/
public void setAutoEscapingPolicy(AutoEscapingPolicy autoEscapingPolicy) {
_NullArgumentException.check("autoEscapingPolicy", autoEscapingPolicy);
diff --git
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
index 740f17ff..580d34eb 100644
---
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
+++
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -1532,7 +1532,9 @@ public abstract class
MutableProcessingConfiguration<SelfT extends MutableProces
* <br>String value: {@code "enableIfDefault"} or {@code
"enableIfDefault"} for
* {@link AutoEscapingPolicy#ENABLE_IF_DEFAULT},
* {@code "enableIfDefault"} or {@code "enableIfSupported"} for
- * {@link AutoEscapingPolicy#ENABLE_IF_SUPPORTED}
+ * {@link AutoEscapingPolicy#ENABLE_IF_SUPPORTED},
+ * {@code "enableIfDefault"} or {@code "force"} for
+ * {@link AutoEscapingPolicy#FORCE}
* {@code "disable"} for {@link AutoEscapingPolicy#DISABLE}.
*
* <li><p>{@code "sourceEncoding"}:
diff --git a/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
b/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
index 69bd6559..404ca5c1 100644
--- a/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
+++ b/freemarker-core/src/main/javacc/org/apache/freemarker/core/FTL.jj
@@ -191,7 +191,8 @@ public class FMParser {
if (outputFormat instanceof MarkupOutputFormat) {
if (autoEscapingPolicy == AutoEscapingPolicy.ENABLE_IF_DEFAULT) {
autoEscaping = ((MarkupOutputFormat)
outputFormat).isAutoEscapedByDefault();
- } else if (autoEscapingPolicy ==
AutoEscapingPolicy.ENABLE_IF_SUPPORTED) {
+ } else if (autoEscapingPolicy ==
AutoEscapingPolicy.ENABLE_IF_SUPPORTED
+ || autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
autoEscaping = true;
} else if (autoEscapingPolicy == AutoEscapingPolicy.DISABLE) {
autoEscaping = false;
@@ -354,7 +355,16 @@ public class FMParser {
template, start);
}
}
-
+
+ private static String
forcedAutoEscapingPolicyExceptionMessage(OutputFormat outputFormat) {
+ return forcedAutoEscapingPolicyExceptionMessage("Non-markup output
format " + outputFormat);
+ }
+
+ private static String forcedAutoEscapingPolicyExceptionMessage(String
whatCanNotBeUsed) {
+ return whatCanNotBeUsed + " can't be used when the \"" +
MutableParsingAndProcessingConfiguration.AUTO_ESCAPING_POLICY_KEY + "\" "
+ + "configuration setting was set to \"force\"
(AutoEscapingPolicy.FORCE).";
+ }
+
private ParserIteratorBlockContext pushIteratorBlockContext() {
if (iteratorBlockContexts == null) {
iteratorBlockContexts = new ArrayList(4);
@@ -1953,6 +1963,14 @@ ASTExpression BuiltIn(ASTExpression lhoExp) :
return result;
}
+ if (result instanceof BuiltInBannedWhenForcedAutoEscaping) {
+ if (autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+ throw new ParseException(
+ forcedAutoEscapingPolicyExceptionMessage("The ?" +
t.image + " expression"),
+ template, t);
+ }
+ }
+
if (result instanceof MarkupOutputFormatBoundBuiltIn) {
if (!(outputFormat instanceof MarkupOutputFormat)) {
throw new ParseException(
@@ -3742,6 +3760,9 @@ ASTDirOutputFormat OutputFormat() :
} else {
outputFormat =
template.getConfiguration().getOutputFormat(paramStr);
}
+ if (!(outputFormat instanceof MarkupOutputFormat) &&
autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+ throw new
ParseException(forcedAutoEscapingPolicyExceptionMessage(outputFormat),
template, start);
+ }
recalculateAutoEscapingField();
} catch (IllegalArgumentException e) {
throw new ParseException("Invalid format name: " + e.getMessage(),
template, start, e.getCause());
@@ -3796,6 +3817,11 @@ ASTDirNoAutoEsc NoAutoEsc() :
{
start = <NOAUTOESC>
{
+ if (autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+ throw new ParseException(
+ forcedAutoEscapingPolicyExceptionMessage("<#noAutoEsc>"),
+ template, start);
+ }
lastAutoEscapingPolicy = autoEscapingPolicy;
autoEscapingPolicy = AutoEscapingPolicy.DISABLE;
recalculateAutoEscapingField();
@@ -4146,6 +4172,11 @@ void HeaderElement() :
autoEscRequester = key;
autoEscapingPolicy =
AutoEscapingPolicy.ENABLE_IF_SUPPORTED;
} else {
+ if (autoEscapingPolicy ==
AutoEscapingPolicy.FORCE) {
+ throw new ParseException(
+
forcedAutoEscapingPolicyExceptionMessage(ks + "=false"),
+ exp);
+ }
autoEscapingPolicy =
AutoEscapingPolicy.DISABLE;
}
recalculateAutoEscapingField();
@@ -4364,6 +4395,10 @@ ASTElement Root() :
HeaderElement()
]
{
+ if (!(outputFormat instanceof MarkupOutputFormat) &&
autoEscapingPolicy == AutoEscapingPolicy.FORCE) {
+ throw new
IllegalArgumentException(forcedAutoEscapingPolicyExceptionMessage(outputFormat));
+ }
+
// There will be no WrongEncodingException exception, release mark
buffer:
if (streamToUnmarkWhenEncEstabd != null) {
streamToUnmarkWhenEncEstabd.mark(0);
diff --git
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
index 747fa606..7c1f9525 100644
---
a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
+++
b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTest.java
@@ -19,29 +19,10 @@
package org.apache.freemarker.test;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.FileNotFoundException;
-import java.io.FilterWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Properties;
-
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.io.IOUtils;
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.ParseException;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateConfiguration;
+import org.apache.freemarker.core.*;
import org.apache.freemarker.core.TemplateConfiguration.Builder;
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.Version;
import org.apache.freemarker.core.templateresolver.TemplateLoader;
import
org.apache.freemarker.core.templateresolver.impl.ByteArrayTemplateLoader;
import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader;
@@ -49,7 +30,13 @@ import
org.apache.freemarker.core.util._NullArgumentException;
import org.apache.freemarker.core.util._StringUtils;
import org.junit.Ignore;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.function.Consumer;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.*;
/**
* Superclass of JUnit tests that process templates.
@@ -104,6 +91,12 @@ public abstract class TemplateTest {
setConfiguration(configurationBuilder.build());
}
+ protected final void
setConfiguration(Consumer<Configuration.ExtendableBuilder<?>>
cfgBuilderAdjuster) {
+ Configuration.ExtendableBuilder<?> configBuilder =
newConfigurationBuilder();
+ cfgBuilderAdjuster.accept(configBuilder);
+ setConfiguration(configBuilder.build());
+ }
+
protected void assertOutput(String ftl, String expectedOut) throws
IOException, TemplateException {
assertOutput(createTemplate(ftl), expectedOut, false);
}